Friday, June 30, 2023

UVM verification environment universal template

 Verilog RTL verification

UVM verification environment universal template

UVM verification environment starting point for use in verification of any RTL Digital Design Under Test (DUT)

In this blog post there is a source code of all UVM verification environment files to use, as a starting point,  in verification of any RTL Digital Design Under Test (DUT). The assembly of files of  the UVM verification template compiles OK.

To achieve the successful compilation of the UVM verification environment universal template files, in this case,  a Scorebord.sv file is not present ( empty file) and a dummy DUT ( SystemVerilog RTL module and its corresponding SystemVerilog interface)   is included.

UVM verification environment  architecture


For UVM verification  of any DUT what  we need to think about is an architecture so our architecture will contain files organized in a  System Verilog UVM hierarchy as described in previously showed  block diagram 

  • top testbench module :  testbench.sv

    • Design file 

      • DUT.sv 

    • UVM verification infrastructure files

      •  Interface.sv ( SystemVerilog interface of a desired DUT ) 

      • UVM verification classes ( one class per file and  per  UVM block in previously presented block diagram) :

        • test class (COMPONENT class, test.sv)

          • environment class (COMPONENT class, env.sv) 

          • scoreboard  class (COMPONENT class, scoreboard.sv)

          • agent class ( COMPONENT class, agent.sv)  

            • driver (COMPONENT class, driver.sv) 

            • monitor (COMPONENT class, monitor.sv)  

            • sequencer (COMPONENT class, sequencer.sv) 

        • sequence/sequence item ( wo OBJECT classes in one file:  sequence.sv)

DUT 

module SimpleDUT (
    input clk,
    input [3:0] din_i,
    output [3:0] dout_o
);
    // Logic for the DUT
    assign dout_o = din_i;
endmodule


Interface

interface SimpleDUT_interface(input logic clock);
  logic [3:0] din_i, dout_o;
endinterface: SimpleDUT_interface


 testbench.sv ( top testbench module )


`timescale 1ns/1ns

// This is where the basic UVM necessary classes are included that we need to get started with our top module.
// The first thing, whenever you start with a UVM test bench, you need to define some UVM based packages so we need to import them here using  this  basic syntax
import uvm_pkg::*;

// we also need to include some UVM macros
`include "uvm_macros.svh"

//--------------------------------------------------------
//Include Files
//--------------------------------------------------------
// The way we include the classes in the top testbench is that the order of the included classes is very important.
// The first class that you are going to use should be the first one included and so on. 

// e.g. that is the reason why we start with sequence item class etc.etc.

`include "interface.sv"
`include "sequence_item.sv"
`include "sequence.sv"
`include "sequencer.sv"
`include "driver.sv"
`include "monitor.sv"
`include "agent.sv"
`include "scoreboard.sv"
`include "env.sv"
`include "test.sv"

module top;

// the clock for DUT is generated  from the top testbench module so let's declare a signal clock here
logic clock;
 
  SimpleDUT_interface intf(.clock(clock));
 
  SimpleDUT dut(
    .clk(intf.clock),
    .din_i(intf.din_i),
    .dout_o(intf.dout_o) );

//--------------------------------------------------------
  //Interface Setting
  //--------------------------------------------------------
  initial begin
// In SystemVerilog, the uvm_config_db is a utility provided by the Universal Verification Methodology (UVM) library. It allows you to set configuration values in a centralized database that can be accessed by different components of your testbench
// uvm_config_db is the class that provides the configuration database functionality in UVM
// In the given example, the uvm_config_db::set call sets the configuration value "vif" with the value intf (an instance of SimpleDUT_interface) in the global context for all instances in the hierarchy. This allows other components of the testbench to access and use this configuration value when needed.
/*
#(virtual SimpleDUT_interface): This is a type parameter passed to the uvm_config_db class, specifying the data type of the configuration value. In this case, it is a virtual interface type called SimpleDUT_interface.
set: This is a static method of the uvm_config_db class used to set a configuration value in the database.
null: This represents the context in which the configuration value is being set. In this case, it is set to null, indicating a global context.
"*": This is the hierarchical path pattern specifying the scope at which the configuration value is applicable. In this case, "*" indicates that the configuration applies to all instances in the hierarchy.
"vif": This is the name of the configuration field being set. It is a user-defined identifier that represents a specific configuration parameter or object in the database.
intf: This is the value being set for the configuration field "vif". It represents an instance of the SimpleDUT_interface virtual interface.
*/

    uvm_config_db #(virtual SimpleDUT_interface)::set(null, "*", "vif", intf );
    //-- Refer: https://www.synopsys.com/content/dam/synopsys/services/whitepapers/hierarchical-testbench-configuration-using-uvm.pdf
  end

