Ready-to-run snippets

Code Examples

Ready-to-run Verilog, SystemVerilog, and UVM examples. Click any to open it live in the IDE.

← All examples

Testbench Top

SystemVerilog Testbench Components

A complete class-based (non-UVM) testbench for a packet switch: generator, driver, semaphore-pipelined monitor, and a scoreboard that checks address routing.

Open in IDE
testbench.sv
// This is the base transaction object that will be used
// in the environment to initiate new transactions and 
// capture transactions at DUT interface
class switch_item;
  rand bit [7:0]  	addr;
  rand bit [15:0] 	data;
  bit [7:0] 		addr_a;
  bit [15:0] 		data_a;
  bit [7:0] 		addr_b;
  bit [15:0] 		data_b;

    // This function allows us to print contents of the data
  	// packet so that it is easier to track in a logfile
  function void print (string tag="");
    $display ("T=%0t %s addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h addr_b=0x%0h data_b=0x%0h",
              	$time, tag, addr, data, addr_a, data_a, addr_b, data_b);
  endfunction
endclass


// The generator class is used to generate a random 
// number of transactions with random addresses and data 
// that can be driven to the design
class generator;
  mailbox drv_mbx;
  event drv_done;
  int num = 20;
  
  task run();
    for (int i = 0; i < num; i++) begin
      switch_item item = new;
      item.randomize();
      $display ("T=%0t [Generator] Loop:%0d/%0d create next item", $time, i+1, num);
      drv_mbx.put(item);
      @(drv_done);
    end
    $display ("T=%0t [Generator] Done generation of %0d items", $time, num);
  endtask
endclass

// The driver is responsible for driving transactions to the DUT 
// All it does is to get a transaction from the mailbox if it is 
// available and drive it out into the DUT interface.
class driver;
  virtual switch_if vif;
  event drv_done;
  mailbox drv_mbx;
  
  task run();
    $display ("T=%0t [Driver] starting ...", $time);
    @ (posedge vif.clk);
    
    // Try to get a new transaction every time and then assign 
    // packet contents to the interface. But do this only if the 
    // design is ready to accept new transactions
    forever begin
      switch_item item;
      
      $display ("T=%0t [Driver] waiting for item ...", $time);
      drv_mbx.get(item);      
	  item.print("Driver");
      vif.vld 	<= 1;
      vif.addr 	<= item.addr;
      vif.data <= item.data;
      
      // When transfer is over, raise the done event
      @ (posedge vif.clk);
      vif.vld 	<= 0;
      ->drv_done;
    end   
  endtask
endclass

// The monitor has a virtual interface handle with which 
// it can monitor the events happening on the interface.
// It sees new transactions and then captures information 
// into a packet and sends it to the scoreboard
// using another mailbox.
class monitor;
  virtual switch_if vif;
  mailbox scb_mbx;
  semaphore sema4;
  
  function new ();
    sema4 = new(1);
  endfunction
  
  task run();
    $display ("T=%0t [Monitor] starting ...", $time);
    
    // To get a pipeline effect of transfers, fork two threads
    // where each thread uses a semaphore for the address phase
	fork
      sample_port("Thread0");
      sample_port("Thread1");
    join
  endtask
  
  task sample_port(string tag="");
    // This task monitors the interface for a complete 
    // transaction and pushes into the mailbox when the 
    // transaction is complete
    forever begin
      @(posedge vif.clk);
      if (vif.rstn & vif.vld) begin
        switch_item item = new;
        sema4.get();
        item.addr = vif.addr;
        item.data = vif.data;
        $display("T=%0t [Monitor] %s First part over", 
                 						$time, tag);
        @(posedge vif.clk);
		sema4.put();
        item.addr_a = vif.addr_a;
        item.data_a = vif.data_a;              
        item.addr_b = vif.addr_b;
        item.data_b = vif.data_b;
        $display("T=%0t [Monitor] %s Second part over", 
                 						$time, tag);        
        scb_mbx.put(item);
        item.print({"Monitor_", tag});                
      end
    end
  endtask
endclass

