A mailbox in SystemVerilog is a type of object used for inter-process communication. It allows one process to place data into the mailbox and another process to retrieve it. This makes mailboxes ideal for coordinating tasks between parallel processes, especially in testbenches where one component might generate data while another component consumes it.
Some key points about mailboxes in SystemVerilog:
- Mailboxes are designed to be thread-safe, ensuring no data races occur during read and write operations.
- They can be bounded (have a fixed maximum capacity) or unbounded (no limit on stored items).
- Mailboxes operate on a FIFO basis, meaning the first data written in is the first data read out.
Declaring and Creating a Mailbox
In SystemVerilog, a mailbox is declared as a mailbox type and created using the new constructor. You can specify an integer argument in the constructor to limit the capacity of the mailbox, making it bounded. If you leave it blank or set it to zero, the mailbox is unbounded.
Example:
mailbox my_mailbox; // Unbounded mailbox
mailbox my_bounded_mailbox = new(4); // Bounded mailbox with a capacity of 4
In the example above:
- my_mailbox is an unbounded mailbox, meaning it can store an unlimited number of items.
- my_bounded_mailbox has a maximum capacity of 4 items, making it useful if you need to control memory usage.
Methods of a Mailbox in SystemVerilog
SystemVerilog provides several methods to interact with mailboxes, allowing processes to send and receive messages in various ways. Here are the most commonly used mailbox methods:
put()
The put() method places an item in the mailbox. If the mailbox is bounded and already full, put() will block the calling process until space becomes available.
Syntax:
my_mailbox.put(data);
- Example: my_mailbox.put(5); — Places the integer
5in my_mailbox. - Blocking Behavior: For a bounded mailbox,
put()will wait until there’s space.
get()
The get() method retrieves and removes an item from the mailbox. If the mailbox is empty, get() blocks the process until an item is available.
Syntax:
my_mailbox.get(data);
- Example:
int data;
my_mailbox.get(data);
This statement removes the first item from my_mailbox and stores it in the variable data.
try_put()
The try_put() method is a non-blocking version of put(). It attempts to place an item in the mailbox and returns a boolean indicating success or failure.
Syntax:
success = my_mailbox.try_put(data);
Example:
if (!my_bounded_mailbox.try_put(10)) begin
$display("Mailbox is full, could not insert data");
end
This code attempts to insert 10 into my_bounded_mailbox without blocking. If it’s full, the insertion fails.
try_get()
The try_get() method is a non-blocking version of get(). It attempts to retrieve an item from the mailbox and returns a boolean indicating success or failure.
Syntax:
success = my_mailbox.try_get(data);
Example:
int data;
if (my_mailbox.try_get(data)) begin
$display("Data received: %0d", data);
end else begin
$display("Mailbox is empty");
end
This code tries to retrieve an item without blocking. If my_mailbox is empty, it simply returns false.
peek()
The peek() method retrieves an item from the mailbox without removing it. If the mailbox is empty, peek() will block until an item becomes available.
Syntax:
my_mailbox.peek(data);
Example:
int data;
my_mailbox.peek(data);
$display("Peeked data: %0d", data);
try_peek()
The try_peek() method is a non-blocking version of peek(). It attempts to look at the first item without removing it and returns a boolean indicating success or failure.
Syntax:
success = my_mailbox.try_peek(data);
Example:
int data;
if (my_mailbox.try_peek(data)) begin
$display("Peeked data: %0d", data);
end else begin
$display("Mailbox is empty");
end
This code tries to peek at the first item without removing it and will not block if the mailbox is empty.
Practical Example: Using a Mailbox in a Testbench
Mailboxes are widely used in test benches for communication between processes. Here’s a simple example demonstrating how one process (a producer) can generate data and send it to another process (a consumer) using a mailbox.
module mailbox_example;
mailbox my_mailbox = new(); // Create an unbounded mailbox
// Producer Task - Generates data and sends it to the mailbox
task producer();
int data;
for (data = 0; data < 5; data++) begin
$display("Producer: Sending data %0d", data);
my_mailbox.put(data);
#5; // Wait 5 time units
end
endtask
// Consumer Task - Receives data from the mailbox
task consumer();
int data;
for (int i = 0; i < 5; i++) begin
my_mailbox.get(data); // Retrieve data from the mailbox
$display("Consumer: Received data %0d", data);
#10; // Wait 10 time units
end
endtask
initial begin
fork
producer();
consumer();
join
end
endmodule
Explanation:
- The
producertask generates integer data values and uses put() to send them to the mailbox. - The
consumertask retrieves each value from the mailbox using get() and displays it. - The
fork...joinblock allows both producer and consumer to run concurrently.
Output:
Producer: Sending data 0
Consumer: Received data 0
Producer: Sending data 1
Consumer: Received data 1
Producer: Sending data 2
Producer: Sending data 3
Consumer: Received data 2
Producer: Sending data 4
Consumer: Received data 3
Consumer: Received data 4
Summary of Mailbox Methods
| Method | Description |
|---|---|
put() | Places an item in the mailbox, blocks if bounded and full |
get() | Retrieves and removes an item, blocks if empty |
try_put() | Non-blocking put(), returns false if bounded and full |
try_get() | Non-blocking get(), returns false if empty |
peek() | Inspects the first item without removing it, blocks if empty |
try_peek() | Non-blocking peek(), returns false if empty |