Funvizeo logo
  • Contents
      • Back
      • Verilog
      • SystemVerilog
      • UVM
      • Digital Basics
      • Verification
Most Popular
Verification
  Testbench Evolution
  Constraint Random Verification
  Verification Techniques
  Verification Plan
  Code Coverage

Verilog
  Data Types
  Basic Constructs
  Behavioral Modeling
  Gate Modeling
  Simulation Basics
  Design Examples
  Interview Questions

SystemVerilog
  Data Types
  Class
  Interface
  Constraints and more!
  Testbench Examples
  Interview Questions

UVM
  Sequences
  Testbench Components
  TLM Tutorial
  Register Model Tutorial
  Testbench Examples
  Interview Questions

Digital Fundamentals
  Binary Arithmetic
  Boolean Logic
  Karnaugh Maps
  Combinational Logic
  Sequential Logic

SystemVerilog Interprocess Communication

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.

EventsDifferent threads synchronize with each other via event handles in a testbench
SemaphoresDifferent threads might need to access the same resource; they take turns by using a semaphore
MailboxThreads/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
[0] Waiting for EventA to be triggered
[5] 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 !

SystemVerilog Functional Coverage

What is functional coverage ?

Functional coverage is a measure of what functionalities/features of the design have been exercised by the tests. This can be useful in constrained random verification (CRV) to know what features have been covered by a set of tests in a regression.

Read more: SystemVerilog Functional Coverage

Verilog Mod-N counter

Design


module modN_ctr 
  # (parameter N = 10,
     parameter WIDTH = 4)
  
  ( input   clk,
    input   rstn,
   	output  reg[WIDTH-1:0] out);
 
  always @ (posedge clk) begin
    if (!rstn) begin
      out <= 0;
    end else begin
      if (out == N-1) 
        out <= 0;
      else
        out <= out + 1;
    end
  end
endmodule

Testbench


module tb;
  parameter N = 10;
  parameter WIDTH = 4;
  
  reg clk;
  reg rstn;
  wire [WIDTH-1:0] out;
  
  modN_ctr u0  ( 	.clk(clk),
                	.rstn(rstn),
                	.out(out));

  always #10 clk = ~clk;
  
  initial begin
    {clk, rstn} <= 0;
    
    $monitor ("T=%0t rstn=%0b out=0x%0h", $time, rstn, out);
    repeat(2) @ (posedge clk);
    rstn <= 1;
    
    repeat(20) @ (posedge clk);
    $finish;  
  end
endmodule
 Simulation Log
ncsim> run
T=0 rstn=0 out=0xx
T=10 rstn=0 out=0x0
T=30 rstn=1 out=0x0
T=50 rstn=1 out=0x1
T=70 rstn=1 out=0x2
T=90 rstn=1 out=0x3
T=110 rstn=1 out=0x4
T=130 rstn=1 out=0x5
T=150 rstn=1 out=0x6
T=170 rstn=1 out=0x7
T=190 rstn=1 out=0x8
T=210 rstn=1 out=0x9
T=230 rstn=1 out=0xa
T=250 rstn=1 out=0x0
T=270 rstn=1 out=0x1
T=290 rstn=1 out=0x2
T=310 rstn=1 out=0x3
T=330 rstn=1 out=0x4
T=350 rstn=1 out=0x5
T=370 rstn=1 out=0x6
T=390 rstn=1 out=0x7
T=410 rstn=1 out=0x8
Simulation complete via $finish(1) at time 430 NS + 0

SystemVerilog Arrays

SystemVerilog offers much flexibility in building complicated data structures through the different types of arrays.

  • Static Arrays
  • Dynamic Arrays
  • Associative Arrays
  • Queues

Static Arrays

A static array is one whose size is known before compilation time. In the example shown below, a static array of 8-bit wide is declared, assigned some value and iterated over to print its value.


