A SystemVerilog mailbox
is a way to allow different processes to exchange data between each other. It is similar to a real postbox where letters can be put into the box and a person can retrieve those letters later on.
SystemVerilog mailboxes are created as having either a bounded or unbounded queue size. A bounded mailbox
can only store a limited amount of data, and if a process attempts to store more messages into a full mailbox, it will be suspended until there's enough room in the mailbox. However, an unbounded mailbox
has unlimited size.
There are two types:
- Generic Mailbox that can accept items of any data type
- Parameterized Mailbox that can accept items of only a specific data type
SystemVerilog Mailbox vs Queue
Although a SystemVerilog mailbox essentially behaves like a queue, it is quite different from the queue
data type. A simple queue can only push and pop items from either the front or the back. However, a mailbox is a built-in class that uses semaphores to have atomic control the push and pop from the queue. Moreover, you cannot access a given index within the mailbox queue, but only retrieve items in FIFO order.
Where is a mailbox used ?
A SystemVerilog mailbox
is typically used when there are multiple threads running in parallel and want to share data for which a certain level of determinism is required.
Generic Mailbox Example
Two processes are concurrently active in the example shown below, where one initial
block puts data into the mailbox and another initial
block gets data from the mailbox.
module tb;
// Create a new mailbox that can hold utmost 2 items
mailbox mbx = new(2);
// Block1: This block keeps putting items into the mailbox
// The rate of items being put into the mailbox is 1 every ns
initial begin
for (int i=0; i < 5; i++) begin
#1 mbx.put (i);
$display ("[%0t] Thread0: Put item #%0d, size=%0d", $time, i, mbx.num());
end
end
// Block2: This block keeps getting items from the mailbox
// The rate of items received from the mailbox is 2 every ns
initial begin
forever begin
int idx;
#2 mbx.get (idx);
$display ("[%0t] Thread1: Got item #%0d, size=%0d", $time, idx, mbx.num());
end
end
endmodule
Semaphore is just like a bucket with a fixed number of keys. Processes that use a semaphore must first get a key from the bucket before they can continue to execute. Other proceses must wait until keys are available in the bucket for them to use. In a sense, they are best used for mutual exclusion, access control to shared resources and basic synchronization.
Syntax
semaphore [identifier_name];
Note that semaphore
is a built-in class and hence it should be used just like any other class object. It has a few methods with which we can allocate the number of keys for that semaphore
object, get and put keys into the bucket.
Methods
Sometimes we come across scenarios where we want the solver to randomly pick one out of the many statements. The keyword randcase
introduces a case
statement that randomly selects one of its branches. The case item expressions are positive integer values that represent the weights associated with each item. Probability of selecting an item is derived by the division of that item's weight divided by the sum of all weights.
Syntax
randcase
item : statement;
...
endcase
Example
The sum of all weights is 9, and hence the probability of taking the first branch is 1/9 or 11.11%, the probability of taking the second branch is 5/9 or 55.56% and the probability of taking the last branch is 3/9 or 33.33%.
Verilog has significant limitations regarding global declarations, especially functions and user-defined types.
- Local Scope: In Verilog, all objects declared within a module are local to that module. This means that any function or task must be re-declared in each module where it is needed, leading to redundant code and increased maintenance efforts.
- Hierarchical References: While Verilog allows hierarchical references to access module objects from other modules, these references are only for verification purposes and do not represent actual hardware behavior. Consequently, they are not synthesizable.
- User-Defined Types: There is often a need to use user-defined types in multiple modules. However, without a mechanism for global declarations, these types must also be redefined in each module, further contributing to code duplication.
SystemVerilog Package
A SystemVerilog package offers a way to store and share data, methods, properties, and parameters that can be reused across multiple modules, interfaces, or programs. Packages have explicitly defined scopes that exist at the same level as top-level modules, allowing all parameters and enumerations to be referenced within this scope.
Randomization of variables in a class can be disabled using rand_mode
method call.
This is very similar to the constraint_mode()
method used to Disable Constraints. So a disabled random variable is treated the same as if they had not been declared rand
or randc
.
rand_mode
can be called both as a function and task. Current state of the variable will be returned if it is called as a function.
// Disables randomization of variable [variable_name] inside [class_object] class
[class_object].[variable_name].rand_mode (0);
// Enables randomization of variable [variable_name] inside [class_object] class
[class_object].[variable_name].rand_mode (1);