// The last thing to add  in our top testbench  is:  start the test  and that is done by calling  run test task in.
// The top module should take care of our test so when you reach this stage.

//Start The Test
  //--------------------------------------------------------
  initial begin
    run_test("SimpleDUT_test");
  end
  

  //--------------------------------------------------------
  //Clock Generation
  //--------------------------------------------------------
// Since the clock for DUT is generated  from the top testbench module , let's generate the clock here
  initial begin
    clock = 0;
    #5;
    forever begin
      clock = ~clock;
      #2;
    end
  end
  //--------------------------------------------------------
  //Maximum Simulation Time
  //--------------------------------------------------------
// let's also keep a safety check which terminates our simulation if a given number of clock Cycles X exceeds so we will keep it to maybe 5000ns
  initial begin
    #5000;
    $display("Sorry! Ran out of clock cycles!");
    $finish();
  end
 
 
  //--------------------------------------------------------
  //Generate Waveforms
  //--------------------------------------------------------
  // if you want to see the waveforms you can also use the dump file  command to  tell the simulator to dump variable values so that
  // you can inspect  the waveforms after verification/simulation

  initial begin
    $dumpfile("d.vcd");
    $dumpvars();
  end
 
endmodule: top





UVM Test Component template


// let's create a new class in a file named test.sv
// we can start with naming our class SimpleDUT_test.
// SimpleDUT_test   extends uvm_test class