module tb;
	bit [7:0] 	m_data; 	// A vector or 1D packed array
	
	initial begin
		// 1. Assign a value to the vector
		m_data = 8'hA2; 
		
		// 2. Iterate through each bit of the vector and print value
		for (int i = 0; i < $size(m_data); i++) begin
			$display ("m_data[%0d] = %b", i, m_data[i]);
		end
	end
endmodule

Read more: SystemVerilog Arrays

SystemVerilog TestBench

  1. What is the purpose of a testbench ?
  2. Components of a testbench
  3. What is DUT ?

We need to have an environment known as a testbench to run any kind of simulation on the design.

Click here to refresh basic concepts of a simulation

What is the purpose of a testbench ?

A testbench allows us to verify the functionality of a design through simulations. It is a container where the design is placed and driven with different input stimulus.

  1. Generate different types of input stimulus
  2. Drive the design inputs with the generated stimulus
  3. Allow the design to process input and provide an output
  4. Check the output with expected behavior to find functional defects
  5. If a functional bug is found, then change the design to fix the bug
  6. Perform the above steps until there are no more functional defects

Components of a testbench

The example shown in Introduction is not modular, scalable, flexible or even re-usable because of the way DUT is connected, and how signals are driven. Let's take a look at a simple testbench and try to understand about the various components that facilitate data transfer from and to the DUT.

ComponentDescription
GeneratorGenerates different input stimulus to be driven to DUT
InterfaceContains design signals that can be driven or monitored
DriverDrives the generated stimulus to the design
MonitorMonitor the design input-output ports to capture design activity
ScoreboardChecks output from the design with expected behavior
EnvironmentContains all the verification components mentioned above
TestContains the environment that can be tweaked with different configuration settings
simple-testbench

What is DUT ?

DUT stands for Design Under Test and is the hardware design written in Verilog or VHDL. DUT is a term typically used in post validation of the silicon once the chip is fabricated. In pre validation, it is also called as Design Under Verification, DUV in short.


// All verification components are placed in this top testbench module
module tb_top;

	// Declare variables that need to be connected to the design instance
	// These variables are assigned some values that in turn gets transferred to
	// the design as inputs because they are connected with the ports in the design
	reg clk;        
	wire en; 		
	wire wr;
	wire data;

	// Instantiate the design module and connect the variables declared above
	// with the ports in the design
	design myDsn ( .clk (clk),
	               .en  (en),
	               .wr  (wr),
	               . ...
	               .rdata);
	
	// Develop rest of the testbench and write stimulus that can be driven to the design
endmodule

Click here for a complete SystemVerilog testbench example !

Read more: SystemVerilog TestBench

  1. SystemVerilog Threads
  2. SystemVerilog Randomization
  3. SystemVerilog Class
  4. SystemVerilog Interface
  5. SystemVerilog 'break' and 'continue'

Page 59 of 68

  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
Interview Questions
  Verilog Interview Set 1
  Verilog Interview Set 2
  Verilog Interview Set 3
  Verilog Interview Set 4
  Verilog Interview Set 5

  SystemVerilog Interview Set 1
  SystemVerilog Interview Set 2
  SystemVerilog Interview Set 3
  SystemVerilog Interview Set 4
  SystemVerilog Interview Set 5

  UVM Interview Set 1
  UVM Interview Set 2
  UVM Interview Set 3
  UVM Interview Set 4
Related Topics
  Digital Fundamentals
  Verilog Tutorial

  Verification
  SystemVerilog Tutorial
  UVM Tutorial
Latest in Verilog
  • Verilog $random
  • Verilog VCD Dump
  • Verilog VCD
  • Verilog Namespace
  • Verilog $stop $finish
Latest in SystemVerilog
  • SystemVerilog `define Macro
  • SystemVerilog Callback
  • SystemVerilog Interview Questions Set 10
  • SystemVerilog Interview Questions Set 9
  • SystemVerilog Interview Questions Set 8
Latest in UVM
  • UVM Callback
  • UVM Singleton Object
  • UVM Component [uvm_component]
  • UVM Object [uvm_object]
  • UVM Root [uvm_root]
© 2025 Funvizeo
Terms and Conditions