Welcome ! This website will help YOU (recent graduates/professionals) learn verification languages like SystemVerilog and UVM. Register for free and access more content !

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:

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
 
Simulation Log
ncsim> run
[1] Thread0: Put item #0, size=1
[2] Thread1:   Got item #0, size=0
[2] Thread0: Put item #1, size=1
[3] Thread0: Put item #2, size=2
[4] Thread1:   Got item #1, size=1
[4] Thread0: Put item #3, size=2
[6] Thread1:   Got item #2, size=2
[6] Thread0: Put item #4, size=2
[8] Thread1:   Got item #3, size=1
[10] Thread1:   Got item #4, size=0
ncsim: *W,RNQUIE: Simulation is complete.

Click to try this example in a simulator!   

SystemVerilog Mailbox Functions and Methods

Function Description
function new (int bound = 0); Returns a mailbox handle, bound > 0 represents size of mailbox queue
function int num (); Returns the number of messages currently in the mailbox
task put (singular message); Blocking method that stores a message in the mailbox in FIFO order; message is any singular expression
function int try_put (singular message); Non-blocking method that stores a message if the mailbox is not full, returns a postive integer if successful else 0
task get (ref singular message); Blocking method until it can retrieve one message from the mailbox, if empty blocks the process
function int try_get (ref singular message); Non-blocking method which tries to get one message from the mailbox, returns 0 if empty
task peek (ref singular message); Copies one message from the mailbox without removing the message from the mailbox queue.
function int try_peek (ref singular message); Tries to copy one message from the mailbox without removing the message from queue

Parameterized mailboxes

By default, a SystemVerilog mailbox is typeless and hence can send and receive objects of mixed data-types. Although this is a good feature, it can result in type mismatches during simulation time and result in errors. To constrain the mailbox to accept and send objects of a fixed data-type, it can be parameterized to that particular data-type.

Example

In the example shown below, we first create an alias for mailboxes that can send and receive strings using the typedef construct. Although this step is optional, it is a good practice to avoid type mismatches between different components from coding errors. Consider that comp1 sends a few strings to comp2 via this mailbox. Naturally both classes need to have a mailbox handle which needs to be connected together and this is best done at the top level or the module where these two classes are instantiated.

 
// Create alias for parameterized "string" type mailbox
typedef mailbox #(string) s_mbox;
 
// Define a component to send messages
class comp1;
 
    // Create a mailbox handle to put items
    s_mbox   names;   
 
  // Define a task to put items into the mailbox
  task send ();
    for (int i = 0; i < 3; i++) begin
      string s = $sformatf ("name_%0d", i);
          #1 $display ("[%0t] Comp1: Put %s", $time, s);
      names.put(s);      
    end
  endtask
endclass
 
// Define a second component to receive messages
class comp2;
 
  // Create a mailbox handle to receive items
  s_mbox   list;
 
 
    // Create a loop that continuously gets an item from
    // the mailbox
  task receive ();
    forever begin
      string s;
      list.get(s);
            $display ("[%0t]    Comp2: Got %s", $time, s);
    end
  endtask
endclass
 
// Connect both mailbox handles at a higher level
module tb;
    // Declare a global mailbox and create both components
    s_mbox   m_mbx    = new();
    comp1   m_comp1  = new();
    comp2   m_comp2  = new();
 
    initial begin
      // Assign both mailbox handles in components with the 
      // global mailbox
      m_comp1.names = m_mbx;
      m_comp2.list = m_mbx;
 
      // Start both components, where comp1 keeps sending
      // and comp2 keeps receiving
      fork
        m_comp1.send();
        m_comp2.receive();
      join
    end
endmodule
 
Simulation Log
ncsim> run
[1] Comp1: Put name_0
[1]    Comp2: Got name_0
[2] Comp1: Put name_1
[2]    Comp2: Got name_1
[3] Comp1: Put name_2
[3]    Comp2: Got name_2
ncsim: *W,RNQUIE: Simulation is complete.

Click to try this example in a simulator!   

Matching different type mailboxes

Let's see what would have happened if the SystemVerilog mailboxes were parameterized to different data-types. Consider comp1 to have string type and comp2 to have a byte type mailbox.

 
class comp2;
  mailbox #(byte)   list;
 
  ...
endclass
 
module tb;
  s_mbox   m_mbx;
  ...
 
  initial begin
    m_comp1.names = m_mbx;
    m_comp2.list  = m_mbx;
  end
endmodule
 

This would result in a compile time error, and allow us to revisit the testbench code to correct the type mismatch.

Simulation Log
 m_comp2.list = m_mbx;
                         |
ncvlog: *E,TYCMPAT (testbench.sv,34|25): assignment operator type check failed (expecting datatype compatible with 'mailbox' but found '$unit::s_mbox (mailbox)' instead).

Click to try this example in a simulator!   

Was this article helpful ?

We use cookies to personalize content and ads, to provide social media features and to analyze our traffic. You consent to our cookies if you continue to use our website. To find out more about the cookies we use and how to delete them, see our privacy policy.

  I accept cookies from this site.
Agree
EU Cookie Directive plugin by www.channeldigital.co.uk