While learning System Verilog you always thought like How do you pass information between two threads/processes?
The solution is a SystemVerilog mailbox. From a hardware point of view, the easiest way to think about a mailbox is that it is just a FIFO, with a source and sink. The source puts data into the mailbox, and the sink gets values from the mailbox.
A mailbox is a communication mechanism that allows messages to be exchanged between processes. Data can be sent to a mailbox by one process and retrieved by another.
Conceptually, mailboxes behave like real mailboxes. When a letter is delivered and put into the mailbox, a person can retrieve the letter from that mailbox (and any data stored within). However, if the letter has not been delivered when the mailbox is checked, the person must choose whether to wait for the letter or to retrieve the letter on a subsequent trip to the mailbox.
Similarly, System Verilog mailboxes provide processes to transfer and retrieve data in a controlled manner. Mailboxes are created as having either a bounded or unbounded queue size.
The bounded mailbox becomes full when it contains the bounded number of messages. A process that attempts to place a message into a full mailbox shall be suspended until enough room becomes available in the mailbox queue. While Unbounded mailbox never suspends a thread in a send operation.
Mailbox is a built-in class that provides the following methods:
- Create a mailbox: new()
- Place a message in a mailbox: put() //Blocking in nature
- Place a message in a mailbox without blocking: try_put()
- Retrieve a message from a mailbox: get() or peek() //Blocking in nature
- Retrieve a message from a mailbox without blocking: try_get() or try_peek()
- Retrieve the number of messages in the mailbox: num()
A put() blocks if the mailbox is full, and get() blocks if the mailbox is empty.
Use try_put() if you want to see if the mailbox is full. And try_get() to see if it is empty. Both are non-blocking methods.
If they are successful, they return a nonzero value; otherwise, they return 0. In other words,
If the mailbox is full, the method try_put() returns 0.
If the mailbox is empty, then the method try_get() or try_peek() returns 0.
The peek() task gets a copy of the data in the mailbox but does not remove it. While get() task retrieve a copy of the data from the mailbox which removes data from the mailbox.
A mailbox never contains objects, only references to them.
There are two types of mailboxes,
1. Generic Mailbox (type-less mailbox)
The default mailbox is type-less, that is, a single mailbox can send and receive any type of data.
2. Parameterized mailbox (mailbox with particular type)
mailbox is used to transfer a particular type of data.
Let’s go through by following example
class transaction; rand bit [7:0] data; rand bit [7:0] addr; endclass : transaction class generator; task transmit_good(input int unsigned n, input mailbox #(transaction) mb); transaction tr; repeat (n) begin // constructing the object tr = new(); // randomizing object if (!tr.randomize()) begin $error("randomization failed"); end else begin $display("GEN: transmit_good: after randomization tr.addr=%0h, tr.data=%0h", tr.addr, tr.data); end // putting object in the mailbox mb.put(tr); end endtask : transmit_good endclass : generator class driver; task receive_good(input mailbox #(transaction) mb); transaction tr; forever begin #5ns; mb.get(tr); // drive tranaction to DUT $display("DRV: receive: Received tr.addr=%0h, tr.data=%0h", tr.addr, tr.data); end endtask : receive_good endclass : driver program main; generator gen; driver drv; mailbox #(transaction) mb; initial begin mb = new(); gen = new(); drv = new(); // Run producer and Consumer in parallel fork begin gen.transmit_good (5, mb); end begin drv.receive_good(mb); end join end endprogram
Mailbox Vs Queues:
Mailbox stores the heterogeneous elements means different data types and queue stores homogeneous elements means the same types of data.