- Details
- Published: Friday, 12 October 2018 04:53
- Hits: 436
UVM: unified verification methodology. UVM created in 2009. It's just a bunch of sv files with lot of tasks, functions, classes, etc already written, so that you don't have to write it. You just have to learn how to call them.
---
NOTE: In sv, classes are created at runtime, as opposed to verilog/vhdl, where all instances are elaborated before runtime.
We can use irun/xrun with -uvm option which then allows us to access cadence uvm library. Top level design units that show while compiling are uvm_pkgs (uvm_pkg.sv cdns_uvm_pkg.sv cdns_uvmapi cdns_assert2uvm_pkg) and tb.sv
uvm library files are usually installed in these paths for Cadence simulator:
ies: /apps/cds/incisiv/14.20.01/tools/methodology/UVM/CDNS-1.1d/additions/sv/ => for uvm 1.1 (all files are system verilog files)
xcelium: /home/.../cadence/xcelium/19.01.v001/tools/methodology/UVM/CDNS-1.2/sv/ => for uvm 2.0
Within this "sv" dir are multiple dir. Source code is in dir = src
src/uvm_pkg.sv => It's a package, with bunch of include files (*.svh) in it
src/uvm_macros.svh => It has bunch of include files for macro defn (*_defines.svh) in it. This is not a package, just a regular file with include files which have various macro defines (i.e `define uvm_delay(TIME) #(TIME);)
There are bunch of more dir in "src" that has all of these include files, and all other files needed by uvm (i.e src/base/uvm_root.svh has run_test task, ..).
sample tb testbench: => no test being called in this
---------------
module tb;
import uvm_pkg::*; // uvm package imported, these 2 lines needed
`include "uvm_macros.svh" //uvm macro included (to get all defn)
initial begin
`uvm_info("TEST", "Hello!!!", UVM_LOW) //NOTE: no semicolon at end since it's replaced by the macro defn found in uvm_macros.svh
end
endmodule : tb
cmd:
---
irun -uvm tb.sv => -uvm compiles uvm pkgs (uvm_pkg.sv cdns_uvm_pkg.sv). Then module tb is compiled. Then ncsim is run, which causes "initial" block in tb to run. It prints "HELLO!!!" from that block
screen o/p:
-----------
SVSEED default: 1
ncsim> source /apps/cds/incisiv/14.20.017p1/tools/inca/files/ncsimrc
ncsim> source /apps/cds/incisiv/14.20.017p1/tools/methodology/UVM/CDNS-1.1d/additions/sv/files/tcl/uvm_sim.tcl => This has bunch of tcl proc which parse various uvm cmds for option correctness, before calling the uvm package (i.e uvm_version proc finally calls uvm_pkg::uvm_version)
UVM_INFO tb.sv(12) @ 0: reporter [TEST] Hello!!!
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit
----------------------------------------------
Now we modify tb above to include a test to run
----------------------------------------------
sample tb testbench: => test being called in this
---------------
module tb;
// uvm package/macro, these 2 lines below needed
import uvm_pkg::*;
`include "uvm_macros.svh"
wire a, b,..; //nets, reg etc defined which are used here
digtop u_digtop (.A(wire1), ...); //inst digtop
pullup(GPIO1); //inst other modules
i2c_bfm u_i2c_bfm (.SCL(SCL), ...);
always @(a) begin .. end //for capturing events
//simple interface for interfacing internal nets => we always connect dut signals to interface, and then interface interacts with our uvc. This is to make it resuable. We use virtual interface (which then needs to be connected to real interface). config database is used to set these virtual i/f.
gpio_interface gpio_if (.pclk(`TB.u_dig_top.clk_16m),.data(..),...);
adc_interface adc_if (....);
//ex of interface: (see pg 300 of UVM lecture notes from cadence)
interface (input clk, rst);
logic [7:0] in_data;
logic ...;
endinterface
//configuring interface ..
initial begin
//components can be configured using uvm_config_db. uvm_config_db is a class that has set and get function defined in it. We can supply the name of field, and value to be set or get using this uvm_config_db
uvm_config_db #(virtual gpio_interface)::set(null, "*", "gpio_vif", gpio_if); //gpio_vif is set to value gpio_if
uvm_config_db #(virtual adc_interface)::set(null, "*", "adc_vif", adc_vif);
end
initial begin
`uvm_info("TEST", "Hello!!!", UVM_LOW)
run_test(); // calling test to run. run_test is a task in .../CDNS-1.1d/sv/src/base/uvm_root.svh [task uvm_root::run_test(string test_name=""); ... endtask] which calls run_phases [fork ... uvm_phase::m_run_phases(); join_none] and finally does $finish, once all done. All uvm tasks are in base dir. i.e base/uvm_phase.svh, etc. So, no explicit $finish needed in our testcase.
end
initial begin
#10ms;
$finish; //for sim timeout (there's already a $finish in uvm run_test call)
end
//code below differs for separate testcases. So, we can keep below code in separate files so that each test has it's own tc.sv file. similar to how we do our regular testcases in non-uvm env. In this ex, testcase is part of same file as tb.
/////////////// testcase start = base_test.sv /////////////
NOTE: classes are same as modules. They have local signals, function, etc
`define testname base_test
class `testname extends uvm_test; //uvm_test is built in uvm class for tests.
`uvm_component_utils(`testname)
byte mask[]; //dynamic byte array of undefined length
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TEST", "Running base Test", UVM_LOW)
endfunction : build_phase
//more func and task (everything below optional and not included for irun)
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
`uvm_info("TEST", "End of elab: Sample Test", UVM_LOW)
endfunction : end_of_elaboration_phase
function init_test_seq(test_seq_base test_seq);
test_seq.env = this.env; ...
endfunction
//tasks
task run_phase(uvm_phase phase); //NOTE: we can also use task "main_phase" instead of "run_phase"
phase.raise_objection(this, "base_test"); //raise obj, uvm keeps track of how many obj raised
... // all tests done here
phase.drop_objection(this, `teststr); // drop obj, only when all obj dropped, $finish called by uvm
endtask:
//more tasks
task i2c_read(input bit [6:0] addr, input byte reg output byte data[]);
...
endtask
endclass : base_test
/////////////////////// testcase end /////////////
endmodule : tb
cmd:
---
irun -uvm tb.sv => calls default test (base_test) to run.
irun -uvm tb.sv +UVM_TESTNAME=base_test => or we can provide test name explicitly if there are many tests. same as above if only 1 test.
If base_test is in separate file (other than tb.sv), then we need to give name of that file too so that irun can compile/elaborate it, i.e
irun -uvm tb.sv base_test.sv +UVM_TESTNAME=base_test
screen o/p:
----------
UVM_INFO tb.sv(12) @ 0: reporter [TEST] Hello!!!
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO tb.sv(29) @ 0: uvm_test_top [TEST] Running Base Test => prints "Running Base test" from function above
--- 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 : 3
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[TEST] 2
Simulation complete via $finish(1) at time 0 FS + 179 => NOTE: here sim finishes via $finish
/apps/cds/incisiv/14.20.017p1/tools/methodology/UVM/CDNS-1.1d/sv/src/base/uvm_root.svh:457 $finish;
ncsim> exit
-----------------------------------
we can create another testcase called sample_test which can extend base_test (in sample_test.sv)
//////////////////////// sample_test.sv => testcase start /////////////
`define testname sample_test
class `testname extends base_test;
`uvm_component_utils(`testname)
byte mask[]; //dynamic byte array of undefined length
//these function defn below may not be needed
extern function new(string name = `teststr, uvm_component parent = null); //defined as external function, so compiler looks for these function outside the class
extern function void build_phase(uvm_phase phase);
extern task run_phase(uvm_phase phase);
extern task main_phase(uvm_phase phase);
endclass
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TEST", "Running sample Test", UVM_LOW)
endfunction : build_phase
//more functions
function check_val (byte data1, int i);
....
endfunction
//main task
task `testname::main_phase(uvm_phase phase);
int a,b;
super.main_phase(phase); //super fn always called for phases before doing anything else
phase.raise_objection(this, "sample_test"); //raise obj
$display(`testname:: main_phase");
#10; addr = ... //write like normal task
#1ms;
phase.drop_objection(this, `teststr); // drop obj
endtask: main_phase
/////////////////////// testcase end /////////////
-------------------------------
see UVM lect from Cadence 5 day workshop:
tb consists of "reusable verification component (UVC)" + DUT. It also has sv i/f to link VC to DUT.
testcases can be put in separate files or within tb.
UVC consists of
- sequencer = to create high level data (as do_read), either random or under control of test data (has TX transmitter)
- driver (BFM=Bus functional Model) = to drive i/f signals to DUT (from high level to low level signals)
- Monitor = to capture activity on i/f for coverage, checking and analysis (has RX receiver)
Top level of UVC is "Env". Env contains one or more agents. Each agent contains inst of Sequencer, driver, monitor and config. config indicates if it'a active agent (i.e TX => all seq, driver, monitor implemented) or passive agent (i.e RX => only monitor implemented)
All above components are created using class.
class sequencer extends base ... endclass
class driver extends base ... endclass
class monitor extends base ... task monitor(bit[3:0] ..) forever begin ... end endtask endclass
interface pds_if(input ...) .. endinterface
//PDS UVC => top level uvc which inst all of the above components
class pds_vc extends base; ... endclass
On top of this, scorboard UVC is also added to tb, so that i/p and o/p to/from DUT can be compared and errors flagged.
UVM phases: (pg 177 of UVM doc from cadence) These are predefined phases. User defined phases can also be added. Phases start running in the order below when global task run_test() is called.
NOTE: to use phases below, we simply declare *_phase function in your class, and add reqd functionality. We should not call *_phase directly. It's automatically executed by simulator during that phase.
- build_phase => building testbench. All hier should be built using build_phase rather than constructors. build_phase executes top down, all other phases execute bottom up.
- connect_phase => making connections. Any connections b/w components should be made using connect_phase rather than constructors
- end_of_elaboration_phase => for any pre run activity
- run_phase => simulation of design. run_phase is implemented as a task, as it can consume time. Rest all phases are implemented as functions, as they run in 0 time. run_phase has many subphases and were added in UVM1.0. These sub-phases run in parallel with run_phase.
- check_phase, report_phase => checking results and generating reports
----------------------------
Built in classes (chapter 4 in uvm book)
---------
UVM lib has many inbuilt classes from which we extend to create our classes. Ex:
1. uvm_object: this is the base class for all (data and component) UVM classes. It has built in methods for copy(), compare(), clone(), print(). Most of the time, we derive our data item from uvm_sequence_tem which itself extends from uvm_transaction which extends from uvm_object.
ex:
class apb_transfer extends uvm_object; //or "extends uvm_sequence_item" can also be used
...
`uvm_objects_utils_begin(apb_transfer) //uvm automation macros => used to declare common operaations
`uvm_field_int(addr, UVM_DEFAULT)
`uvm_objects_utils_end
function new (string name = "apb xfer"); //uvm construction new() - not mandatory
super.new(name);
endfunction
endclass
2. uvm_component: All infrastructure components as testbench comp, UVCs, tests are derived directly or indirectly from uvm_component. Typically we derive our user classes from methodology classes (uvm_agent, uvm_driver, uvm_sequencer, uvm_monitor, uvm_scoreboard, uvm_test and so on), which are themselves extension of uvm_component. Phases and configuration methods are functionality provided by uvm_component base class.
ex:
class testbench_comp extends uvm_component; //testcase
function new(string name, uvm_component parent)
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_uvc = if_comp::type_id::create("my_uvc", this);
endfunction
//similarly other function for end_of_elaboration_phase, etc can be added here
task run_phase (uvm_phase phase); //this is where the testcase is coded and run
...
endtask
endclass
class if_comp extends uvm_component; //this is some other class that has many uvm_phases
function new ... endfunction
function void build_phase ... endfunction
task run_phase ... endtask
endclass
module test; //testbench to run above testcase
testbench_comp tb;
initial begin
tb = testbench_comp::type_id::create("testbench", null);
run_test(); //this call starts the uvm phases for all the classes above
end
endmodule
-----------------
contraints:
-------
In any class, we can have constraints defined for any field.
ex: constraint c_addr {addr[1:0] == 2'b01; }
we can overwrite any constraint in subclass, by redeclaring. Parent class contraint can be removed in subclass by leaving it empty.
ex: constarint c_addr {} => constraint c_addr is removed for this subclass, even though it's present in parent class.
-----------
coverage in uvm: see system verilog notes also:
----------
Metric driven verification
1. functional coverage
2. assertions (static formal verification and sims)
3. directed tests
4. code coverage
Most coverage constructs are placed in monitors.
1. functional coverage of 2 types:
A. assertions for control oriented coverage => cannot be in class
ex:
property req_gnt (cyc);
@(posedge clk) req |=> ##[cyc] gnt;
endproperty
cover property (req_gnt(3));
cover property (req_gnt(4));
B. covergroups for data-oriented coverage => can be declared as class member
ex:
covergroup cg @(posedge clk);
len: coverpoint pkt.length { ...}
addr: cross pkt.addr, len;
endgroup
cg cg1 = new();
--------------