//COMPONENT class
class SimpleDUT_test extends uvm_test;
  // first thing we need to do in a class
  // is declare the necessary macros that will help it
  // register everything with the UVM classes
  // so we need to call this macro called `uvm_component_utils
  // and we need to pass the same name of the class that we are going to use
  // and that is SimpleDUT_test

  `uvm_component_utils(SimpleDUT_test)

  //  second thing we need to do is write a Constructor
  //  start with: function new();
  //                    endfunction
  // inside brackets of new() two parameters:  first  string name is equal to the
  // name of the class( SimpleDUT_test) and then we also need to write
// uvm_component parent

// Finally basic constructor for UVM classes is:
// function new(string name = "SimpleDUT_test", uvm_component parent);
//    super.new(name, parent);
// endfunction: new

// also keep some display statement so that it becomes easier to debug
//  we will use the default `uvm_info macro that UVM already has
// it has like three parameters first is the tag of the comment that we want to give(  we can just give a
// TEST_CLASS and then we can also use a
// just some comment that we want to pass when  inside this Constructor
// ( "Inside Constructor!")
// and then verbosity level for example UVM_HIGH

       
  //--------------------------------------------------------
  // Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_test", uvm_component parent);
    super.new(name, parent);
    `uvm_info("TEST_CLASS", "Inside Constructor!", UVM_HIGH)
  endfunction: new

  //and now we need to write the build phase so
  // we do that by using function void

  //--------------------------------------------------------
  // Build Phase
  //--------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TEST_CLASS", "Build Phase!", UVM_HIGH)
   
  endfunction: build_phase

// Next is  connect phase which is same similar to build phase
// but we'll just change the syntax

//--------------------------------------------------------
  //Connect Phase
  //--------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("TEST_CLASS", "Connect Phase!", UVM_HIGH)

  endfunction: connect_phase

// we also need to write a run phase. There are a lot of phases in a UVM but
// these three are the main phases  with which
// our SimplDUT could be verified easily

// all the other phases are functions while only the run phase is a task because
// run phase can consume time



//--------------------------------------------------------
  //Run Phase
  //--------------------------------------------------------
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("TEST_CLASS", "Run Phase!", UVM_HIGH)
  endtask: run_phase


endclass: SimpleDUT_test


Environment class/other COMPONENT classes templates

 Let's create the environment class template which will be same as the test class template ( and in the same fashion all other necessary COMPONENT classes templates ) 

  • As a friendly reminder the  environment class template  will have  instances of agent and scoreboard classes template istances  as it is planned in our UVM verification infrastructure architecture 



class SimpleDUT_env extends uvm_test;
  `uvm_component_utils(SimpleDUT_env)

  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_env", uvm_component parent);
    super.new(name, parent);
    `uvm_info("ENV_CLASS", "Inside Constructor!", UVM_HIGH)

  endfunction: new

  //--------------------------------------------------------
  //Build Phase
  //--------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("ENV_CLASS", "Build Phase!", UVM_HIGH)
 
endfunction: build_phase

 
  //--------------------------------------------------------
  //Connect Phase
  //--------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("ENV_CLASS", "Connect Phase!", UVM_HIGH)

  endfunction: connect_phase

 
  //--------------------------------------------------------
  //Run Phase
  //--------------------------------------------------------
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("ENV_CLASS", "Run Phase!", UVM_HIGH)

  // Logic

    endtask: run_phase

endclass: SimpleDUT_env



Agent class template ( once more Component class ) 

Let's create the agent class template which will be same as the environment and test class templates ( and in the same fashion all other necessary COMPONENT classes ) 

  • The agent class template  will have  three instances  class templates:  driver sequencer and monitor



class SimpleDUT_agent extends uvm_agent;
  `uvm_component_utils(SimpleDUT_agent)

 
  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_agent", uvm_component parent);
    super.new(name, parent);
    `uvm_info("AGENT_CLASS", "Inside Constructor!", UVM_HIGH)

  endfunction: new

 
  //--------------------------------------------------------
  //Build Phase
  //--------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("AGENT_CLASS", "Build Phase!", UVM_HIGH)
 
endfunction: build_phase

 
  //--------------------------------------------------------
  //Connect Phase
  //--------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("AGENT_CLASS", "Connect Phase!", UVM_HIGH)

  endfunction: connect_phase

 
  //--------------------------------------------------------
  //Run Phase
  //--------------------------------------------------------
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("AGENT_CLASS", "Run Phase!", UVM_HIGH)

  // Logic

    endtask: run_phase

endclass: SimpleDUT_agent



Driver class  template ( one more Component class )

Note: driver class is a parameterized class so we need to pass the name of the sequence item class that we are going to use 

  • we haven't created the sequence item class but I know the name of it will be: SimpleDUT_sequence_item)

    • e.g. class SimpleDUT_driver extends uvm_driver#(SimpleDUT_sequence_item);

The driver knows what type of sequence item that we are going to drive on the sequencer and that is why we pass this as a paramete


// Note: driver class is a parameterized class so we need to pass the name of the sequence item class that we are going to use
// we haven't created the sequence item class but I know the name of it will be SimpleDUT_sequence_item)

  class SimpleDUT_driver extends uvm_driver#(SimpleDUT_sequence_item);
  `uvm_component_utils(SimpleDUT_driver)

 
  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_driver", uvm_component parent);
    super.new(name, parent);
    `uvm_info("DRIVER_CLASS", "Inside Constructor!", UVM_HIGH)

  endfunction: new

 
  //--------------------------------------------------------
  //Build Phase
  //--------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("DRIVER_CLASS", "Build Phase!", UVM_HIGH)
 
endfunction: build_phase

 
  //--------------------------------------------------------
  //Connect Phase
  //--------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("DRIVER_CLASS", "Connect Phase!", UVM_HIGH)

  endfunction: connect_phase

 
  //--------------------------------------------------------
  //Run Phase
  //--------------------------------------------------------
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("DRIVER_CLASS", "Run Phase!", UVM_HIGH)

  // Logic

    endtask: run_phase

endclass: SimpleDUT_driver


Monitor class  template ( one more Component class )


class SimpleDUT_monitor extends uvm_monitor;
  `uvm_component_utils(SimpleDUT_monitor)

 
  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_monitor", uvm_component parent);
    super.new(name, parent);
    `uvm_info("MONITOR_CLASS", "Inside Constructor!", UVM_HIGH)

  endfunction: new

 
  //--------------------------------------------------------
  //Build Phase
  //--------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("MONITOR_CLASS", "Build Phase!", UVM_HIGH)
 
endfunction: build_phase

 
  //--------------------------------------------------------
  //Connect Phase
  //--------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("MONITOR_CLASS", "Connect Phase!", UVM_HIGH)

  endfunction: connect_phase

 
  //--------------------------------------------------------
  //Run Phase
  //--------------------------------------------------------
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("MONITOR_CLASS", "Run Phase!", UVM_HIGH)

  // Logic

    endtask: run_phase

endclass: SimpleDUT_monitor


Sequencer class  template ( once more Component class )

Note #1: Same as driver class, the sequencer class  is a parameterized class so we need to pass the name of the sequence item class that we are going to use 

  • we haven't created the sequence item class but I know the name of it will be: SimpleDUT_sequence_item)

    • e.g. class SimpleDUT_sequencer extends uvm_sequencer#(SimpleDUT_sequence_item);

Note# 2: also in the sequencer we don't need the “run” phase so I'll just comment out  that part 

  • actually we are not going to use any phases in sequencer but just since this is a basic test bench let’s keep it here anyway.


// Note #1: Same as driver class, the sequencer class  is a parameterized class so we need to pass the name of the sequence item class that we are going to use
// we haven't created the sequence item class but I know the name of it will be SimpleDUT_sequence_item)

// class SimpleDUT_sequencer extends uvm_sequencer;
  class SimpleDUT_sequencer extends uvm_sequencer#(SimpleDUT_sequence_item);
  `uvm_component_utils(SimpleDUT_sequencer)

 
  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_sequencer", uvm_component parent);
    super.new(name, parent);
    `uvm_info("SEQUENCER_CLASS", "Inside Constructor!", UVM_HIGH)

  endfunction: new

 
  //--------------------------------------------------------
  //Build Phase
  //--------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("SEQUENCER_CLASS", "Build Phase!", UVM_HIGH)
 
endfunction: build_phase

 
  //--------------------------------------------------------
  //Connect Phase
  //--------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("SEQUENCER_CLASS", "Connect Phase!", UVM_HIGH)

  endfunction: connect_phase

  // Note# 2: also in the sequencer we don't need the Run phase so I'll just comment out  that part
  /*
  //--------------------------------------------------------
  //Run Phase
  //--------------------------------------------------------
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("SEQUENCER_CLASS", "Run Phase!", UVM_HIGH)

  // Logic

    endtask: run_phase
  */

endclass: SimpleDUT_sequencer


Sequence item class  template ( an OBJECT class)

The sequence item class  is an object class and not a component class so I won't copy

everything from the previous component class  templates.


//Object class
class SimpleDUT_sequence_item extends uvm_sequence_item;
// just like the component class this class will also need a macro but it will be UVM object utils
  `uvm_object_utils(SimpleDUT_sequence_item)

