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

Guide - Using Verification Components

UVM accelerates the development process and facilitates re-use. Inorder to create a testbench from UVCs, you'll need to

  • Review the configuration parameters of each UVC
  • Instantiate and configure UVCs
  • Create re-usable sequences for interface components
  • Add a virtual sequencer
  • Add checking and functional coverage extensions
  • Create tests to cover all functionalities

Read more: Guide - Using Verification Components

SystemVerilog Concurrent Assertions

Concurrent assertions describe behavior that spans over simulation time and are evaluated only at the occurence of a clock tick.

SystemVerilog concurrent assertion statements can be specified in a module, interface or program block running concurrently with other statements. Following are the properties of a concurrent assertion:

  • Test expression is evaluated at clock edges based on values in sampled variables
  • Sampling of variables is done in the preponed region and evaluation of the expression is done in the observed region of the simulation scheduler.
  • It can be placed in a procedural, module, interface, or program block
  • It can be used in both dynamic and formal verification techniques

Example #1

Two signals a and b are declared and driven at positive edges of a clock with some random value to illustrate how a concurrent assertion works. The assertion is written by the assert statement on an immediate property which defines a relation between the signals at a clocking event.

In this example, both signals a and b are expected to be high at the positive edge of clock for the entire simulation. The assertion is expected to fail for all instances where either a or b is found to be zero.


  module tb;
      bit a, b;
      bit clk;

      always #10 clk = ~clk;

      initial begin
          for (int i = 0; i < 10; i++) begin
              @(posedge clk);
              a <= $random;
              b <= $random;
              $display("[%0t] a=%0b b=%0b", $time, a, b);
          end
          #10 $finish;
      end
      
    // This assertion runs for entire duration of simulation
    // Ensure that both signals are high at posedge clk
    assert property (@(posedge clk) a & b);   

  endmodule

The assertion is executed on every positive edge of clk and evaluates the expression using values of variables in the preponed region, which is a delta cycle before given edge of clock. So, if a changes from 0 to 1 on the same edge as clock goes from 0 to 1, the value of a taken for assertion will be zero because it was zero just before the clock edge.

preponed-region systemverilog-concurrent-assertion

It can be seen that assertion fails for all cases where either a or b is found zero because the expression given within the assert statement is expected to be true for the entire duration of simulation.

Time (ns)abResult
1000FAIL
3001FAIL
5011PASS
7011PASS
9010FAIL
11011PASS
13001FAIL
15010FAIL
17010FAIL
19010FAIL
 Simulation Log
Compiler version P-2019.06-1; Runtime version P-2019.06-1;  Dec 11 14:46 2019
[10] a=0 b=0
testbench.sv", 24: tb.unnamed$$_4: started at 10ns failed at 10ns
	Offending '(a & b)'
[30] a=0 b=1
"testbench.sv", 24: tb.unnamed$$_4: started at 30ns failed at 30ns
	Offending '(a & b)'
[50] a=1 b=1
[70] a=1 b=1
[90] a=1 b=0
"testbench.sv", 24: tb.unnamed$$_4: started at 90ns failed at 90ns
	Offending '(a & b)'
[110] a=1 b=1
[130] a=0 b=1
"testbench.sv", 24: tb.unnamed$$_4: started at 130ns failed at 130ns
	Offending '(a & b)'
[150] a=1 b=0
"testbench.sv", 24: tb.unnamed$$_4: started at 150ns failed at 150ns
	Offending '(a & b)'
[170] a=1 b=0
"testbench.sv", 24: tb.unnamed$$_4: started at 170ns failed at 170ns
	Offending '(a & b)'
[190] a=1 b=0
"testbench.sv", 24: tb.unnamed$$_4: started at 190ns failed at 190ns
	Offending '(a & b)'
$finish called from file "testbench.sv", line 14.
$finish at simulation time                  200

Example #2

The expression defined as a property for the assert statement is modified from the above example to an OR condition.


  module tb;
      bit a, b;
      bit clk;

      always #10 clk = ~clk;

      initial begin
          for (int i = 0; i < 10; i++) begin
              @(posedge clk);
              a <= $random;
              b <= $random;
              $display("[%0t] a=%0b b=%0b", $time, a, b);
          end
          #10 $finish;
      end
      
    // This assertion runs for entire duration of simulation
    // Ensure that atleast 1 of the two signals is high on every clk
    assert property (@(posedge clk) a | b);   

  endmodule
