image/svg+xml
  • Contents
      • Back
      • Digital Basics
      • Verilog
      • Verification
      • SystemVerilog
      • UVM
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

SystemVerilog
  Data Types
  Class
  Interface
  Constraints and more!
  Testbench Examples

UVM
  Sequences
  Testbench Components
  TLM Tutorial
  Register Model Tutorial
  Testbench Examples

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




UVM Verification Testbench Example

This session is a real example of how design and verification happens in the real industry. We'll go through the design specification, write a test plan that details how the design will be tested, develop a UVM testbench structure and verify the design.

Design

This is a simple pattern detector written in Verilog to identify a pattern in a stream of input values. On every clock, there is a new input to the design and when it matches the pattern '1011', the output out will be set to 1. For this purpose, the design is implemented as a state machine which moves through different stages as it progresses through pattern identification sequence.

Read more: UVM Verification Testbench Example

SystemVerilog Testbench Example Adder

Here is an example of how a SystemVerilog testbench can be constructed to verify functionality of a simple adder. Remember that the goal here is to develop a modular and scalable testbench architecture with all the standard verification components in a testbench.

You can also write Verilog code for testing such simple circuits, but bigger and more complex designs typically require a scalable testbench architecture and this is an example of how to build a scalable testbench. Different designs require different driver, monitor and scoreboard implementation that depends on design specifics.

Design


// An adder is combinational logic and does not
// have a clock

module my_adder (adder_if _if);
  always_comb begin
    if (_if.rstn) begin
      _if.sum <= 0;
      _if.carry <= 0;
    end else begin
      {_if.carry, _if.sum} <= _if.a + _if.b;
    end
  end
endmodule

Transaction Object


// To verify that the adder adds, we also need to check that it 
// does not add when rstn is 0, and hence rstn should also be 
// randomized along with a and b.
class Packet;
  rand bit 		rstn;
  rand bit[7:0] a;
  rand bit[7:0] b;
  bit [7:0] 	sum;
  bit 			carry;
  
  // Print contents of the data packet
  function void print(string tag="");
    $display ("T=%0t %s a=0x%0h b=0x%0h sum=0x%0h carry=0x%0h", $time, tag, a, b, sum, carry);
  endfunction
  
  // This is a utility function to allow copying contents in 
  // one Packet variable to another.
  function void copy(Packet tmp);
    this.a = tmp.a;
    this.b = tmp.b;
    this.rstn = tmp.rstn;
    this.sum = tmp.sum;
    this.carry = tmp.carry;
  endfunction
endclass

Driver


class driver;
  virtual adder_if m_adder_vif;
  virtual clk_if  m_clk_vif;
  event drv_done;
  mailbox drv_mbx;
  
  task run();
    $display ("T=%0t [Driver] starting ...", $time);
    
    // 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
      Packet item;
      
      $display ("T=%0t [Driver] waiting for item ...", $time);
      drv_mbx.get(item);
      @ (posedge m_clk_vif.tb_clk);
	  item.print("Driver");
      m_adder_vif.rstn <= item.rstn;
      m_adder_vif.a <= item.a;
      m_adder_vif.b <= item.b;
      ->drv_done;
    end   
  endtask
endclass

Monitor


// 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 adder_if 	m_adder_vif;
  virtual clk_if 	m_clk_vif;
  
  mailbox scb_mbx; 		// Mailbox connected to scoreboard
  
  task run();
    $display ("T=%0t [Monitor] starting ...", $time);
    
    // Check forever at every clock edge to see if there is a 
    // valid transaction and if yes, capture info into a class
    // object and send it to the scoreboard when the transaction 
    // is over.
    forever begin
	  Packet m_pkt = new();
      @(posedge m_clk_vif.tb_clk);
      #1;
        m_pkt.a 	= m_adder_vif.a;
        m_pkt.b 	= m_adder_vif.b;
      	m_pkt.rstn 	= m_adder_vif.rstn;
        m_pkt.sum 	= m_adder_vif.sum;
        m_pkt.carry = m_adder_vif.carry;
        m_pkt.print("Monitor");
      scb_mbx.put(m_pkt);
    end
  endtask
endclass

Scoreboard