// Instantiation logic/wires/registers same as used in interface go here
// so in other words: straight copy from interface
// We are going to use this to generate random values about DUT input port values on one hand and to store the output result on the other hand.
// So to do that what we are going to do is declare these signals as DUT input port values Rand signals


  //--------------------------------------------------------
  //Instantiation
  //--------------------------------------------------------
  rand logic [3:0] din_i;
  logic [3:0] dout_o;

// let's see how Constructor for an object class will be defined: 

// In object class Constructor  we just pass the name which can be done  like this
  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name = "SimpleDUT_sequence_item");
    super.new(name);

  endfunction: new

endclass: SimpleDUT_sequence_item

Base sequence class template ( an OBJECT class, file: sequence.sv )

class SimpleDUT_base_sequence extends uvm_sequence;
  `uvm_object_utils(SimpleDUT_base_sequence)

  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name= "SimpleDUT_base_sequence");
    super.new(name);
    `uvm_info("BASE_SEQ", "Inside Constructor!", UVM_HIGH)
  endfunction

// in sequence class we need to define
// body describing what is going to happen  when we call this sequence

  //--------------------------------------------------------
  //Body Task
  //--------------------------------------------------------
  task body();
    `uvm_info("BASE_SEQ", "Inside body task!", UVM_HIGH)

       
  endtask: body
 
endclass: SimpleDUT_base_sequence




class SimpleDUT_test_sequence extends SimpleDUT_base_sequence;
  `uvm_object_utils(SimpleDUT_test_sequence)
 
  SimpleDUT_sequence_item item;
 
  //--------------------------------------------------------
  //Constructor
  //--------------------------------------------------------
  function new(string name= "SimpleDUT_test_sequence");
    super.new(name);
    `uvm_info("TEST_SEQ", "Inside Constructor!", UVM_HIGH)
  endfunction
 
 
  //--------------------------------------------------------
  //Body Task
  //--------------------------------------------------------
  task body();
    `uvm_info("TEST_SEQ", "Inside body task!", UVM_HIGH)
   
  endtask: body
 
 