concurrent-assertion-a-or-b
Time (ns)abResult
1000FAIL
3001PASS
5011PASS
7011PASS
9010PASS
11011PASS
13001PASS
15010PASS
17010PASS
19010PASS
 Simulation Log
Compiler version P-2019.06-1; Runtime version P-2019.06-1;  Dec 11 15:13 2019
[10] a=0 b=0
testbench.sv", 24: tb.unnamed$$_4: started at 10ns failed at 10ns
	Offending '(a | b)'
[30] a=0 b=1
[50] a=1 b=1
[70] a=1 b=1
[90] a=1 b=0
[110] a=1 b=1
[130] a=0 b=1
[150] a=1 b=0
[170] a=1 b=0
[190] a=1 b=0
$finish called from file "testbench.sv", line 14.

Example #3

The expression defined as a property for the assert statement is modified from the above example to an XNOR condition after negation of a .


  module tb;
      bit a, b;
      bit clk;

      always #10 clk = ~clk;

      initial begin
          for (int i = 0; i < 10; i++) begin
              @(posedge clk);
              a <= $random;
              b <= $random;
              $display("[%0t] a=%0b b=%0b", $time, a, b);
          end
          #10 $finish;
      end
      
    // This assertion runs for entire duration of simulation
    // Ensure that atleast 1 of the two signals is high on every clk
    assert property (@(posedge clk) !(!a ^ b));   

  endmodule
concurrent-assertion-not-a-xnor-b
Time (ns)abExpression !( !a ^ b )Result
10000FAIL
30011PASS
50110FAIL
70110FAIL
90101PASS
110110FAIL
130011PASS
150101PASS
170101PASS
190101PASS
 Simulation Log
Compiler version P-2019.06-1; Runtime version P-2019.06-1;  Dec 11 15:26 2019
[10] a=0 b=0
"testbench.sv", 24: tb.unnamed$$_4: started at 10ns failed at 10ns
	Offending '(!((!a) ^ b))'
[30] a=0 b=1
[50] a=1 b=1
"testbench.sv", 24: tb.unnamed$$_4: started at 50ns failed at 50ns
	Offending '(!((!a) ^ b))'
[70] a=1 b=1
"testbench.sv", 24: tb.unnamed$$_4: started at 70ns failed at 70ns
	Offending '(!((!a) ^ b))'
[90] a=1 b=0
[110] a=1 b=1
"testbench.sv", 24: tb.unnamed$$_4: started at 110ns failed at 110ns
	Offending '(!((!a) ^ b))'
[130] a=0 b=1
[150] a=1 b=0
[170] a=1 b=0
[190] a=1 b=0
$finish called from file "testbench.sv", line 14.
$finish at simulation time                  200

UVM Register Abstraction Layer

  1. What are registers ?

What are registers ?

Registers are essential components in hardware designs, used to store configuration settings, control information, and status data. In modern designs, these registers are often accessed by software or firmware using bus protocols like AXI/APB and can be programmed with certain values. For example, there could be a 32-bit register with several individual fields within it. Each field represents a particular feature that can be configured by software when required.

hardware-register-example

Read more: UVM Register Abstraction Layer

UVM Testbench Example 1

A SystemVerilog based testbench was explored before to verify a simple register/memory element design that stores write data and gives back read data from requested addresses. Let us build a similar testbench using UVM components so that you can compare it with a traditional SystemVerilog testbench.

uvm testbench example

Sequence Item

A data class called reg_item is defined to hold random input address and write data and will be randomized inside the sequence before it is sent to the driver. The class also has utility macros to help with common requirements like print, copy and clone.


