Components in a testbench often need to communicate with each other to exchange data and check output values of the design. A few mechanisms that allow components or threads to affect the control flow of data are shown in the table below.

Events Different threads synchronize with each other via event handles in a testbench
Semaphores Different threads might need to access the same resource; they take turns by using a semaphore
Mailbox Threads/Components need to exchange data with each other; data is put in a mailbox and sent

What are Events ?

An event is a way to synchronize two or more different processes. One process waits for the event to happen while another process triggers the event. When the event is triggered, the process waiting for the event will resume execution.

1. Create an event using event
 
event   eventA;    // Creates an event called "eventA"
 
2. Trigger an event using -> operator
 
  ->eventA;     // Any process that has access to "eventA" can trigger the event
 
3. Wait for event to happen
 
  @eventA;             // Use "@" operator to wait for an event
  wait (eventA.triggered);    // Or use the wait statement with "eventA.triggered" 
 
4. Pass events as arguments to functions
 
module tb_top;
  event eventA;     // Declare an event handle called  "eventA"
 
  initial begin     
    fork 
      waitForTrigger (eventA);    // Task waits for eventA to happen
      #5 ->eventA;                // Triggers eventA
    join
  end
 
  // The event is passed as an argument to this task. It simply waits for the event 
  // to be triggered
  task waitForTrigger (event eventA);
    $display ("[%0t] Waiting for EventA to be triggered", $time);
    wait (eventA.triggered);
    $display ("[%0t] EventA has triggered", $time);
  endtask
endmodule
 
Simulation Log
[20] Waiting for EventA to be triggered
[25] EventA has triggered
ncsim: *W,RNQUIE: Simulation is complete.

Click here to read more about a SystemVerilog event !

What's a semaphore ?

Let's say you wanted to rent a room in the library for a few hours. The admin desk will give you a key to use the room for the time you have requested access. After you are done with your work, you will return the key to the admin, which will then be given to someone else who wants to use the same room. This way two people will not be allowed to use the room at the same time. The key is a semaphore in this context.

A semaphore is used to control access to a resource and is known as a mutex (mutually exclusive) because only one entity can have the semaphore at a time.

 
module tb_top;
   semaphore key;         // Create a semaphore handle called "key"
 
   initial begin 
      key = new (1);       // Create only a single key; multiple keys are also possible
      fork
         personA ();       // personA tries to get the room and puts it back after work
         personB ();       // personB also tries to get the room and puts it back after work
         #25 personA ();     // personA tries to get the room a second time
      join_none
   end
 
   task getRoom (bit [1:0] id);
      $display ("[%0t] Trying to get a room for id[%0d] ...", $time, id);
      key.get (1);
      $display ("[%0t] Room Key retrieved for id[%0d]", $time, id);
   endtask
 
   task putRoom (bit [1:0] id);
      $display ("[%0t] Leaving room id[%0d] ...", $time, id);
      key.put (1);
      $display ("[%0t] Room Key put back id[%0d]", $time, id);
   endtask
 
   // This person tries to get the room immediately and puts 
   // it back 20 time units later
   task personA ();       
      getRoom (1);
      #20 putRoom (1);
   endtask
 
  // This person tries to get the room after 5 time units and puts it back after
  // 10 time units
   task personB ();
      #5  getRoom (2);
      #10 putRoom (2);
   endtask
endmodule
 
Simulation Log
[0] Trying to get a room for id[1] ...
[0] Room Key retrieved for id[1]
[5] Trying to get a room for id[2] ...
[20] Leaving room id[1] ...
[20] Room Key put back id[1]
[20] Room Key retrieved for id[2]
[25] Trying to get a room for id[1] ...
[30] Leaving room id[2] ...
[30] Room Key put back id[2]
[30] Room Key retrieved for id[1]
[50] Leaving room id[1] ...
[50] Room Key put back id[1]

Note the following about semaphores.

  • A semaphore object key is declared and created using new () function. Argument to new () defines the number of keys.
  • You get the key by using the get () keyword which will wait until a key is available (blocking)
  • You put the key back using the put () keyword

Click here to read more about a SystemVerilog semaphore !

What's a mailbox ?

A mailbox is like a dedicated channel established to send data between two components.

For example, a mailbox can be created and the handles be passed to a data generator and a driver. The generator can push the data object into the mailbox and the driver will be able to retrieve the packet and drive the signals onto the bus.

 
// Data packet in this environment
class transaction;
   rand bit [7:0] data;
 
   function display ();
      $display ("[%0t] Data = 0x%0h", $time, data);
   endfunction
endclass
 
// Generator class - Generate a transaction object and put into mailbox
class generator;
   mailbox mbx;
 
   function new (mailbox mbx);
      this.mbx = mbx;
   endfunction
 
   task genData ();
      transaction trns = new ();
      trns.randomize ();
      trns.display ();
      $display ("[%0t] [Generator] Going to put data packet into mailbox", $time);
      mbx.put (trns);
      $display ("[%0t] [Generator] Data put into mailbox", $time);
   endtask
endclass
 
// Driver class - Get the transaction object from Generator
class driver;
   mailbox mbx;
 
   function new (mailbox mbx);
      this.mbx = mbx;
   endfunction
 
   task drvData ();
      transaction drvTrns = new ();
      $display ("[%0t] [Driver] Waiting for available data", $time);
      mbx.get (drvTrns);
      $display ("[%0t] [Driver] Data received from Mailbox", $time);
      drvTrns.display ();
   endtask
endclass
 
// Top Level environment that will connect Gen and Drv with a mailbox
module tb_top;
   mailbox   mbx;
   generator Gen;
   driver    Drv;
 
   initial begin
      mbx = new ();
      Gen = new (mbx);
      Drv = new (mbx);
 
      fork 
         #10 Gen.genData ();
         Drv.drvData ();
      join_none
   end
endmodule
 
Simulation Log
[0] [Driver] Waiting for available data
[10] Data = 0x9d
[10] [Generator] Put data packet into mailbox
[10] [Generator] Data put into mailbox
[10] [Driver] Data received from Mailbox
[10] Data = 0x9d
ncsim: *W,RNQUIE: Simulation is complete.

Click here to read more about a SystemVerilog mailbox !

You may also like:

You consent to our cookies if you continue to use our website. To know more about cookies, see our privacy policy. I accept cookies from this site.

Agree