// The scoreboard is responsible to check data integrity. Since the design
// simple adds inputs to give sum and carry, scoreboard helps to check if the
// output has changed for given set of inputs based on expected logic
class scoreboard;
  mailbox scb_mbx;
  
  task run();
    forever begin
      Packet item, ref_item;
      scb_mbx.get(item);
      item.print("Scoreboard");
      
      // Copy contents from received packet into a new packet so
      // just to get a and b.
      ref_item = new();
      ref_item.copy(item);
      
      // Let us calculate the expected values in carry and sum
      if (ref_item.rstn) 
      	{ref_item.carry, ref_item.sum} = ref_item.a + ref_item.b;
      else
      {ref_item.carry, ref_item.sum} = 0;
      
      // Now, carry and sum outputs in the reference variable can be compared
      // with those in the received packet
      if (ref_item.carry != item.carry) begin
        $display("[%0t] Scoreboard Error! Carry mismatch ref_item=0x%0h item=0x%0h", $time, ref_item.carry, item.carry);
      end else begin
        $display("[%0t] Scoreboard Pass! Carry match ref_item=0x%0h item=0x%0h", $time, ref_item.carry, item.carry);
      end
      
      if (ref_item.sum != item.sum) begin
        $display("[%0t] Scoreboard Error! Sum mismatch ref_item=0x%0h item=0x%0h", $time, ref_item.sum, item.sum);
      end else begin
        $display("[%0t] Scoreboard Pass! Sum match ref_item=0x%0h item=0x%0h", $time, ref_item.sum, item.sum);
      end
    end
  endtask
endclass

Generator


// Sometimes we simply need to generate N random transactions to random
// locations so a generator would be useful to do just that. In this case
// loop determines how many transactions need to be sent
class generator;
  int 	loop = 10;
  event drv_done;
  mailbox drv_mbx;
  
  task run();
    for (int i = 0; i < loop; i++) begin
      Packet item = new;
      item.randomize();
      $display ("T=%0t [Generator] Loop:%0d/%0d create next item", $time, i+1, loop);
      drv_mbx.put(item);
      $display ("T=%0t [Generator] Wait for driver to be done", $time);
      @(drv_done);
    end
  endtask
endclass

Environment


// Lets say that the environment class was already there, and generator is 
// a new component that needs to be included in the ENV. 
class env;
  generator 		g0; 			// Generate transactions
  driver 			d0; 			// Driver to design
  monitor 			m0; 			// Monitor from design
  scoreboard 		s0; 			// Scoreboard connected to monitor
  mailbox 			scb_mbx; 		// Top level mailbox for SCB <-> MON 
  virtual adder_if 	m_adder_vif; 	// Virtual interface handle
  virtual clk_if 	m_clk_vif; 		// TB clk
  
  event drv_done;
  mailbox drv_mbx;
  
  function new();
    d0 = new;
    m0 = new;
    s0 = new;
    scb_mbx = new();
    g0 = new;
    drv_mbx = new;
  endfunction
  
  virtual task run();
    // Connect virtual interface handles
    d0.m_adder_vif = m_adder_vif;
    m0.m_adder_vif = m_adder_vif;
    d0.m_clk_vif = m_clk_vif;
    m0.m_clk_vif = m_clk_vif;
    
    // Connect mailboxes between each component
    d0.drv_mbx = drv_mbx;
    g0.drv_mbx = drv_mbx;
    
    m0.scb_mbx = scb_mbx;
    s0.scb_mbx = scb_mbx;
    
    // Connect event handles
    d0.drv_done = drv_done;
    g0.drv_done = drv_done;
    
    // Start all components - a fork join_any is used because 
    // the stimulus is generated by the generator and we want the
    // simulation to exit only when the generator has finished 
    // creating all transactions. Until then all other components
    // have to run in the background.
    fork
    	s0.run();
		d0.run();
    	m0.run();
      	g0.run();
    join_any
  endtask
endclass

Test


// The test can instantiate any environment. In this test, we are using
// an environment without the generator and hence the stimulus should be 
// written in the test. 
class test;
  env e0;
  mailbox drv_mbx;
  
  function new();
    drv_mbx = new();
    e0 = new();
  endfunction
  
  virtual task run();
    e0.d0.drv_mbx = drv_mbx;
    e0.run();
  endtask
endclass

Interface


// Adder interface contains all signals that the adder requires
// to operate
interface adder_if();
  logic 		rstn;
  logic [7:0] 	a;
  logic [7:0] 	b;
  logic [7:0] 	sum;
  logic 		carry;
endinterface

// Although an adder does not have a clock, let us create a mock clock 
// used in the testbench to synchronize when value is driven and when 
// value is sampled. Typically combinational logic is used between 
// sequential elements like FF in a real circuit. So, let us assume
// that inputs to the adder is provided at some posedge clock. But because
// the design does not have clock in its input, we will keep this clock
// in a separate interface that is available only to testbench components
interface clk_if();
  logic tb_clk;
  
  initial tb_clk <= 0;
  
  always #10 tb_clk = ~tb_clk;
endinterface

Testbench Top


module tb;
  bit tb_clk;
  
  clk_if 	m_clk_if 	();
  adder_if 	m_adder_if	();
  my_adder 	u0 			(m_adder_if);
  
  initial begin
    test t0;

    t0 = new;
    t0.e0.m_adder_vif = m_adder_if;
    t0.e0.m_clk_vif = m_clk_if;
    t0.run();
    
    // Once the main stimulus is over, wait for some time
    // until all transactions are finished and then end 
    // simulation. Note that $finish is required because
    // there are components that are running forever in 
    // the background like clk, monitor, driver, etc
    #50 $finish;
  end
endmodule
 Simulation Log