// The scoreboard is responsible to check data integrity. Since
// the design routes packets based on an address range, the
// scoreboard checks that the packet's address is within valid
// range.
class scoreboard;
  mailbox scb_mbx;
    
  task run();
    forever begin
      switch_item item;
      scb_mbx.get(item);
      
      if (item.addr inside {[0:'h3f]}) begin
        if (item.addr_a != item.addr | item.data_a != item.data)
          $display ("T=%0t [Scoreboard] ERROR! Mismatch addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h", $time, item.addr, item.data, item.addr_a, item.data_a);
        else
          $display ("T=%0t [Scoreboard] PASS! Match addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h", $time, item.addr, item.data, item.addr_a, item.data_a);
      
      end else begin
        if (item.addr_b != item.addr | item.data_b != item.data)
          $display ("T=%0t [Scoreboard] ERROR! Mismatch addr=0x%0h data=0x%0h addr_b=0x%0h data_b=0x%0h", $time, item.addr, item.data, item.addr_b, item.data_b);
        else
          $display ("T=%0t [Scoreboard] PASS! Match addr=0x%0h data=0x%0h addr_b=0x%0h data_b=0x%0h", $time, item.addr, item.data, item.addr_b, item.data_b);
      end
    end
  endtask
endclass

// The environment is a container object simply to hold 
// all verification  components together. This environment can
// then be reused later and all components in it would be
// automatically connected and available for use
class env;
  driver 		d0; 		// Driver handle
  monitor 		m0; 		// Monitor handle
  generator		g0; 		// Generator Handle
  scoreboard	s0; 		// Scoreboard handle
  
  mailbox 	drv_mbx; 		// Connect GEN -> DRV
  mailbox 	scb_mbx; 		// Connect MON -> SCB
  event 	drv_done; 		// Indicates when driver is done
  
  virtual switch_if vif; 	// Virtual interface handle
  
  function new();
    d0 = new;
    m0 = new;
    g0 = new;
    s0 = new;
    drv_mbx = new();
    scb_mbx = new();
    
    d0.drv_mbx = drv_mbx;
    g0.drv_mbx = drv_mbx;
    m0.scb_mbx = scb_mbx;
    s0.scb_mbx = scb_mbx;
    
    d0.drv_done = drv_done;
    g0.drv_done = drv_done;
  endfunction
  
  virtual task run();
    d0.vif = vif;
    m0.vif = vif;
    
    fork
      d0.run();
      m0.run();
      g0.run();
      s0.run();
    join_any
  endtask
endclass

// Test class instantiates the environment and starts it.
class test;
  env e0;
  
  function new();
    e0 = new;
  endfunction
  
  task run();
    e0.run();
  endtask
endclass

// Design interface used to monitor activity and capture/drive 
// transactions
interface switch_if (input bit clk);
  logic 		rstn;
  logic 		vld;
  logic [7:0] 	addr;
  logic [15:0]	data;
  
  logic [7:0] 	addr_a;
  logic [15:0]	data_a;
  
  logic [7:0] 	addr_b;
  logic [15:0]	data_b;
endinterface

// Top level testbench module to instantiate design, interface
// start clocks and run the test
module tb;
  reg clk;
  
  always #10 clk =~ clk;
  switch_if 	_if (clk);
  switch u0 ( 	.clk(clk),
             .rstn(_if.rstn),
             .addr(_if.addr),
             .data(_if.data),
             .vld (_if.vld),
             .addr_a(_if.addr_a),
             .data_a(_if.data_a),
             .addr_b(_if.addr_b),
             .data_b(_if.data_b));
  test t0;
  
  initial begin
    {clk, _if.rstn} <= 0;
    
    // Apply reset and start stimulus
    #20 _if.rstn <= 1;
    t0 = new;
    t0.e0.vif = _if;
    t0.run();
    
    // Because multiple components and clock are running
    // in the background, we need to call $finish explicitly
    #50 $finish;
  end
  
  // System tasks to dump VCD waveform file
  initial begin
    $dumpvars;
    $dumpfile ("dump.vcd");
  end
endmodule




/*
Simulation Log:
---------------

ncsim> run
T=20 [Driver] starting ...
T=20 [Monitor] starting ...
T=20 [Generator] Loop:1/20 create next item
T=30 [Driver] waiting for item ...
T=30 Driver addr=0x68 data=0xe394 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=50 [Driver] waiting for item ...
T=50 [Monitor] Thread0 First part over
T=50 [Generator] Loop:2/20 create next item
T=50 Driver addr=0x26 data=0x5336 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=70 [Monitor] Thread0 Second part over
T=70 Monitor_Thread0 addr=0x68 data=0xe394 addr_a=0x0 data_a=0x0 addr_b=0x68 data_b=0xe394
T=70 [Driver] waiting for item ...
T=70 [Monitor] Thread1 First part over
T=70 [Scoreboard] PASS! Match addr=0x68 data=0xe394 addr_b=0x68 data_b=0xe394
T=70 [Generator] Loop:3/20 create next item
T=70 Driver addr=0xea data=0xe19f addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=90 [Monitor] Thread1 Second part over
T=90 Monitor_Thread1 addr=0x26 data=0x5336 addr_a=0x26 data_a=0x5336 addr_b=0x0 data_b=0x0
T=90 [Driver] waiting for item ...
T=90 [Monitor] Thread0 First part over
T=90 [Scoreboard] PASS! Match addr=0x26 data=0x5336 addr_a=0x26 data_a=0x5336
T=90 [Generator] Loop:4/20 create next item
T=90 Driver addr=0x8d data=0x5411 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=110 [Monitor] Thread0 Second part over
T=110 Monitor_Thread0 addr=0xea data=0xe19f addr_a=0x0 data_a=0x0 addr_b=0xea data_b=0xe19f
T=110 [Driver] waiting for item ...
T=110 [Monitor] Thread1 First part over
T=110 [Scoreboard] PASS! Match addr=0xea data=0xe19f addr_b=0xea data_b=0xe19f
T=110 [Generator] Loop:5/20 create next item
T=110 Driver addr=0x30 data=0x2e4d addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=130 [Monitor] Thread1 Second part over
T=130 Monitor_Thread1 addr=0x8d data=0x5411 addr_a=0x0 data_a=0x0 addr_b=0x8d data_b=0x5411
T=130 [Driver] waiting for item ...
T=130 [Monitor] Thread0 First part over
T=130 [Scoreboard] PASS! Match addr=0x8d data=0x5411 addr_b=0x8d data_b=0x5411
T=130 [Generator] Loop:6/20 create next item
T=130 Driver addr=0x5a data=0xc2b9 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=150 [Monitor] Thread0 Second part over
T=150 Monitor_Thread0 addr=0x30 data=0x2e4d addr_a=0x30 data_a=0x2e4d addr_b=0x0 data_b=0x0
T=150 [Driver] waiting for item ...
T=150 [Monitor] Thread1 First part over
T=150 [Scoreboard] PASS! Match addr=0x30 data=0x2e4d addr_a=0x30 data_a=0x2e4d
T=150 [Generator] Loop:7/20 create next item
T=150 Driver addr=0x84 data=0x11f4 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=170 [Monitor] Thread1 Second part over
T=170 Monitor_Thread1 addr=0x5a data=0xc2b9 addr_a=0x0 data_a=0x0 addr_b=0x5a data_b=0xc2b9
T=170 [Driver] waiting for item ...
T=170 [Monitor] Thread0 First part over
T=170 [Scoreboard] PASS! Match addr=0x5a data=0xc2b9 addr_b=0x5a data_b=0xc2b9
T=170 [Generator] Loop:8/20 create next item
T=170 Driver addr=0x4 data=0x4ce2 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=190 [Monitor] Thread0 Second part over
T=190 Monitor_Thread0 addr=0x84 data=0x11f4 addr_a=0x0 data_a=0x0 addr_b=0x84 data_b=0x11f4
T=190 [Driver] waiting for item ...
T=190 [Monitor] Thread1 First part over
T=190 [Scoreboard] PASS! Match addr=0x84 data=0x11f4 addr_b=0x84 data_b=0x11f4
T=190 [Generator] Loop:9/20 create next item
T=190 Driver addr=0xf3 data=0xc6d8 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=210 [Monitor] Thread1 Second part over
T=210 Monitor_Thread1 addr=0x4 data=0x4ce2 addr_a=0x4 data_a=0x4ce2 addr_b=0x0 data_b=0x0
T=210 [Driver] waiting for item ...
T=210 [Monitor] Thread0 First part over
T=210 [Scoreboard] PASS! Match addr=0x4 data=0x4ce2 addr_a=0x4 data_a=0x4ce2
T=210 [Generator] Loop:10/20 create next item
T=210 Driver addr=0xb0 data=0xf534 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=230 [Monitor] Thread0 Second part over
T=230 Monitor_Thread0 addr=0xf3 data=0xc6d8 addr_a=0x0 data_a=0x0 addr_b=0xf3 data_b=0xc6d8
T=230 [Driver] waiting for item ...
T=230 [Monitor] Thread1 First part over
T=230 [Scoreboard] PASS! Match addr=0xf3 data=0xc6d8 addr_b=0xf3 data_b=0xc6d8
T=230 [Generator] Loop:11/20 create next item
T=230 Driver addr=0x17 data=0xff19 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=250 [Monitor] Thread1 Second part over
T=250 Monitor_Thread1 addr=0xb0 data=0xf534 addr_a=0x0 data_a=0x0 addr_b=0xb0 data_b=0xf534
T=250 [Driver] waiting for item ...
T=250 [Monitor] Thread0 First part over
T=250 [Scoreboard] PASS! Match addr=0xb0 data=0xf534 addr_b=0xb0 data_b=0xf534
T=250 [Generator] Loop:12/20 create next item
T=250 Driver addr=0x92 data=0xbc1e addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=270 [Monitor] Thread0 Second part over
T=270 Monitor_Thread0 addr=0x17 data=0xff19 addr_a=0x17 data_a=0xff19 addr_b=0x0 data_b=0x0
T=270 [Driver] waiting for item ...
T=270 [Monitor] Thread1 First part over
T=270 [Scoreboard] PASS! Match addr=0x17 data=0xff19 addr_a=0x17 data_a=0xff19
T=270 [Generator] Loop:13/20 create next item
T=270 Driver addr=0x5f data=0x6da5 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=290 [Monitor] Thread1 Second part over
T=290 Monitor_Thread1 addr=0x92 data=0xbc1e addr_a=0x0 data_a=0x0 addr_b=0x92 data_b=0xbc1e
T=290 [Driver] waiting for item ...
T=290 [Monitor] Thread0 First part over
T=290 [Scoreboard] PASS! Match addr=0x92 data=0xbc1e addr_b=0x92 data_b=0xbc1e
T=290 [Generator] Loop:14/20 create next item
T=290 Driver addr=0xec data=0x2503 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=310 [Monitor] Thread0 Second part over
T=310 Monitor_Thread0 addr=0x5f data=0x6da5 addr_a=0x0 data_a=0x0 addr_b=0x5f data_b=0x6da5
T=310 [Driver] waiting for item ...
T=310 [Monitor] Thread1 First part over
T=310 [Scoreboard] PASS! Match addr=0x5f data=0x6da5 addr_b=0x5f data_b=0x6da5
T=310 [Generator] Loop:15/20 create next item
T=310 Driver addr=0x9c data=0x1b8f addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=330 [Monitor] Thread1 Second part over
T=330 Monitor_Thread1 addr=0xec data=0x2503 addr_a=0x0 data_a=0x0 addr_b=0xec data_b=0x2503
T=330 [Driver] waiting for item ...
T=330 [Monitor] Thread0 First part over
T=330 [Scoreboard] PASS! Match addr=0xec data=0x2503 addr_b=0xec data_b=0x2503
T=330 [Generator] Loop:16/20 create next item
T=330 Driver addr=0xb7 data=0x50ed addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=350 [Monitor] Thread0 Second part over
T=350 Monitor_Thread0 addr=0x9c data=0x1b8f addr_a=0x0 data_a=0x0 addr_b=0x9c data_b=0x1b8f
T=350 [Driver] waiting for item ...
T=350 [Monitor] Thread1 First part over
T=350 [Scoreboard] PASS! Match addr=0x9c data=0x1b8f addr_b=0x9c data_b=0x1b8f
T=350 [Generator] Loop:17/20 create next item
T=350 Driver addr=0x3e data=0xbda3 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=370 [Monitor] Thread1 Second part over
T=370 Monitor_Thread1 addr=0xb7 data=0x50ed addr_a=0x0 data_a=0x0 addr_b=0xb7 data_b=0x50ed
T=370 [Driver] waiting for item ...
T=370 [Monitor] Thread0 First part over
T=370 [Scoreboard] PASS! Match addr=0xb7 data=0x50ed addr_b=0xb7 data_b=0x50ed
T=370 [Generator] Loop:18/20 create next item
T=370 Driver addr=0x21 data=0x4ffc addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=390 [Monitor] Thread0 Second part over
T=390 Monitor_Thread0 addr=0x3e data=0xbda3 addr_a=0x3e data_a=0xbda3 addr_b=0x0 data_b=0x0
T=390 [Driver] waiting for item ...
T=390 [Monitor] Thread1 First part over
T=390 [Scoreboard] PASS! Match addr=0x3e data=0xbda3 addr_a=0x3e data_a=0xbda3
T=390 [Generator] Loop:19/20 create next item
T=390 Driver addr=0xec data=0xe906 addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=410 [Monitor] Thread1 Second part over
T=410 Monitor_Thread1 addr=0x21 data=0x4ffc addr_a=0x21 data_a=0x4ffc addr_b=0x0 data_b=0x0
T=410 [Driver] waiting for item ...
T=410 [Monitor] Thread0 First part over
T=410 [Scoreboard] PASS! Match addr=0x21 data=0x4ffc addr_a=0x21 data_a=0x4ffc
T=410 [Generator] Loop:20/20 create next item
T=410 Driver addr=0xf data=0xbc6f addr_a=0x0 data_a=0x0 addr_b=0x0 data_b=0x0
T=430 [Monitor] Thread0 Second part over
T=430 Monitor_Thread0 addr=0xec data=0xe906 addr_a=0x0 data_a=0x0 addr_b=0xec data_b=0xe906
T=430 [Driver] waiting for item ...
T=430 [Monitor] Thread1 First part over
T=430 [Scoreboard] PASS! Match addr=0xec data=0xe906 addr_b=0xec data_b=0xe906
T=430 [Generator] Done generation of 20 items
T=450 [Monitor] Thread1 Second part over
T=450 Monitor_Thread1 addr=0xf data=0xbc6f addr_a=0xf data_a=0xbc6f addr_b=0x0 data_b=0x0
T=450 [Scoreboard] PASS! Match addr=0xf data=0xbc6f addr_a=0xf data_a=0xbc6f
Simulation complete via $finish(1) at time 480 NS + 0
./testbench.sv:258     #50 $finish;
ncsim> exit
*/
design.sv
module switch 
  # (parameter ADDR_WIDTH = 8,
     parameter DATA_WIDTH = 16,
     parameter ADDR_DIV = 8'h3F
    )
  
  ( input clk,
   	input rstn,
    input vld,
    
    input [ADDR_WIDTH-1:0] 	addr,
   input [DATA_WIDTH-1:0] 	data,
   
   output reg [ADDR_WIDTH-1:0] 	addr_a,
   output reg [DATA_WIDTH-1:0] 	data_a,

   output reg [ADDR_WIDTH-1:0] 	addr_b,
   output reg [DATA_WIDTH-1:0] 	data_b
  );
  
  always @ (posedge clk) begin
    if (!rstn) begin
      	addr_a <= 0;
    	data_a <= 0;
    	addr_b <= 0;
    	data_b <= 0;
    end else begin
      if (vld) begin
      	if (addr >= 0 & addr <= ADDR_DIV) begin
        	addr_a <= addr;
      		data_a <= data;
          	addr_b <= 0;
          	data_b <= 0;
      	end else begin
          	addr_a <= 0;
          	data_a <= 0;
        	addr_b <= addr;
        	data_b <= data;
      	end
      end
    end
  end
endmodule
Admin mode — edit and delete controls are visible on each row. Use the IDE to add new snippets via File → Save as Snippet.