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




Data and Driver

In the previous session, we had a simple testbench structure with only test and environment.That was just enough to utilize a UVM component, and print "Hello UVM". But, in a normal testbench we would have a data element that gets routed through different verification components. So, in this part, we will create a "data" packet object and send it to the "driver".

Read more: Data and Driver

Hello UVM !

Class hierarchy was discussed in the previous session. Now, let's try to construct a testbench that will print out "Hello UVM". We'll keep the testbench simple with only two main components.

  • Test
  • Environment

Testbench Setup

The testbench structure is setup as shown in the image below, with the environment instantiated inside the test and the DUT interacting with the test via the DUT interface.


simple UVM testbench

DUT

The DUT is a simple memory module that will accept data into the memory array upon a write transaction and provide data at the output ports for a read transaction. It has the following ports


module dut (   
               input    clk,                 // Clock at some freq
               input    rstn,                // Active Low  Sync Reset
               input    wr,                  // Active High Write
               input    en,                  // Module Enable
               input    wdata,               // Write Data
               input    addr,                // Address

               output   rdata                // Read Data
            );		

Interface

Let's put all the signals of the DUT inside an interface and use an interface handle to access the signals. The interface is defined as below.


interface dut_if (input clk);

   logic          rstn;
   logic [7:0]    wdata;
   logic [7:0]    rdata;
   logic [7:0]    addr;
   logic          wr;
   logic          en;

endinterface  

DUT Wrapper

It'll be easier to instantiate the DUT in the top level module if we put a wrapper around it.


module dut_wrapper (dut_if _if);

   // Instantiate the design module and connect interface signals to DUT
   dut   dsn0     (  .clk     (_if.clk),
                     .rstn    (_if.rstn),
                     .wr      (_if.wr),
                     .en      (_if.en),
                     .wdata   (_if.wdata),
                     .addr    (_if.addr),
                     .rdata   (_if.rdata));

endmodule

With the DUT wrapper, instantiating at the top level reduces to a single line.


dut_wrapper    dut_wr0  (._if (dut_if1));

Watch how to install Modelsim and run Hello World !

Test

The test is an object of uvm_test and contains the environment. The interface handle is obtained from the top level module and will pass it to all components inside the environment. The test will also decide on what sequence to run when simulation is started. This is very convenient because now you have the ability to create multiple tests that can choose from a library of sequences wihtout having to edit the code in the testbench. Let's break the class test and go through each line.


class base_test extends uvm_test;

We have defined a class called base_test that inherits all the properties and methods from uvm_test. Now, we have to register this component with the UVM factory for it to work properly by calling the macro `uvm_component_utils because test is a derivative of uvm_component.


	`uvm_component_utils (base_test)

Now, we'll create an object of the environment and a virtual interface handle that can be made to point to the correct interface.


    my_env   m_top_env;
    virtual  dut_if dut_vi;

Next, we'll code the constructor new () with arguments name and parent (default is set to null).


      function new (string name, uvm_component parent = null);
         super.new (name, parent);
      endfunction : new