ncsim> run
T=0 [Driver] starting ...
T=0 [Driver] waiting for item ...
T=0 [Monitor] starting ...
T=0 [Generator] Loop:1/5 create next item
T=0 [Generator] Wait for driver to be done
T=10 Driver a=0x16 b=0x11 sum=0x0 carry=0x0
T=10 [Driver] waiting for item ...
T=10 [Generator] Loop:2/5 create next item
T=10 [Generator] Wait for driver to be done
T=11 Monitor a=0x16 b=0x11 sum=0x0 carry=0x0
T=11 Scoreboard a=0x16 b=0x11 sum=0x0 carry=0x0
[11] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[11] Scoreboard Pass! Sum match ref_item=0x0 item=0x0
T=30 Driver a=0xde b=0x6 sum=0x0 carry=0x0
T=30 [Driver] waiting for item ...
T=30 [Generator] Loop:3/5 create next item
T=30 [Generator] Wait for driver to be done
T=31 Monitor a=0xde b=0x6 sum=0x0 carry=0x0
T=31 Scoreboard a=0xde b=0x6 sum=0x0 carry=0x0
[31] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[31] Scoreboard Pass! Sum match ref_item=0x0 item=0x0
T=50 Driver a=0xb1 b=0xbd sum=0x0 carry=0x0
T=50 [Driver] waiting for item ...
T=50 [Generator] Loop:4/5 create next item
T=50 [Generator] Wait for driver to be done
T=51 Monitor a=0xb1 b=0xbd sum=0x0 carry=0x0
T=51 Scoreboard a=0xb1 b=0xbd sum=0x0 carry=0x0
[51] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[51] Scoreboard Pass! Sum match ref_item=0x0 item=0x0
T=70 Driver a=0x63 b=0xfb sum=0x0 carry=0x0
T=70 [Driver] waiting for item ...
T=70 [Generator] Loop:5/5 create next item
T=70 [Generator] Wait for driver to be done
T=71 Monitor a=0x63 b=0xfb sum=0x5e carry=0x1
T=71 Scoreboard a=0x63 b=0xfb sum=0x5e carry=0x1
[71] Scoreboard Pass! Carry match ref_item=0x1 item=0x1
[71] Scoreboard Pass! Sum match ref_item=0x5e item=0x5e
T=90 Driver a=0x71 b=0xbc sum=0x0 carry=0x0
T=90 [Driver] waiting for item ...
T=91 Monitor a=0x71 b=0xbc sum=0x0 carry=0x0
T=91 Scoreboard a=0x71 b=0xbc sum=0x0 carry=0x0
[91] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[91] Scoreboard Pass! Sum match ref_item=0x0 item=0x0
T=111 Monitor a=0x71 b=0xbc sum=0x0 carry=0x0
T=111 Scoreboard a=0x71 b=0xbc sum=0x0 carry=0x0
[111] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[111] Scoreboard Pass! Sum match ref_item=0x0 item=0x0
T=131 Monitor a=0x71 b=0xbc sum=0x0 carry=0x0
T=131 Scoreboard a=0x71 b=0xbc sum=0x0 carry=0x0
[131] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[131] Scoreboard Pass! Sum match ref_item=0x0 item=0x0
Simulation complete via $finish(1) at time 140 NS + 0
./testbench.sv:265     #50 $finish;

Buggy Design

Although the previous simulation showed everything as pass, how do we know if there is a bug in the checker ? Let us introduce a bug in the design to see if the checker fails.


module my_adder (adder_if _if);
  always_comb begin
  	// Let sum and carry be reset when rstn is 1 instead of 0
  	// A simple but yet possible design bug
    if (_if.rstn) begin
      _if.sum <= 0;
      _if.carry <= 0;
    end else begin
      {_if.carry, _if.sum} <= _if.a + _if.b;
    end
  end
endmodule

See that the checker now reports an error which proves that the checker is implemented correctly.

Testbench example with design bug
 Simulation Log