endclass: SimpleDUT_test_sequence


Compilation

[2023-06-30 07:38:14 EDT] vcs -licqueue '-timescale=1ns/1ns' '+vcs+flush+all' '+warn=all' '-sverilog' +incdir+$UVM_HOME/src $UVM_HOME/src/uvm.sv $UVM_HOME/src/dpi/uvm_dpi.cc -CFLAGS -DVCS design.sv testbench.sv  && ./simv +vcs+lic+wait '+UVM_VERBOSITY=UVM_HIGH' 

……

Parsing design file 'design.sv'

Parsing design file 'testbench.sv'


Note-[SV-LCM-PPWI] Package previously wildcard imported

testbench.sv, 8

$unit

 Package 'uvm_pkg' already wildcard imported.

 Ignoring uvm_pkg::*

 See the SystemVerilog LRM(1800-2005), section 19.2.1.


Parsing included file '/apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/uvm_macros.svh'.

Back to file 'testbench.sv'.

Parsing included file 'interface.sv'.

Back to file 'testbench.sv'.

Parsing included file 'sequence_item.sv'.

Back to file 'testbench.sv'.

Parsing included file 'sequence.sv'.

Back to file 'testbench.sv'.

Parsing included file 'sequencer.sv'.

Back to file 'testbench.sv'.

Parsing included file 'driver.sv'.

Back to file 'testbench.sv'.

Parsing included file 'monitor.sv'.

Back to file 'testbench.sv'.

Parsing included file 'agent.sv'.

Back to file 'testbench.sv'.

Parsing included file 'scoreboard.sv'.

Back to file 'testbench.sv'.

Parsing included file 'env.sv'.

Back to file 'testbench.sv'.

Parsing included file 'test.sv'.

Back to file 'testbench.sv'.

Top Level Modules:

      top

TimeScale is 1 ns / 1 ns

…..

recompiling module SimpleDUT

recompiling module SimpleDUT_interface

recompiling module top

All of 6 modules done

……

UVM_INFO test.sv(42) @ 0: uvm_test_top [TEST_CLASS] Inside Constructor!

UVM_INFO @ 0: reporter [RNTST] Running test SimpleDUT_test...

UVM_INFO test.sv(53) @ 0: uvm_test_top [TEST_CLASS] Build Phase!

UVM_INFO test.sv(66) @ 0: uvm_test_top [TEST_CLASS] Connect Phase!

UVM_INFO test.sv(84) @ 0: uvm_test_top [TEST_CLASS] Run Phase!

UVM_INFO /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_report_server.svh(904) @ 0: reporter [UVM/REPORT/SERVER]

--- UVM Report Summary ---


** Report counts by severity

UVM_INFO :    6

UVM_WARNING :    0

UVM_ERROR :    0

UVM_FATAL :    0

** Report counts by id

[RNTST]     1

[TEST_CLASS]     4

[UVM/RELNOTES]     1


$finish called from file "/apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_root.svh", line 527.

$finish at simulation time                    0

          Done

© 2023 ASIC Stoic. All rights reserved.

No comments:

Post a Comment