Mailbox in System Verilog

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.

            mailbox mailbox_name;

    2. Parameterized mailbox (mailbox with particular type)

        mailbox is used to transfer a particular  type of data.

            mailbox#(type) mailbox_name;

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.

on Facebook on Google+

About the author

The Art of Verification

Hi, I’m Hardik, and welcome to The Art of Verification.

I’m a Verification Engineer who loves to crack complex designs and here to help others commit to mastering Verification Skills through self-learning, System Verilog, UVM, and most important to develop that thought process that every verification engineer should have.

I’ve made it my mission to give back and serve others beyond myself.

I will NEVER settle for less than I can be, do, give, or create.

View all posts