ncsim> run
T=0 [Driver] starting ...
T=0 [Driver] waiting for item ...
T=0 [Monitor] starting ...
T=0 [Generator] Loop:1/5 create next item
T=0 [Generator] Wait for driver to be done
T=10 Driver a=0x16 b=0x11 sum=0x0 carry=0x0
T=10 [Driver] waiting for item ...
T=10 [Generator] Loop:2/5 create next item
T=10 [Generator] Wait for driver to be done
T=11 Monitor a=0x16 b=0x11 sum=0x27 carry=0x0
T=11 Scoreboard a=0x16 b=0x11 sum=0x27 carry=0x0
[11] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[11] Scoreboard Error! Sum mismatch ref_item=0x0 item=0x27
T=30 Driver a=0xde b=0x6 sum=0x0 carry=0x0
T=30 [Driver] waiting for item ...
T=30 [Generator] Loop:3/5 create next item
T=30 [Generator] Wait for driver to be done
T=31 Monitor a=0xde b=0x6 sum=0xe4 carry=0x0
T=31 Scoreboard a=0xde b=0x6 sum=0xe4 carry=0x0
[31] Scoreboard Pass! Carry match ref_item=0x0 item=0x0
[31] Scoreboard Error! Sum mismatch ref_item=0x0 item=0xe4
T=50 Driver a=0xb1 b=0xbd sum=0x0 carry=0x0
T=50 [Driver] waiting for item ...
T=50 [Generator] Loop:4/5 create next item
T=50 [Generator] Wait for driver to be done
T=51 Monitor a=0xb1 b=0xbd sum=0x6e carry=0x1
T=51 Scoreboard a=0xb1 b=0xbd sum=0x6e carry=0x1
[51] Scoreboard Error! Carry mismatch ref_item=0x0 item=0x1
[51] Scoreboard Error! Sum mismatch ref_item=0x0 item=0x6e
T=70 Driver a=0x63 b=0xfb sum=0x0 carry=0x0
T=70 [Driver] waiting for item ...
T=70 [Generator] Loop:5/5 create next item
T=70 [Generator] Wait for driver to be done
T=71 Monitor a=0x63 b=0xfb sum=0x0 carry=0x0
T=71 Scoreboard a=0x63 b=0xfb sum=0x0 carry=0x0
[71] Scoreboard Error! Carry mismatch ref_item=0x1 item=0x0
[71] Scoreboard Error! Sum mismatch ref_item=0x5e item=0x0
T=90 Driver a=0x71 b=0xbc sum=0x0 carry=0x0
T=90 [Driver] waiting for item ...
T=91 Monitor a=0x71 b=0xbc sum=0x2d carry=0x1
T=91 Scoreboard a=0x71 b=0xbc sum=0x2d carry=0x1
[91] Scoreboard Error! Carry mismatch ref_item=0x0 item=0x1
[91] Scoreboard Error! Sum mismatch ref_item=0x0 item=0x2d
T=111 Monitor a=0x71 b=0xbc sum=0x2d carry=0x1
T=111 Scoreboard a=0x71 b=0xbc sum=0x2d carry=0x1
[111] Scoreboard Error! Carry mismatch ref_item=0x0 item=0x1
[111] Scoreboard Error! Sum mismatch ref_item=0x0 item=0x2d
T=131 Monitor a=0x71 b=0xbc sum=0x2d carry=0x1
T=131 Scoreboard a=0x71 b=0xbc sum=0x2d carry=0x1
[131] Scoreboard Error! Carry mismatch ref_item=0x0 item=0x1
[131] Scoreboard Error! Sum mismatch ref_item=0x0 item=0x2d
Simulation complete via $finish(1) at time 140 NS + 0
./testbench.sv:265     #50 $finish;

UVM TLM Non-blocking Get Port

A previous article showed examples of using a uvm_blocking_get_port TLM port that was blocking in nature where the receiver gets stalled until the sender finishes with the get task.

Similarly, UVM TLM also has a non-blocking method of type uvm_nonblocking_get_port where the sender has to use try_get to see if the get was successful or can_get method to see if the sender is ready to start a transfer. Like before, the UVM TLM non-blocking get port should ultimately be connected to a non-blocking get implementation port.

UVM TLM Nonblocking Get Example

A class called Packet is defined below to act as the data item that will be transferred from one component to another. This class object will have two random variables that can be randomized before sending.


// Create a class data object that can be sent from one 
// component to another
class Packet extends uvm_object;
  rand bit[7:0] addr;
  rand bit[7:0] data;
  
  `uvm_object_utils_begin(Packet)
  	`uvm_field_int(addr, UVM_ALL_ON)
  	`uvm_field_int(data, UVM_ALL_ON)
  `uvm_object_utils_end
  
  function new(string name = "Packet");
    super.new(name);
  endfunction
endclass
1. Create receiver class with a port of type uvm_nonblocking_get_port

A class called componentB is created which has a uvm_nonblocking_get_port parameterized to accept a data object of type Packet. The port has to be instantiated with the new() method preferably in the build_phase of the same component.

In this example, a class object of type Packet is received via the get_port handle by calling the try_get method. Many such packets can be received using a simple loop controlled by a configurable variable. The try_get function should ideally return 1 if the transfer was successful and 0 if it failed and should be provided by the sender which implements the function.


class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   // Create a get_port to request for data from componentA
   uvm_nonblocking_get_port #(Packet) m_get_port;
   int m_num_tx = 2;
 
  function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
     m_get_port = new ("m_get_port", this);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      Packet pkt;
     phase.raise_objection(this);
     
     // Try to get a transaction which does not consume simulation time
     // as try_get() is a function
     repeat (m_num_tx) begin
       if (m_get_port.try_get(pkt))
       	`uvm_info ("COMPB", "ComponentA just gave me the packet", UVM_LOW)
       else
         `uvm_info ("COMPB", "ComponentA did not give packet", UVM_LOW)
        pkt.print (uvm_default_line_printer);
      end
     phase.drop_objection(this);
   endtask