Then, the build phase is defined to instantiate an object of the environment, and collect the interface from top level module. Note that we have called the parent's build phase first using super keyword. The line that contains type_id is the preferred way to create objects of classes in UVM. This is because it uses factory methods to create the object that can be easily substituted/modified later on. Note that you can also create an object using the new () method, but that does not make object instantiation flexible enough and hence is not recommended. The line that contains uvm_config_db is the new way of setting and retrieving variable values in UVM. In C, you would have declared the variable to be global and it can be accessed from anywhere, in any header/C file. In UVM, variables can be put inside the database and make it available to selected components and only those components will be able to access the variable value. So, in this case we are trying to get the dut_if object from the database.


     virtual function void build_phase (uvm_phase phase);
         super.build_phase (phase);

         m_top_env  = my_env::type_id::create ("m_top_env", this);
      
         if (! uvm_config_db #(virtual dut_if) :: get (this, "", "dut_if", dut_vi)) begin
            `uvm_error (get_type_name (), "DUT Interface not found !")
         end
      endfunction : build_phase

The function get () has arguments in the order ['context', 'instance_name', 'field_name', 'variable_to store_value'].

We can also print out the topology of the environment after the build and connect phases.


	virtual function void end_of_elaboration_phase (uvm_phase phase);
 		uvm_top.print_topology ();
 	endfunction

In the start_of_simulation_phase you can specify the sequence that a particular sequencer has to operate on.

Environment

All we do in the environment is to print out a message "Hello UVM !"


class my_env extends uvm_env ;
	`uvm_component_utils (my_env)
   
	function new (string name, uvm_component parent);
		super.new (name, parent);
	endfunction : new
   
	function void build_phase (uvm_phase phase);
		super.build_phase (phase);
	endfunction : build_phase
   
	task run_phase (uvm_phase phase);
		set_report_verbosity_level (UVM_MEDIUM);
		uvm_report_info      (get_name(), $sformatf ("Hello UVM ! Simulation has started."), UVM_MEDIUM, `__FILE__, `__LINE__);
		`uvm_info   (get_name(), $sformatf("Finishing up with run_phase ... "), UVM_LOW)
	endtask : run_phase
endclass : my_env
 Simulation Log
----------------------------------------------------------------
CDNS-UVM-1.1d (14.10-s004)
(C) 2007-2013 Mentor Graphics Corporation
(C) 2007-2013 Cadence Design Systems, Inc.
(C) 2006-2013 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:
------------------------------------
Name          Type       Size  Value
------------------------------------
uvm_test_top  base_test  -     @2601
  m_top_env   my_env     -     @201
------------------------------------

UVM_INFO ./tb/my_pkg.sv(30) @ 0: uvm_test_top.m_top_env [m_top_env] Hello UVM ! Simulation has started.
UVM_INFO ./tb/my_pkg.sv(31) @ 0: uvm_test_top.m_top_env [m_top_env] Finishing up with run_phase ...

--- UVM Report catcher Summary ---


Number of demoted UVM_FATAL reports  :    0
Number of demoted UVM_ERROR reports  :    0
Number of demoted UVM_WARNING reports:    0
Number of caught UVM_FATAL reports   :    0
Number of caught UVM_ERROR reports   :    0
Number of caught UVM_WARNING reports :    0

--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :    4
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[RNTST]     1
[UVMTOP]     1
[m_top_env]     2
Simulation complete via $finish(1) at time 0 FS + 179

Go to the next step Data and Driver

UVM Installation

The UVM (Universal Verification Methodology) library is typically included with most commercial and open-source digital design verification tools, such as Cadence Incisive, Mentor Graphics Questa, or Synopsys VCS. The UVM library provides a set of SystemVerilog classes and components that can be used to create modular and reusable testbenches for digital designs.

To install the UVM library, you typically need to follow these steps:

  • Install a compatible digital design verification tool on your computer, such as Cadence Incisive, Mentor Graphics Questa, or Synopsys VCS. You can download trial versions of these tools from their respective websites.
  • Check if the UVM library is included with the tool. Most modern digital design verification tools include the UVM library as a standard feature.
  • If the UVM library is not included with the tool, you can download it separately from the Accellera Systems Initiative website. The UVM library is an open-source library that is freely available to download and use.
  • Once you have the UVM library installed on your computer, you can start using it to create testbenches for your digital designs. You will need to import the UVM classes and components into your SystemVerilog testbench code, and then use them to create the various testbench components, such as drivers, monitors, and agents.

What are the ways to practice UVM ?

To start practicing UVM, you would need one of the following :

  • Linux terminal with a suitable simulator
  • Use UVM libraries with Modelsim in Windows
  • EDA Playground

Read more: UVM Installation

Verilog Johnson Counter

Johnson Counter

Design


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

Testbench


module tb;
  parameter WIDTH = 4;
  
  reg clk;
  reg rstn;
  wire [WIDTH-1:0] out;
  
  johnson_ctr 	u0 (.clk (clk),
                .rstn (rstn),
                .out (out));
  
  always #10 clk = ~clk;
  
  initial begin
    {clk, rstn} <= 0;

    $monitor ("T=%0t out=%b", $time, out);
    repeat (2) @(posedge clk);
    rstn <= 1;
    repeat (15) @(posedge clk);
    $finish;
  end
endmodule
 Simulation Log
ncsim> run
T=0 out=xxxx
T=10 out=0001
T=50 out=0000
T=70 out=1000
T=90 out=1100
T=110 out=1110
T=130 out=1111
T=150 out=0111
T=170 out=0011
T=190 out=0001
T=210 out=0000
T=230 out=1000
T=250 out=1100
T=270 out=1110
T=290 out=1111
T=310 out=0111
Simulation complete via $finish(1) at time 330 NS + 0

SystemVerilog Loops

What are loops ?

A loop is a piece of code that keeps executing over and over. A conditional statement is typically included in a loop so that it can terminate once the condition becomes true. If the loop runs forever, then the simulation will hang indefinitely.

Different types of looping constructs in SystemVerilog are given in the table below.

foreverRuns the given set of statements forever
repeatRepeats the given set of statements for a given number of times
whileRepeats the given set of statments as long as given condition is true
forSimilar to while loop, but more condense and popular form
do whileRepeats the given set of statements atleast once, and then loops as long as condition is true
foreachUsed mainly to iterate through all elements in an array

forever

This is an infinite loop, just like while (1). Note that your simulation will hang unless you include a time delay inside the forever block to advance simulation time.


module tb;
  
  // This initial block has a forever loop which will "run forever"
  // Hence this block will never finish in simulation
  initial begin
    forever begin
      #5 $display ("Hello World !");
    end
  end
 
  // Because the other initial block will run forever, our simulation will hang!
  // To avoid that, we will explicity terminate simulation after 50ns using $finish
  initial 
    #50 $finish;    
endmodule

Note that simulation would have continued indefinitely if $finish was not called.

 Simulation Log
ncsim> run
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Simulation complete via $finish(1) at time 50 NS + 0

repeat

Used to repeat statements in a block a certain number of times. The example shown below will display the message 5 times and continues with rest of the code.


module tb;
  
  	// This initial block will execute a repeat statement that will run 5 times and exit
	initial begin
      
        // Repeat everything within begin end 5 times and exit "repeat" block
		repeat(5) begin
			$display ("Hello World !");
		end
	end
endmodule
 Simulation Log
ncsim> run
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
ncsim: *W,RNQUIE: Simulation is complete.

while

You already know this if you know verilog/C. It'll repeat the block as long as the condition is true. Counter is initially zero and increments until it reaches 10.


module tb;
 bit clk;
  
  always #10 clk = ~clk;
  initial begin
  	bit [3:0] counter;
 
    $display ("Counter = %0d", counter);      // Counter = 0
  	while (counter < 10) begin
    	@(posedge clk);
    	counter++;
        $display ("Counter = %0d", counter);      // Counter increments
  	end
  	$display ("Counter = %0d", counter);      // Counter = 10
    $finish;
end
endmodule
 Simulation Log
ncsim> run
Counter = 0
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 6
Counter = 7
Counter = 8
Counter = 9
Counter = 10
Counter = 10
Simulation complete via $finish(1) at time 190 NS + 0

for

Similar to verilog/C, this allows you to mention starting value, condition and incremental expression all on the same line.


module tb;
 bit clk;
  
  always #10 clk = ~clk;
  initial begin
  	bit [3:0] counter;
 
    $display ("Counter = %0d", counter);      // Counter = 0
  	for (counter = 2; counter < 14; counter = counter + 2) begin
    	@(posedge clk);
    	$display ("Counter = %0d", counter);      // Counter increments
  	end
    $display ("Counter = %0d", counter);      // Counter = 14
    $finish;
  end
endmodule
 Simulation Log
ncsim> run
Counter = 0
Counter = 2
Counter = 4
Counter = 6
Counter = 8
Counter = 10
Counter = 12
Counter = 14
Simulation complete via $finish(1) at time 110 NS + 0

do while

This executes the code first and then checks for the condition to see if the code should be executed again.


module tb;
 bit clk;
  
  always #10 clk = ~clk;
  initial begin
  	bit [3:0] counter;
 
    $display ("Counter = %0d", counter);      // Counter = 0
		do begin 
			@ (posedge clk);
			counter ++;
          $display ("Counter = %0d", counter);      // Counter increments
        end while (counter < 5);
    $display ("Counter = %0d", counter);      // Counter = 14
    $finish;
  end
endmodule
 Simulation Log
ncsim> run
Counter = 0
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 5
Simulation complete via $finish(1) at time 90 NS + 0

foreach

This is best suited to loop through array variables, because you don't have to find the array size, set up a variable to start from 0 until array_size-1 and increment it on every iteration.


module tb_top;
   bit [7:0] array [8];   // Create a fixed size array

   initial begin
   
      // Assign a value to each location in the array
      foreach (array [index]) begin
         array[index] = index;
      end
      
      // Iterate through each location and print the value of current location
      foreach (array [index]) begin
         $display ("array[%0d] = 0x%0d", index, array[index]);
      end
   end
endmodule
 Simulation Log
ncsim> run
array[0] = 0x0
array[1] = 0x1
array[2] = 0x2
array[3] = 0x3
array[4] = 0x4
array[5] = 0x5
array[6] = 0x6
array[7] = 0x7
ncsim: *W,RNQUIE: Simulation is complete.
  1. Verilog Gray Counter
  2. Verilog Scheduling Semantics
  3. Verilog Timescale
  4. SystemVerilog Interprocess Communication
  5. SystemVerilog Functional Coverage

Page 52 of 63

  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
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