class reg_item extends uvm_sequence_item;
  rand bit [`ADDR_WIDTH-1:0]  	addr;
  rand bit [`DATA_WIDTH-1:0] 	wdata;
  rand bit 						wr;
  bit [`DATA_WIDTH-1:0] 		rdata;  

  // Use utility macros to implement standard functions
  // like print, copy, clone, etc
  `uvm_object_utils_begin(reg_item)
  	`uvm_field_int (addr, UVM_DEFAULT)
  	`uvm_field_int (wdata, UVM_DEFAULT)
  	`uvm_field_int (rdata, UVM_DEFAULT)
  	`uvm_field_int (wr, UVM_DEFAULT)
  `uvm_object_utils_end
  
  virtual function string convert2str();
    return $sformatf("addr=0x%0h wr=0x%0h wdata=0x%0h rdata=0x%0h", addr, wr, wdata, rdata);
  endfunction
  
  function new(string name = "reg_item");
    super.new(name);
  endfunction
endclass

UVM Driver

The UVM driver extends from uvm_driver and is parameterized to accept an object of type reg_item . It also recieves handle to a virtual interface that is used to toggle pins of the DUT. The driver uses standard method calls get_next_item and item_done to communicate with its sequencer.


class driver extends uvm_driver #(reg_item);              
  `uvm_component_utils(driver)
  function new(string name = "driver", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  virtual reg_if vif;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (!uvm_config_db#(virtual reg_if)::get(this, "", "reg_vif", vif))
      `uvm_fatal("DRV", "Could not get vif")
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    forever begin
      reg_item m_item;
      `uvm_info("DRV", $sformatf("Wait for item from sequencer"), UVM_LOW)
      seq_item_port.get_next_item(m_item);
      drive_item(m_item);
      seq_item_port.item_done();
    end
  endtask
  
  virtual task drive_item(reg_item m_item);
      vif.sel <= 1;
      vif.addr 	<= m_item.addr;
      vif.wr 	<= m_item.wr;
      vif.wdata <= m_item.wdata;
      @ (posedge vif.clk);
      while (!vif.ready)  begin
        `uvm_info("DRV", "Wait until ready is high", UVM_LOW)
        @(posedge vif.clk);
      end
      
      vif.sel <= 0;  
  endtask
endclass

UVM Monitor

The UVM monitor is derived from uvm_monitor and has a virtual interface handle to listen to activity on the given interface. It tries to decode pin level activity into a protocol packet which it can send out to other testbench components. In this case, it tries to see if address and data pairs are available on the DUT interface, capture them into a class object of type reg_item and send out via an analysis port.

Since it has to decode and capture any activity happening on the DUT interface, it has to run for as long as the simulation is active and hence it is placed inside a forever loop.


class monitor extends uvm_monitor;
  `uvm_component_utils(monitor)
  function new(string name="monitor", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  uvm_analysis_port  #(reg_item) mon_analysis_port;
  virtual reg_if vif;
  semaphore sema4;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (!uvm_config_db#(virtual reg_if)::get(this, "", "reg_vif", vif))
      `uvm_fatal("MON", "Could not get vif")
    sema4 = new(1);
    mon_analysis_port = new ("mon_analysis_port", this);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    // This task monitors the interface for a complete 
    // transaction and writes into analysis port when complete
    forever begin
      @ (posedge vif.clk);
      if (vif.sel) begin
        reg_item item = new;
        item.addr = vif.addr;
        item.wr = vif.wr;
        item.wdata = vif.wdata;

        if (!vif.wr) begin
          @(posedge vif.clk);
        	item.rdata = vif.rdata;
        end
        `uvm_info(get_type_name(), $sformatf("Monitor found packet %s", item.convert2str()), UVM_LOW)
        mon_analysis_port.write(item);
      end
    end
  endtask
endclass

UVM Agent

The role of a UVM agent is to encapsulate the sequencer, driver and monitor into a single container. The sequencer is responsible for acting on a given sequence and send the driver with data objects. It is parameterized to accept objects of type reg_item and its seq_item_port is connected to the driver's seq_item_export.


class agent extends uvm_agent;
  `uvm_component_utils(agent)
  function new(string name="agent", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  driver 		d0; 		// Driver handle
  monitor 		m0; 		// Monitor handle
  uvm_sequencer #(reg_item)	s0; 		// Sequencer Handle

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    s0 = uvm_sequencer#(reg_item)::type_id::create("s0", this);
    d0 = driver::type_id::create("d0", this);
    m0 = monitor::type_id::create("m0", this);
  endfunction
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    d0.seq_item_port.connect(s0.seq_item_export);
  endfunction
endclass

UVM Scoreboard

The scoreboard receives a data object through its uvm_analysis_imp port from the monitor. The scoreboard receives data packets from the monitor for both write and read operations and hence it can predict what the read data should be for a given address. As soon as the scoreboard receives an item, its write method will be executed which in turn runs the checker and predicts the result.

This scoreboard has an internal array which is used to store data for different addresses and hence mimics design behavior. This is also called as a reference model. Reference models may be as simple as this or very complicated based on design functionality.


class scoreboard extends uvm_scoreboard;
  `uvm_component_utils(scoreboard)
  function new(string name="scoreboard", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  reg_item refq[`DEPTH];
  uvm_analysis_imp #(reg_item, scoreboard) m_analysis_imp;
    
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_analysis_imp = new("m_analysis_imp", this);
  endfunction
  
  virtual function write(reg_item item);   
     if (item.wr) begin
        if (refq[item.addr] == null)
          refq[item.addr] = new;
        
        refq[item.addr] = item;
       `uvm_info(get_type_name(), $sformatf("Store addr=0x%0h wr=0x%0h data=0x%0h", item.addr, item.wr, item.wdata), UVM_LOW)
      end
      
        if (!item.wr) begin
          if (refq[item.addr] == null)
            if (item.rdata != 'h1234)
              `uvm_error (get_type_name(), $sformatf("First time read, addr=0x%0h exp=1234 act=0x%0h",
                                                     item.addr, item.rdata))
          	else
              `uvm_info(get_type_name(), $sformatf("PASS! First time read, addr=0x%0h exp=1234 act=0x%0h",
                                                   item.addr, item.rdata), UVM_LOW)
          else
            if (item.rdata != refq[item.addr].wdata)
              `uvm_error (get_type_name(), $sformatf("addr=0x%0h exp=0x%0h act=0x%0h",
                                                    item.addr, refq[item.addr].wdata, item.rdata))
           else
             `uvm_info(get_type_name(), $sformatf("PASS! addr=0x%0h exp=0x%0h act=0x%0h", 
                       item.addr, refq[item.addr].wdata, item.rdata), UVM_LOW)
        end
  endfunction
endclass

UVM Environment

The UVM environment is responsible for instantiating and connecting all testbench components. Here, analysis port from the agent's monitor is connected to the analysis implementation port in scoreboard. This connection allows the scoreboard to receive packets whenever the monitor pushes new data into the analysis port.


class env extends uvm_env;
  `uvm_component_utils(env)
  function new(string name="env", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  agent 		a0; 		// Agent handle
  scoreboard	sb0; 		// Scoreboard handle
    
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    a0 = agent::type_id::create("a0", this);
    sb0 = scoreboard::type_id::create("sb0", this);
  endfunction
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    a0.m0.mon_analysis_port.connect(sb0.m_analysis_imp);
  endfunction
endclass

UVM Test

The test instantiates an environment, sets up virtual interface handles to sub components and starts a top level sequence. In this simple example, we have chosen to also reset the DUT using a reset task, after which a sequence of type gen_item_seq is started on the agent's sequencer.

Since the sequence consumes simulation time and all other testbench components have to run as long as the sequence is active, it is important to raise and drop objections appropriately. Note that the sequence has a random variable to represent the number of packets sent to the driver and can be constrained appropriately to test more combinations of address and data.


class test extends uvm_test;
  `uvm_component_utils(test)
  function new(string name = "test", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  env e0;
  virtual reg_if vif;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    e0 = env::type_id::create("e0", this);
    if (!uvm_config_db#(virtual reg_if)::get(this, "", "reg_vif", vif))
      `uvm_fatal("TEST", "Did not get vif")
      
      uvm_config_db#(virtual reg_if)::set(this, "e0.a0.*", "reg_vif", vif);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    gen_item_seq seq = gen_item_seq::type_id::create("seq");
    phase.raise_objection(this);
    apply_reset();
    
    seq.randomize() with {num inside {[20:30]}; };
    seq.start(e0.a0.s0);
    #200;
    phase.drop_objection(this);
  endtask
  
  virtual task apply_reset();
    vif.rstn <= 0;
    repeat(5) @ (posedge vif.clk);
    vif.rstn <= 1;
    repeat(10) @ (posedge vif.clk);
  endtask
endclass

UVM Sequence

Last but not the least, we have the main sequence that forms the stimulus and randomizer aspect of the testbench. The sequence when started on a sequencer gets the body method executed which ultimately randomizes address/data and send them to the driver.


class gen_item_seq extends uvm_sequence;
  `uvm_object_utils(gen_item_seq)
  function new(string name="gen_item_seq");
    super.new(name);
  endfunction
  
  rand int num; 	// Config total number of items to be sent
  
  constraint c1 { soft num inside {[2:5]}; }
  
  virtual task body();
    for (int i = 0; i < num; i ++) begin
    	reg_item m_item = reg_item::type_id::create("m_item");
    	start_item(m_item);
    	m_item.randomize();
    	`uvm_info("SEQ", $sformatf("Generate new item: "), UVM_LOW)
    	m_item.print();
      	finish_item(m_item);
    end
    `uvm_info("SEQ", $sformatf("Done generation of %0d items", num), UVM_LOW)
  endtask
endclass
uvm testbench example 2
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test test...
UVM_INFO testbench.sv(80) @ 0: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 290: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xa4 wr=0x0 wdata=0x9783 rdata=0x0
UVM_INFO testbench.sv(80) @ 310: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 310: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x1b wr=0x1 wdata=0x3025 rdata=0x0
UVM_INFO testbench.sv(141) @ 330: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xa4 wr=0x0 wdata=0x9783 rdata=0x1234
UVM_INFO testbench.sv(182) @ 330: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0xa4 exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 330: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(141) @ 350: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x1b wr=0x1 wdata=0x3025 rdata=0x0
UVM_INFO testbench.sv(172) @ 350: uvm_test_top.e0.sb0 [scoreboard] Store addr=0x1b wr=0x1 data=0x3025
UVM_INFO testbench.sv(80) @ 350: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 350: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x9 wr=0x0 wdata=0x6169 rdata=0x0
UVM_INFO testbench.sv(80) @ 370: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 370: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x6e wr=0x0 wdata=0x2b4f rdata=0x0
UVM_INFO testbench.sv(141) @ 390: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x9 wr=0x0 wdata=0x6169 rdata=0x1234
UVM_INFO testbench.sv(182) @ 390: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0x9 exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 390: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(80) @ 410: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 410: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x4a wr=0x1 wdata=0x8dd7 rdata=0x0
UVM_INFO testbench.sv(141) @ 430: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x6e wr=0x0 wdata=0x2b4f rdata=0x1234
UVM_INFO testbench.sv(182) @ 430: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0x6e exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 430: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(141) @ 450: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x4a wr=0x1 wdata=0x8dd7 rdata=0x0
UVM_INFO testbench.sv(172) @ 450: uvm_test_top.e0.sb0 [scoreboard] Store addr=0x4a wr=0x1 data=0x8dd7
UVM_INFO testbench.sv(80) @ 450: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 450: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x9d wr=0x0 wdata=0x8901 rdata=0x0
UVM_INFO testbench.sv(80) @ 470: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 470: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x66 wr=0x1 wdata=0x1cce rdata=0x0
UVM_INFO testbench.sv(141) @ 490: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x9d wr=0x0 wdata=0x8901 rdata=0x1234
UVM_INFO testbench.sv(182) @ 490: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0x9d exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 490: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(141) @ 510: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x66 wr=0x1 wdata=0x1cce rdata=0x0
UVM_INFO testbench.sv(172) @ 510: uvm_test_top.e0.sb0 [scoreboard] Store addr=0x66 wr=0x1 data=0x1cce
UVM_INFO testbench.sv(80) @ 510: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 510: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xa7 wr=0x1 wdata=0x493c rdata=0x0
UVM_INFO testbench.sv(141) @ 530: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xa7 wr=0x1 wdata=0x493c rdata=0x0
UVM_INFO testbench.sv(172) @ 530: uvm_test_top.e0.sb0 [scoreboard] Store addr=0xa7 wr=0x1 data=0x493c
UVM_INFO testbench.sv(80) @ 530: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 530: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x5e wr=0x1 wdata=0xe4c rdata=0x0
UVM_INFO testbench.sv(141) @ 550: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x5e wr=0x1 wdata=0xe4c rdata=0x0
UVM_INFO testbench.sv(172) @ 550: uvm_test_top.e0.sb0 [scoreboard] Store addr=0x5e wr=0x1 data=0xe4c
UVM_INFO testbench.sv(80) @ 550: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 550: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x8d wr=0x1 wdata=0x6bfe rdata=0x0
UVM_INFO testbench.sv(141) @ 570: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x8d wr=0x1 wdata=0x6bfe rdata=0x0
UVM_INFO testbench.sv(172) @ 570: uvm_test_top.e0.sb0 [scoreboard] Store addr=0x8d wr=0x1 data=0x6bfe
UVM_INFO testbench.sv(80) @ 570: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 570: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x32 wr=0x0 wdata=0x6252 rdata=0x0
UVM_INFO testbench.sv(80) @ 590: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 590: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x4f wr=0x0 wdata=0xf148 rdata=0x0
UVM_INFO testbench.sv(141) @ 610: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x32 wr=0x0 wdata=0x6252 rdata=0x1234
UVM_INFO testbench.sv(182) @ 610: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0x32 exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 610: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(80) @ 630: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 630: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xe2 wr=0x0 wdata=0x18e0 rdata=0x0
UVM_INFO testbench.sv(141) @ 650: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x4f wr=0x0 wdata=0xf148 rdata=0x1234
UVM_INFO testbench.sv(182) @ 650: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0x4f exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 650: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(80) @ 670: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 670: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xec wr=0x0 wdata=0xd91a rdata=0x0
UVM_INFO testbench.sv(141) @ 690: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xe2 wr=0x0 wdata=0x18e0 rdata=0x1234
UVM_INFO testbench.sv(182) @ 690: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0xe2 exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 690: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(80) @ 710: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 710: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x6e wr=0x1 wdata=0x31f6 rdata=0x0
UVM_INFO testbench.sv(141) @ 730: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xec wr=0x0 wdata=0xd91a rdata=0x1234
UVM_INFO testbench.sv(182) @ 730: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0xec exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 730: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(141) @ 750: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x6e wr=0x1 wdata=0x31f6 rdata=0x0
UVM_INFO testbench.sv(172) @ 750: uvm_test_top.e0.sb0 [scoreboard] Store addr=0x6e wr=0x1 data=0x31f6
UVM_INFO testbench.sv(80) @ 750: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 750: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x66 wr=0x0 wdata=0x2374 rdata=0x0
UVM_INFO testbench.sv(80) @ 770: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 770: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xd5 wr=0x1 wdata=0xad94 rdata=0x0
UVM_INFO testbench.sv(141) @ 790: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x66 wr=0x0 wdata=0x2374 rdata=0x1cce
UVM_INFO testbench.sv(189) @ 790: uvm_test_top.e0.sb0 [scoreboard] PASS! addr=0x66 exp=0x1cce act=0x1cce
UVM_INFO testbench.sv(94) @ 790: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(141) @ 810: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xd5 wr=0x1 wdata=0xad94 rdata=0x0
UVM_INFO testbench.sv(172) @ 810: uvm_test_top.e0.sb0 [scoreboard] Store addr=0xd5 wr=0x1 data=0xad94
UVM_INFO testbench.sv(80) @ 810: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 810: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xbb wr=0x1 wdata=0xd056 rdata=0x0
UVM_INFO testbench.sv(141) @ 830: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xbb wr=0x1 wdata=0xd056 rdata=0x0
UVM_INFO testbench.sv(172) @ 830: uvm_test_top.e0.sb0 [scoreboard] Store addr=0xbb wr=0x1 data=0xd056
UVM_INFO testbench.sv(80) @ 830: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 830: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0x18 wr=0x0 wdata=0x8bba rdata=0x0
UVM_INFO testbench.sv(80) @ 850: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(52) @ 850: uvm_test_top.e0.a0.s0@@seq [SEQ] Generate new item: addr=0xec wr=0x1 wdata=0xdfc1 rdata=0x0
UVM_INFO testbench.sv(141) @ 870: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0x18 wr=0x0 wdata=0x8bba rdata=0x1234
UVM_INFO testbench.sv(182) @ 870: uvm_test_top.e0.sb0 [scoreboard] PASS! First time read, addr=0x18 exp=1234 act=0x1234
UVM_INFO testbench.sv(94) @ 870: uvm_test_top.e0.a0.d0 [DRV] Wait until ready is high
UVM_INFO testbench.sv(141) @ 890: uvm_test_top.e0.a0.m0 [monitor] Monitor found packet addr=0xec wr=0x1 wdata=0xdfc1 rdata=0x0
UVM_INFO testbench.sv(172) @ 890: uvm_test_top.e0.sb0 [scoreboard] Store addr=0xec wr=0x1 data=0xdfc1
UVM_INFO testbench.sv(80) @ 890: uvm_test_top.e0.a0.d0 [DRV] Wait for item from sequencer
UVM_INFO testbench.sv(55) @ 890: uvm_test_top.e0.a0.s0@@seq [SEQ] Done generation of 20 items
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 1090: 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) @ 1090: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

SystemVerilog Scope Resolution Operator

The scope resolution operator :: is used to refer an identifier within the scope of a class.

Left hand side of the scope resolution operator :: should be a class type name, package name, covergroup type name, coverpoint or cross name, typedef name. The right hand side of the operator should be an identifier like a variable or method name.

Why is scope resolution operator required ?

Classes and other scopes can have same identifier names and may create a namespace collision if referred to without specifying the scope. The scope resolution operator :: uniquely identifies a member or parameter of a given class.

They are also used to access static variables and methods, parameters and local parameters of a class from outside the class. It also allows access to public and protected members of a base class from within the child class.

Examples

1. Defining extern function


class ABC;
	int 	data;
	
	extern virtual function void display();
endclass

// Definition of an external function using scope
// resolution operator
function void ABC::display();
	$display("data = 0x%0h", data);
endfunction

module tb;
	initial begin	
		ABC abc = new();
		abc.data = 32'hface_cafe;
		abc.display();
	end
endmodule
 Simulation Log
ncsim> run
data = 0xfacecafe
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

2. Accessing static methods and functions


class ABC;
	static int 	data;
	
	static function void display();
		$display("data = 0x%0h", data);
	endfunction
endclass

module tb;
	initial begin
      	ABC a1, a2;
      	
      	// Assign to static variable before creating 
      	// class objects, and display using class_type and
      	// scope resolution operator
		ABC::data = 32'hface_cafe;
		ABC::display();
      
      	a1 = new();
      	a2 = new();
      	$display ("a1.data=0x%0h a2.data=0x%0h", a1.data, a2.data);
	end
endmodule
 Simulation Log
ncsim> run
data = 0xfacecafe
a1.data=0xfacecafe a2.data=0xfacecafe
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

3. Using package


package my_pkg;
	typedef enum bit {FALSE, TRUE} e_bool;
endpackage

module tb;
  bit val;
  
  initial begin
  	// Refer to types that have been declared
  	// in a package. Note that package has to 
  	// be included in compilation but not 
  	// necessarily "imported"
    val = my_pkg::TRUE;
    $display("val = 0x%0h", val);
  end
endmodule
 Simulation Log
ncsim> run
val = 0x1
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

4. Avoid namespace collision


package my_pkg;
	typedef enum bit {FALSE, TRUE} e_bool;
endpackage

import my_pkg::*;

module tb;
  typedef enum bit {TRUE, FALSE} e_bool;
  
  initial begin
    e_bool val;
    
    // Be explicit and say that TRUE from my_pkg
    // should be assigned to val
    val = my_pkg::TRUE;
    $display("val = 0x%0h", val);
    
    // TRUE from current scope will be assigned to
    // val
    val = TRUE;
    $display("val = 0x%0h", val);
  end
endmodule
 Simulation Log
ncsim> run
val = 0x1
val = 0x0
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit
  1. UVM Phases
  2. UVM Testbench Example 2
  3. Base Classes
  4. Reporting Classes
  5. Guide - Developing Reusable Verification Components

Page 66 of 68

  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
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