endclass
3. Create sender class that implements the get method

The sender class needs to define an implementation port using uvm_nonblocking_get_imp. Since the port is nonblocking in nature, the try_get implementation is a function which has to be defined by this component.


class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
   uvm_nonblocking_get_imp #(Packet, componentA) m_get_imp;
 
  function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_get_imp = new ("m_get_imp", this);
   endfunction
 
   virtual function bit try_get (output Packet pkt);
      pkt = new();
      assert (pkt.randomize());
      `uvm_info ("COMPA", "ComponentB has requested for a packet", UVM_LOW)
      pkt.print (uvm_default_line_printer);
      return 1;
   endfunction
     
     virtual function bit can_get();
     endfunction
endclass
tlm-get 4. Connect port and its implementation at a higher level

The connection between a port and its implementation has to be done at a higher hierarchical level. Since both components are instantiated directly within the test class in this example, the connection between them can be done during the connect_phase of the test. If these two components were instantiated in another component or environment, they have to be connected during the connect_phase of that component or environment.


class my_test extends uvm_test;
  `uvm_component_utils (my_test)
 
   componentA compA;
   componentB compB;
 
  function new (string name = "my_test", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   // Create objects of both components, set number of transfers
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      compA = componentA::type_id::create ("compA", this);
      compB = componentB::type_id::create ("compB", this);
   endfunction
 
   // Connection between componentA and componentB is done here
   virtual function void connect_phase (uvm_phase phase);
     compB.m_get_port.connect (compA.m_get_imp);  
   endfunction
  
   virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    uvm_top.print_topology();
  endfunction
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
-----------------------------------------------------
Name            Type                      Size  Value
-----------------------------------------------------
uvm_test_top    my_test                   -     @1836
  compA         componentA                -     @1905
    m_get_imp   uvm_nonblocking_get_imp   -     @1971
  compB         componentB                -     @1936
    m_get_port  uvm_nonblocking_get_port  -     @2010
-----------------------------------------------------

UVM_INFO testbench.sv(97) @ 0: uvm_test_top.compA [COMPA] ComponentB has requested for a packet
Packet: (Packet@1903) { addr: 'he8  data: 'hc5  } 
UVM_INFO testbench.sv(67) @ 0: uvm_test_top.compB [COMPB] ComponentA just gave me the packet
Packet: (Packet@1903) { addr: 'he8  data: 'hc5  } 
UVM_INFO testbench.sv(97) @ 0: uvm_test_top.compA [COMPA] ComponentB has requested for a packet
Packet: (Packet@2058) { addr: 'hd6  data: 'hd  } 
UVM_INFO testbench.sv(67) @ 0: uvm_test_top.compB [COMPB] ComponentA just gave me the packet
Packet: (Packet@2058) { addr: 'hd6  data: 'hd  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

UVM TLM can_get Example

Instead of directly trying to get a packet, the receiver can first query to see if the sender is ready or not with can_get function and then get the packet.


class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   // Create a get_port to request for data from componentA
   uvm_nonblocking_get_port #(Packet) m_get_port;
   int m_num_tx = 2;
 
  function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
     m_get_port = new ("m_get_port", this);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      Packet pkt;
     phase.raise_objection(this);
     
     // Try to get a transaction which does not consume simulation time
     // as try_get() is a function
     repeat (m_num_tx) begin
       while (!m_get_port.can_get()) begin
         #10 `uvm_info("COMPB", $sformatf("See if can_get() is ready"), UVM_LOW)
       end
       
       `uvm_info("COMPB", $sformatf("COMPA ready, get packet now"), UVM_LOW)
       m_get_port.try_get(pkt);
       pkt.print (uvm_default_line_printer);
      end
     phase.drop_objection(this);
   endtask
endclass

The can_get function in componentA is set to return a random value in this example to model readiness of the receiver.


class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
   uvm_nonblocking_get_imp #(Packet, componentA) m_get_imp;
 
  // Rest of the code remains same
 
   virtual function bit try_get (output Packet pkt);
      pkt = new();
      assert (pkt.randomize());
      `uvm_info ("COMPA", "ComponentB has requested for a packet", UVM_LOW)
      pkt.print (uvm_default_line_printer);
      return 1;
   endfunction
     
     virtual function bit can_get();
       bit ready;
       std::randomize(ready) with { ready dist {0:/70, 1:/30}; };
       return ready;
     endfunction
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
-----------------------------------------------------
Name            Type                      Size  Value
-----------------------------------------------------
uvm_test_top    my_test                   -     @1837
  compA         componentA                -     @1906
    m_get_imp   uvm_nonblocking_get_imp   -     @1972
  compB         componentB                -     @1937
    m_get_port  uvm_nonblocking_get_port  -     @2011
-----------------------------------------------------

UVM_INFO testbench.sv(60) @ 10: uvm_test_top.compB [COMPB] See if can_get() is ready
UVM_INFO testbench.sv(60) @ 20: uvm_test_top.compB [COMPB] See if can_get() is ready
UVM_INFO testbench.sv(60) @ 30: uvm_test_top.compB [COMPB] See if can_get() is ready
UVM_INFO testbench.sv(62) @ 30: uvm_test_top.compB [COMPB] COMPA ready, get packet now
UVM_INFO testbench.sv(97) @ 30: uvm_test_top.compA [COMPA] ComponentB has requested for a packet
Packet: (Packet@2065) { addr: 'h8c  data: 'h99  } 
Packet: (Packet@2065) { addr: 'h8c  data: 'h99  } 
UVM_INFO testbench.sv(62) @ 30: uvm_test_top.compB [COMPB] COMPA ready, get packet now
UVM_INFO testbench.sv(97) @ 30: uvm_test_top.compA [COMPA] ComponentB has requested for a packet
Packet: (Packet@2095) { addr: 'h97  data: 'hb8  } 
Packet: (Packet@2095) { addr: 'h97  data: 'hb8  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 30: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 30: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

UVM TLM Port to Export to Imp

UVM TLM ports and exports are also used to send transaction objects cross different levels of testbench hierarchy.

Ports shall be used to initiate and forward packets to the top layer of the hierarchy. Exports shall be used to accept and forward packets from the top layer to destination. Implementation ports shall be used to define the put method at the target. Shown below are a few examples that use ports, exports and implementation for components at different hierarchy levels.

Port to Port to Export to Imp

subCompA is a subcomponent within componentA that is trying to send transactions to another subcomponent called subCompB in componentB. To maintain flexibility and portability of code, it is recommended to allow subCompA to send data to componentA which should then forward them to the top layer of the hierarchy. componentB shall accept the transaction and forward it to subCompB.

uvm-tlm-put-port-port-export-imp

A class called Packet is defined below to act as the data item that will be transferred from one component to another. This class object will have two random variables that can be randomized before sending.


// Create a class data object that can be sent from one 
// component to another
class Packet extends uvm_object;
  rand bit[7:0] addr;
  rand bit[7:0] data;
  
  `uvm_object_utils_begin(Packet)
  	`uvm_field_int(addr, UVM_ALL_ON)
  	`uvm_field_int(data, UVM_ALL_ON)
  `uvm_object_utils_end
  
  function new(string name = "Packet");
    super.new(name);
  endfunction
endclass

Subcomponent A


class subCompA extends uvm_component;
  `uvm_component_utils (subCompA)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  uvm_blocking_put_port #(Packet) m_put_port;
  int m_num_tx=2;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_put_port = new ("m_put_port", this);
  endfunction
  
  // Create a packet, randomize it and send it through the port
  // Note that put() is a method defined by the receiving component
  // Repeat these steps N times to send N packets
   virtual task run_phase (uvm_phase phase);
     phase.raise_objection(this);
     repeat (m_num_tx) begin
         Packet pkt = Packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
       
       	 // Print the packet to be displayed in log
       `uvm_info ("SUBCOMPA", "Packet sent to subCompB", UVM_LOW)
         pkt.print (uvm_default_line_printer);
       
         // Call the TLM put() method of put_port class and pass packet as argument
         m_put_port.put (pkt);
      end
      phase.drop_objection(this);
   endtask
endclass

Component A


class componentA extends uvm_component;
   `uvm_component_utils (componentA)
   function new (string name = "componentA", uvm_component parent= null);
      super.new (name, parent);
   endfunction

    subCompA m_subcomp_A;
    uvm_blocking_put_port #(Packet) m_put_port;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_subcomp_A = subCompA::type_id::create("m_subcomp_A", this);
    m_put_port = new ("m_put_port", this);
  endfunction
  
  // Connection with subCompA
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    m_subcomp_A.m_put_port.connect(this.m_put_port);
  endfunction
endclass

Component B


class componentB extends uvm_component;
   `uvm_component_utils (componentB)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
 
	subCompB m_subcomp_B;
   uvm_blocking_put_export#(Packet) m_put_export;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_subcomp_B = subCompB::type_id::create("m_subcomp_B", this);
    m_put_export = new("m_put_export", this);
  endfunction
  
  // Connection with subCompB
  virtual function void connect_phase(uvm_phase phase);
    m_put_export.connect(m_subcomp_B.m_put_imp);
  endfunction
endclass

Subcomponent B


class subCompB extends uvm_component;
  `uvm_component_utils (subCompB)
  function new (string name = "subCompB", uvm_component parent = null);
      super.new (name, parent);
   endfunction

   // Mention type of transaction, and type of class that implements the put ()
  uvm_blocking_put_imp #(Packet, subCompB) m_put_imp;
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
     m_put_imp = new ("m_put_imp", this);
   endfunction
 
    // Implementation of the 'put()' method in this case simply prints it.
  	virtual task put (Packet pkt);            
      `uvm_info ("SUBCOMPB", "Packet received from subCompA", UVM_LOW)
      pkt.print(uvm_default_line_printer);
   endtask
endclass

class my_test extends uvm_test;
  `uvm_component_utils (my_test)
 
   componentA compA;
   componentB compB;
 
  function new (string name = "my_test", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   // Create objects of both components, set number of transfers
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      compA = componentA::type_id::create ("compA", this);
      compB = componentB::type_id::create ("compB", this);
   endfunction
 
   // Connection between componentA and componentB is done here
   virtual function void connect_phase (uvm_phase phase);
     compA.m_put_port.connect (compB.m_put_export);  
   endfunction
  
   virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    uvm_top.print_topology();
  endfunction
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
------------------------------------------------------
Name              Type                     Size  Value
------------------------------------------------------
uvm_test_top      my_test                  -     @1842
  compA           componentA               -     @1911
    m_put_port    uvm_blocking_put_port    -     @2008
    m_subcomp_A   subCompA                 -     @1974
      m_put_port  uvm_blocking_put_port    -     @2047
  compB           componentB               -     @1942
    m_put_export  uvm_blocking_put_export  -     @2115
    m_subcomp_B   subCompB                 -     @2081
      m_put_imp   uvm_blocking_put_imp     -     @2152
------------------------------------------------------

UVM_INFO testbench.sv(71) @ 0: uvm_test_top.compA.m_subcomp_A [SUBCOMPA] Packet sent to subCompB
pkt: (Packet@2191) { addr: 'h24  data: 'h31  } 
UVM_INFO testbench.sv(151) @ 0: uvm_test_top.compB.m_subcomp_B [SUBCOMPB] Packet received from subCompA
pkt: (Packet@2191) { addr: 'h24  data: 'h31  } 
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.compA.m_subcomp_A [SUBCOMPA] Packet sent to subCompB
pkt: (Packet@2209) { addr: 'h9a  data: 'hfb  } 
UVM_INFO testbench.sv(151) @ 0: uvm_test_top.compB.m_subcomp_B [SUBCOMPB] Packet received from subCompA
pkt: (Packet@2209) { addr: 'h9a  data: 'hfb  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

Port to Port to Imp

In this example componentA forwards the packet from subCompA to the destination componentB. The only difference is that componentB is the target and hence should define the implementation port instead of an export.

uvm-tlm-put-port-port-imp

// componentB shall implement "put" and there's no subCompB
class componentB extends uvm_component;
   `uvm_component_utils (componentB)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
 
  uvm_blocking_put_imp#(Packet, componentB) m_put_imp;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_put_imp = new("m_put_imp", this);
  endfunction
  
      // Implementation of the 'put()' method in this case simply prints it.
    virtual task put (Packet pkt);            
      `uvm_info ("COMPB", "Packet received from subCompA", UVM_LOW)
      pkt.print(uvm_default_line_printer);
   endtask
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
----------------------------------------------------
Name              Type                   Size  Value
----------------------------------------------------
uvm_test_top      my_test                -     @1839
  compA           componentA             -     @1908
    m_put_port    uvm_blocking_put_port  -     @2005
    m_subcomp_A   subCompA               -     @1971
      m_put_port  uvm_blocking_put_port  -     @2044
  compB           componentB             -     @1939
    m_put_imp     uvm_blocking_put_imp   -     @2081
----------------------------------------------------

UVM_INFO testbench.sv(70) @ 0: uvm_test_top.compA.m_subcomp_A [SUBCOMPA] Packet sent to CompB
pkt: (Packet@2119) { addr: 'h24  data: 'h31  } 
UVM_INFO testbench.sv(124) @ 0: uvm_test_top.compB [COMPB] Packet received from subCompA
pkt: (Packet@2119) { addr: 'h24  data: 'h31  } 
UVM_INFO testbench.sv(70) @ 0: uvm_test_top.compA.m_subcomp_A [SUBCOMPA] Packet sent to CompB
pkt: (Packet@2133) { addr: 'h9a  data: 'hfb  } 
UVM_INFO testbench.sv(124) @ 0: uvm_test_top.compB [COMPB] Packet received from subCompA
pkt: (Packet@2133) { addr: 'h9a  data: 'hfb  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

Port to Export to Imp

In this example componentA is the initiator and sends a packet from its port to the destination subCompB which implements the put method. Since componentB is the container for the target, it should have an export to forward the packets received from the connected port at the top level.

uvm-tlm-put-port-export-imp

// componentA will start transactions and send to its port
class componentA extends uvm_component;
   `uvm_component_utils (componentA)
   function new (string name = "componentA", uvm_component parent= null);
      super.new (name, parent);
   endfunction

    uvm_blocking_put_port #(Packet) m_put_port;
    int m_num_tx=2;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_put_port = new ("m_put_port", this);
  endfunction
  
  
  // Create a packet, randomize it and send it through the port
  // Note that put() is a method defined by the receiving component
  // Repeat these steps N times to send N packets
   virtual task run_phase (uvm_phase phase);
     phase.raise_objection(this);
     repeat (m_num_tx) begin
         Packet pkt = Packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
       
       	 // Print the packet to be displayed in log
       `uvm_info ("SUBCOMPA", "Packet sent to subCompB", UVM_LOW)
         pkt.print (uvm_default_line_printer);
       
         // Call the TLM put() method of put_port class and pass packet as argument
         m_put_port.put (pkt);
      end
      phase.drop_objection(this);
   endtask
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
------------------------------------------------------
Name              Type                     Size  Value
------------------------------------------------------
uvm_test_top      my_test                  -     @1839
  compA           componentA               -     @1908
    m_put_port    uvm_blocking_put_port    -     @1974
  compB           componentB               -     @1939
    m_put_export  uvm_blocking_put_export  -     @2044
    m_subcomp_B   subCompB                 -     @2010
      m_put_imp   uvm_blocking_put_imp     -     @2081
------------------------------------------------------

UVM_INFO testbench.sv(73) @ 0: uvm_test_top.compA [SUBCOMPA] Packet sent to subCompB
pkt: (Packet@2120) { addr: 'ha1  data: 'h64  } 
UVM_INFO testbench.sv(127) @ 0: uvm_test_top.compB.m_subcomp_B [SUBCOMPB] Packet received from subCompA
pkt: (Packet@2120) { addr: 'ha1  data: 'h64  } 
UVM_INFO testbench.sv(73) @ 0: uvm_test_top.compA [SUBCOMPA] Packet sent to subCompB
pkt: (Packet@2134) { addr: 'hc1  data: 'hb9  } 
UVM_INFO testbench.sv(127) @ 0: uvm_test_top.compB.m_subcomp_B [SUBCOMPB] Packet received from subCompA
pkt: (Packet@2134) { addr: 'hc1  data: 'hb9  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

Port to Port to Export to Export to Imp

In this example there is an additional layer between componentB and the target subCompB2 called subCompB1. This layer has to simply forward the packet to the destination and hence shall have an export.

uvm-tlm-put-port-port-export-export-imp
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------------------------
Name                Type                     Size  Value
--------------------------------------------------------
uvm_test_top        my_test                  -     @1845
  compA             componentA               -     @1914
    m_put_port      uvm_blocking_put_port    -     @2011
    m_subcomp_A     subCompA                 -     @1977
      m_put_port    uvm_blocking_put_port    -     @2050
  compB             componentB               -     @1945
    m_put_export    uvm_blocking_put_export  -     @2118
    m_subcomp_B1    subCompB1                -     @2084
      m_put_export  uvm_blocking_put_export  -     @2186
      m_subcomp_B2  subCompB2                -     @2152
        m_put_imp   uvm_blocking_put_imp     -     @2223
--------------------------------------------------------

UVM_INFO testbench.sv(75) @ 0: uvm_test_top.compA.m_subcomp_A [SUBCOMPA] Packet sent to subCompB
pkt: (Packet@2262) { addr: 'h24  data: 'h31  } 
UVM_INFO testbench.sv(179) @ 0: uvm_test_top.compB.m_subcomp_B1.m_subcomp_B2 [SUBCOMPB2] Packet received from subCompA
pkt: (Packet@2262) { addr: 'h24  data: 'h31  } 
UVM_INFO testbench.sv(75) @ 0: uvm_test_top.compA.m_subcomp_A [SUBCOMPA] Packet sent to subCompB
pkt: (Packet@2284) { addr: 'h9a  data: 'hfb  } 
UVM_INFO testbench.sv(179) @ 0: uvm_test_top.compB.m_subcomp_B1.m_subcomp_B2 [SUBCOMPB2] Packet received from subCompA
pkt: (Packet@2284) { addr: 'h9a  data: 'hfb  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

SystemVerilog Testbench Example 2

This is another example of a SystemVerilog testbench using OOP concepts like inheritance, polymorphism to build a functional testbench for a simple design.

Design


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

Read more: SystemVerilog Testbench Example 2

  1. SystemVerilog Testbench Example 1
  2. Verilog Delay Control
  3. Switch Level Modeling
  4. Verilog Gate Delay
  5. Verilog Pattern Detector

Page 19 of 63

  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
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
  • Verilog Testbench
  • Verilog Coding Style Effect
  • Verilog Conditional Statements
  • Verilog Interview Set 10
  • Synchronous FIFO
  • SystemVerilog Interview Set 10
  • SystemVerilog Interview Set 9
  • SystemVerilog Interview Set 8
  • SystemVerilog Interview Set 7
  • SystemVerilog Interview Set 6
  • UVM Singleton Object
  • UVM Component [uvm_component]
  • UVM Object [uvm_object]
  • UVM Root [uvm_root]
  • UVM Interview Set 4
© 2015 - 2023 ChipVerify
Terms and Conditions | DMCA.com Protection Status