Uvm by Praveen

Download as pdf or txt
Download as pdf or txt
You are on page 1of 26

UVM crash course:

Topics covered:
1. UVM methodology
2. UVM factory
3. Factory overriding
4. Stimulus modelling
5. UVM phases
6. Reporting mechanism
7. TLM overview
8. UVM configuration
9. Virtual interface configuration
10. Test bench components
11. UVM sequencer

UVM Methodology:
UVM – universal verification methodology, introduced in 2011.
UVM is all about automation of the test bench.
It provides a frame work for creating modular, reusable testbench components that can be easily integrated into the
design verification process.
It includes a set of guidelines and best practices for developing test benches, as well as methodology for running
simulation and analysing results.
OVM – Open verification methodology, introduced in 2008.
Singleton class – using this class only one object can be created. Eg: test class
SV is good for IP verification and UVM is suitable for IP verification and SOC verification also.
PDC’s – protocol dependent components. Eg: Transactors (driver, monitor, sequencer)
UVC – universal verification component. Eg: agent class
Sequence means sequence of packets. Handle or instance means both are same.
In UVM library except uvm_driver, uvm_sequencer all are virtual classes.

Features of UVM: (why methodology?)


1. Reusability – plug and play is possible in UVM.
2. Configurability – (configuration methods, factory) configuration of the test bench from the top level.
3. Independability – develop test cases independent to the TB environment
4. Inter-operability – standard communication protocols (TLM) to achieve interoperability.
5. Portability - plug and play is possible in UVM.

What is methodology:
1. Best practices by verification experts.
2. Base class library.
3. Standard communication mechanism to achieve interoperability.
4. Configuration of the testbench from the top level.
5. Generate sequences independent of TB environment.
6. Achieve reusability in plug and play manner.
Typical UVM Testbench hierarchy:

UVM Agent (UVC):


Encapsulated, ready to use, configurable component (agent can be active or passive).

Reusable – plug and play.

Agent has monitor, driver (if active type agent) and sequencer (if active type agent).

Agents will be kept inside the agent_top, it is called UVC component repository.

UVM environment:
Under the environment component there will be agents, virtual sequencer, scoreboard, uvm subscriber.

Verification components includes (inside the agent class):


Sequence_item – Data item based on the dut protocol.

Sequencer – Routes sequence_item from a sequence to the driver.

Driver – it converts sequence_item into the pin level waggles for DUT.

Monitor – it converts pin level data from the DUT into transaction, for the use in scoreboard, coverage models, etc.

Configuration object – A container object, used to pass information to the agent which affects what it does and how
it is built and connected.

Sequencer and Driver are connected using TLM ports.

Agent configuration – active or passive (only monitor will be there).

UVM Hierarchy:

Classes used for creating test bench hierarchy are called components.

Classes used for stimulus generation are called Objects.

Phases are included in components where executable code has to be written.

Components are connected using TLM ports and Implementation ports.


UVM Factory:
It is a single ton class.

It is a class that manufacture (create) UVM components and objects during run time.

For the factory to create components and objects:

1. Register class type with the factory


2. Creating components and objects using factory.
3. Overriding components and objects, (using factory it can be done, it is kind of polymorphism happening
in the back ground using wrapper id).

Only the derived class can be overridden directly, base/parent class can’t be overridden, as they are virtual type.

Overriding can be done from the top-level hierarchy.

Factory registration:
1. Using factory registration macro:
`uvm_component_utils – for component class.
`uvm_object_utils – for stimulus and sequences.

2. Without using factory registration macro:


An uvm_component_registry wrapper, typedefed to type_id.
A static function to get the type_id.
A function to get the type name.
Syntax:

class my_component extends uvm_component;

typedef uvm_component_registry #(my_component) type_id;


static function type_id get_type();
return type_id::get();
endfunction
function string get_type_name();
return "my_component";
endfunction
endclass

Registering components and objects:


class env1 extends uvm_env;
`uvm_component_utils(env1)
function new(string name = "env1",uvm_component parent=null); // default constructor for the component class
super.new(name,parent);
endfunction
endclass
class env2 #(type T = int,int W=32,string S) extends uvm_env;
`uvm_component_param_utils(env2 #(T,W,S))
T num = W;
function new(string name="env2",uvm_component parent=null);
super.new(name,parent);
endfunction
task run_phase(uvm_phase phase);
$display("printing from the env2 class, the value of num is %0d, bit size of num is %0d, the string is
%s",num,$bits(num),S);
endtask
endclass
class seq #(type T,int N,string S) extends uvm_sequence_item;
`uvm_object_param_utils(seq #(T,N,S))

T num = N;
function new(string name = "seq"); // default constructor for the object class
super.new(name);
endfunction

function void println;


$display(" printing from the seq class the value of N is %0d, the string S is %s",N,S);
$display("num is %0d",num);
endfunction
endclass

class test extends uvm_test;


`uvm_component_utils(test)
env1 env1h;
env2 #(bit [10:1],2,"bye") env2h;
seq #(int,10,"seq") seqh;
function new(string name = "test",uvm_component parent=null);
super.new(name,parent);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
env1h = env1::type_id::create("envh",this);
env2h = env2 #(bit [10:1],2,"bye")::type_id::create("env2h",this);
seqh = seq #(int,10,"seq")::type_id::create("seqh"); // it is an object so, it is not shown in the topology. as
it is not part of the tb, it is part of stimulus.
endfunction
endclass
Factory “create” method is used to create an instance of class.

There are two types of factory overriding methods:


1. Global overriding
set_type_override_by_type(compa::get_type(), compb::get_type(),0); // bit replace default value is 1
2. Instance overriding
set_inst_type_override_by_type("*",compa::get_type(),compb::get_type(),0);//* indicates relative path
above are the syntax for “override_by_type”, and “override_by_name” variant is also available in UVM.

type_id is an alias of uvm_*_registry class. (here * can be component or object)

create method is defined in the uvm_*_registry class.

If bit replace = 0, then existing override will not be replaced by the new one. in other words, if there is already an
override for a given type or instance, the new override request will be ignored, preserving the existing override.
Stimulus modelling:
The methods like print, copy, compare,clone etc can be enabled by using field macros or by overriding the
corresponding virtual methods like do_print, do_copy, do_compare.
Transaction class has to be extended from the uvm_sequence_item class.
It represents the main transaction input to the DUT based on the protocol of the Design.
Steps to define stimulus:
1. Derive a data item class from uvm_sequence_item base class
2. Register with the factory using `uvm_object_utils macro
3. Define a constructor for the data item.
4. Add control fields for the items such as constraints
5. UVM field macros to enable printing, copying, comparing etc.

Field macros are used to register the class properties(fields) so, that these properties will be enabled for various pre-
defined methods like copy, compare and print etc.

Handy list of field macros that we use in between:


`uvm_*_utils_begin(class_name) // here * can be component or object.

`uvm_*_utils_end

`uvm_field_int(fieldName,UVM_ALL_ON+UVM_NOCOPY) Implements data operation for any packed integral


properties.
`uvm_field_enum(enum_name,fieldName,UVM_ALL_ON) Implement data operation for the enum property.
`uvm_field_string(fieldName,UVM_ALL_ON) Implement data operation for the string property.
`uvm_field_real(fieldName,UVM_ALL_ON) Implement data operation for the real property.
`uvm_field_event(fieldName, UVM_ALL_ON) Implement data operation for the event property.
`uvm_field_object(filedName, UVM_ALL_ON) Implements data operation for the object-based
property.
Note: refer-> https://www.systemverilog.io/verification/uvm-field-macros/
Flag arguments:
Flags Description
UVM_ALL_ON (used for shallow copying) Set all operations on (default).
UVM_DEFAULT (used for deep copying) Use the default flag settings
UVM_NOCOPY Do not copy this field
UVM_NOCOMPARE Do not compare this field
UVM_NOPRINT Do not print this field
UVM_NODEFPRINT Do not print the field if it is the same as it is
UVM_NOPACK Do not pack or unpack this field.
Built-in methods:
Methods called by user Virtual methods Purpose
copy do_copy Performs copy of an object, deep or shallow copy depends on
flag arguments.
clone do_copy First creates an object and later performs the deep copy.
compare do_compare Compares one object to another of the same type.
convert2string - Returns a string representation of the object.
print do_print Prints the result of convert2string to the screen, default is table
sprint do_print Returns the result of the convert2string.
record do_record Handles transaction reccordings.
pack do_pack Compresses object contents into a bit format
unpack do_unpack Converts a bit format into the data object format.
Methods called by the user are not recommended, as they inject lot of unnecessary code into our design.
methods:
1. Copy:
wr_copy_xtnh.copy(wr_xtnh); // here ‘wr_xtnh’ data will be copied to the object of ‘wr_copy_xtnh’
2. Clone:
$cast(wr_xtn2,wr_xtn1.clone()); //copy object of ‘wr_xtn1’ will created and assigned to ‘wr_xtn2’ handle.
3. Compare:
if(! w_xtn1.compare(w_xtn2)) $display(“compare failed”);
else $display(“compare success”);
4. Print:
Wr_xtn.print(uvm_default_tree_printer);// now we override default type of printing initial it is table printer
$display(“hello : %p”,wr_xtn.sprint()); // it is one way of printing
Printer format:
1. uvm_default_tree_printer
2. uvm_default_table_printer // it is the default format
3. uvm_default_line_printer

Virtual methods:
virtual function void do_copy(uvm_object rhs);
packet2 rhs_;
if(! $cast(rhs_,rhs)) `uvm_fatal("do_copy","cast of the rhs object failed")
super.do_copy(rhs);
this.num = rhs_.num;
this.names = rhs_.names;
this.packet1_result = rhs_.packet1_result;
endfunction

virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);


packet2 rhs_;
if(! $cast(rhs_,rhs)) begin
`uvm_error("do_compare","cast of the rhs object failed") // here we should
not give the 'uvm_fatal'
return 0;
end
return super.do_compare(rhs,comparer) && this.num == rhs_.num && this.names ==
rhs_.names && this.packet1_result == rhs_.packet1_result;
endfunction
Radix format Description
UVM_BIN Print/record the field in binary. (base 2)
UVM_DEC Print/record the field in decimal.
UVM_UNSIGNED Print/record the field in unsigned decimal
UVM_OCT Print/record the field in octal (base 8)
UVM_HEX Print/record the field in hexadecimal (base 16)
UVM_STRING Print/record the field in string format.
UVM_TIME Print/record the field in time format.

virtual function void do_print(uvm_printer printer);


super.do_print(printer);
printer.print_field("num",num,32,UVM_DEC);
printer.print_object(“object_name”,object_handle);
printer.print_string("names",names);
// printer.print_generic(
"property_name","enum_name",$bits(property_name),property_name.name);
printer.print_generic("packet1_result","result",$bits(packet1_result),packet1_resu
lt.name);
endfunction

UVM phases:
There are 3 major groups of phases,
1. build phases -> tb is configured and constructed.
2. Run phases/ run time phases -> where time is consumed in running the test cases.
3. Clean up phases -> where the results of the test case are collected and reported.

Build phase and final phase are top-down approach, remaining phases are
bottom-up approach except run phase.

Run phase parallel execution, it is time consuming phase. It has sub-run phases.

Shotcut-> BC ES R EC RF

uvm_test_top is the handle of test class.

Object class does not have phases, only the component classes has the phases.

Calling run_test(), construct the uvm environment root component and then
initiates the uvm phasing.

Phases are used for synchronization.

If number of raised objections is equal to number of dropped objection then our


TB moves to extract_phase.

End of test – objections:


To specify at what point of time our TB can processed to the extraction_phase from the run_phase, for this we use
objections. These objections are raised and dropped in the ‘run_phase’ if it is a component. If object then it can be
done in ‘body’ method.

For Components: For Objects:


phase.raise_objection(this); starting_phase.raise_objection(this);
phase.drop_objection(this); starting_phase.drop_objection(this);
Note: if we forgot to drop the objections, then simulator will wait for 92x1011ns and after throws ‘fatal error’.

Delay phase end:


Last chance for raising the objection, it is done using ‘phase_ready_to_end’ method.

Note: The delay in the ‘run_phase’ of components has to be more than the delay in the test class ‘run_phase’
class collector1 extends uvm_component; // in this class we used the 'delay phase end'
`uvm_component_utils(collector1) // refer the code-> objections_delay.sv

int busy = 1, ending = 0; // for "Delay phase end"

function new(string name="collector1",uvm_component parent=null);


super.new(name,parent);
endfunction

task run_phase(uvm_phase phase);


forever begin ////////////////////////////// this, like convention is useful in 'monitor class'
#100; // time should be consumed more than the 'test' class run_phase
if(ending) begin
busy = 0;
`uvm_info(get_type_name,"in the \"collector1\" dropped objection run_task",UVM_NONE)
phase.drop_objection(this);
end
end
endtask

function void phase_ready_to_end(uvm_phase phase);


if(phase.get_name == "run") begin
if(busy) begin
ending = 1;
`uvm_info(get_type_name,"in the \"collector1\" raised objection run_task",UVM_NONE)
phase.raise_objection(this);
end
end
endfunction

Reporting mechanism:
These are two types -> (i) reporting methods (not recommended), (ii) using uvm_macros

It helps in debugging the environment with many agents, control the display messages using their names and can
filter the displaying messages.

Severity for messages (it indicates the importance of messages):


Fatal – UVM_DISPLAY+UVM_EXIT (termination of simulation)
Error – UVM_DISPLAY + UVM_COUNT
Warning – UVM_DISPLAY
Info – UVM_DISPLAY
(UVM_NO_ACTION – do nothing, UVM_LOG – write report to file) // these are actions for the severities.

Verbosity (it for filtering the messages):


Default verbosity threshold of uvm compiler is UVM_MEDIUM.

Verbosity levels -> UVM_NONE (least verbosity), UVM_LOW, UVM_MEDIUM, UVM_HIGH, UVM_FULL, UVM_DEBUG
(highest verbosity).

Messages with a verbosity greater than the maximum are ignored.

UVM_macros:
`uvm_info(“id”,$sformatf(“bjjgwjgw %d”,val),UVM_NONE)

`uvm_warning(“id”,”iwgiwjggg”) // default verbosity is UVM_NONE

`uvm_error(“id”,”boigoiwgoi”) // default verbosity is UVM_NONE

`uvm_fatal(“id”,”message”) // default verbosity is UVM_NONE

Setting the Verbosity of the compiler:


./simv+UVM_VERBOSITY=UVM_HIGH (*********It is inline command for the questa sim **********)

./simv+uvm_set_verbosity=component_name,id,UVM_HIGH,phase_name,option_all_time

Comph.set_report_severity_action(UVM_WARNING,UVM_DISPLAY+UVM_EXIT);

Comph.set_report_verbosity_level(UVM_LOW);

Setting verbosity in the transaction class:

Write_xtn Xtn; // Refer -> reporting_mechanism.sv

Initial begin
Xtn = new(“Xtn”); // this setting verbosity can be done in component classes only.
Xtn.set_report_verbosity_level(UVM_LOW); //it is present inside the uvm_report_object
TLM (Transaction Level Modeling):
It is all about interoperability (mixed language verification environment) and reusability (maximizes reuse and
minimizes the time and effort).

TLM – 2.0, an OSCI (open system initiative) initiative.

Port: it initiates a transaction and forward it to the higher level of abstraction.

Export/implementation port: it takes from higher level of abstraction and forwards to another abstraction. It
forwards transaction from one component to another component. Fifo’s has the implementation ports.

Analysis port: it broad castes the data, and later it is received by the single or multiple components where the
analysis implementation is used.

Analysis export: it

In TLM the ‘initiator’ is like a host and the ‘target’ is like the audience.

‘Ports’ have the set of methods like the get(), put(), peek() etc,. and ‘exports’, ‘imp’ has the implementation of the
above ports.

By using TLM the following cases are possible, but the 4th case is not possible:

cases Generator Driver Here in this the case 4 is not possible with the TLM.
1 Initiator Target
By using the mail box we can do the case1, and case 2
2 Target Initiator
3 Initiator Initiator only but the case 3 is not possible with the mail box.
4 Target Target
Blocking get port:
class consumer extends uvm_component;
`uvm_component_utils(consumer)
uvm_blocking_get_port #(trans) get_port;

trans transh;
function new(string name = "consumer", uvm_component parent=null);
super.new(name,parent);
get_port = new("get_port",this); // in ports we will call the method.
endfunction

task run_phase(uvm_phase phase);


get_port.get(transh);
transh.print();
endtask

endclass

class producer extends uvm_component;


`uvm_component_utils(producer)
uvm_blocking_get_imp #(trans,producer) get_imp;
function new(string name = "producer", uvm_component parent);
super.new(name,parent);
get_imp = new("get_imp",this);
endfunction

task get(output trans x); // in imp we will define the methods.


trans transh = trans::type_id::create("transh");
transh.name = "data sent from the producer";
x = transh;
endtask
endclass
bellow code is written inside the test class:
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
conh.get_port.connect(proh.get_imp);
endfunction
blocking put port:
class producer2 extends uvm_component;
`uvm_component_utils(producer2)
uvm_blocking_put_port #(trans) put_port2;
trans transh;

function new(string name="producer2",uvm_component parent=null);


super.new(name,parent);
put_port2 = new("put_port2",this);
transh=trans::type_id::create("transh");
endfunction

task run_phase(uvm_phase phase);


transh.names = "data from producer2";
`uvm_info(get_type_name,$sformatf("sending data, num = %0d, string
names=%s",transh.num,transh.names),UVM_NONE)
put_port2.put(transh); // blocking type
endtask
endclass

class consumer2 extends uvm_component;


`uvm_component_utils(consumer2)
uvm_blocking_put_imp #(trans,consumer2) put_imp2;

function new(string name="consumer2",uvm_component parent=null);


super.new(name,parent);
put_imp2 = new("put_imp2",this);
endfunction

virtual task put(trans transh);


`uvm_info(get_type_name,$sformatf("received data, num = %0d, string=%s",transh.num,transh.names),UVM_NONE)
endtask
// virtual function bit put(trans transh); // it is also working for blocking type.
// `uvm_info(get_type_name,$sformatf("received data, num = %0d, string=%s",transh.num,transh.names),UVM_NONE)
// return 1;
// endfunction
endclass

TLM fifo:
// ====================================== tlm fifo ===============================
// |-----------| |-----------|
// | generator | |----| | driver |
// |(initiator)|[]==============O|fifo|O=================>[]|(initiator)|
// | | |----| | |
// |-----------| put_export get_export |-----------|
// put port get port
//=================================================================================
class generator extends uvm_component;
`uvm_component_utils(generator)
uvm_blocking_put_port #(trans) gen_put_port;
trans transh;

function new(string name = "generator", uvm_component parent);


super.new(name,parent);
gen_put_port = new("gen_put_port",this);
transh = trans::type_id::create("transh",this);
endfunction

task run_phase(uvm_phase phase);


transh.name = "sending the data from the generator";
repeat(1)
gen_put_port.put(transh);
endtask

endclass

class driver extends uvm_component;


`uvm_component_utils(driver)
uvm_blocking_get_port #(trans) drv_get_port;
trans transh;

function new(string name = "driver", uvm_component parent);


super.new(name,parent);
drv_get_port = new("drv_get_port",this);
endfunction

task run_phase(uvm_phase phase);


repeat(1)
drv_get_port.get(transh);
transh.print();
endtask

endclass

class test extends uvm_test;


`uvm_component_utils(test)
uvm_tlm_fifo #(trans) fifoh;

generator genh;
driver drvh;

function new(string name = "test", uvm_component parent);


super.new(name,parent);
fifoh = new("fifoh",this);
genh = generator::type_id::create("genh",this);
drvh = driver::type_id::create("drvh",this);
endfunction

task run_phase(uvm_phase phase);


phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask

function void connect_phase(uvm_phase phase);


super.connect_phase(phase);
genh.gen_put_port.connect(fifoh.put_export);
drvh.drv_get_port.connect(fifoh.get_export);
endfunction

function void end_of_elaboration_phase(uvm_phase phase);


super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
endclass

Analysis interface:
It has single non-blocking function – write() (it is possible made as non-blocking, as here we are broad casting the
data)

It supports single ‘analysis port’ and multiple ‘analysis exports’.

Analysis port may be connected to zero, one or many analysis exports.

Analysis port and export connection:


class monitor extends uvm_monitor;
`uvm_component_utils(monitor)
uvm_analysis_port #(trans) analysis_port; // through this port data is broad casted.
trans transh;

function new(string name = "monitor", uvm_component parent);


super.new(name,parent);
analysis_port = new("analysis_port",this);
transh = trans::type_id::create("transh");
endfunction
task run_phase(uvm_phase phase);
transh.name = "sending the data from the monitor via analysis port";
analysis_port.write(transh);
endtask
endclass
class scoreboard extends uvm_scoreboard;
`uvm_component_utils(scoreboard)
uvm_analysis_imp #(trans,scoreboard) analysis_imp; //through this implementation port data is received.

function new(string name = "scoreboard", uvm_component parent = null);


super.new(name,parent);
analysis_imp = new("analysis_imp",this);
endfunction

function void write(input trans transh); // if we remove 'input' also it will work.
transh.print();
$display("from the scoreboard");
endfunction
endclass

class coverage_block extends uvm_component;


`uvm_component_utils(coverage_block)
uvm_analysis_imp #(trans,coverage_block) analysis_imp; //through this implementation port data is received.

function new(string name = "coverage_block", uvm_component parent = null);


super.new(name,parent);
analysis_imp = new("analysis_imp",this);
endfunction

function void write(input trans transh); // if we remove 'input' also it will work.
transh.print();
$display("from the coverage_block");
endfunction
endclass

class test extends uvm_test;


`uvm_component_utils(test)
monitor monh;
scoreboard sbh;
coverage_block covh;

function new(string name = "test", uvm_component parent);


super.new(name,parent);
monh = monitor::type_id::create("monh",this);
sbh = scoreboard::type_id::create("sbh",this);
covh = coverage_block::type_id::create("covh",this);
endfunction

task run_phase(uvm_phase phase);


phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
monh.analysis_port.connect(sbh.analysis_imp);
monh.analysis_port.connect(covh.analysis_imp);
endfunction
endclass

Analysis fifo and UVM subscriber :


class monitor extends uvm_component;
`uvm_component_utils(monitor)
uvm_analysis_port #(trans) analysis_port;
trans transh;

function new(string name = "monitor", uvm_component parent);


super.new(name,parent);
analysis_port = new("analysis_port",this);
transh = trans::type_id::create("transh");
endfunction

task run_phase(uvm_phase phase);


repeat(1) begin
transh.name = "sending data from the monitor";
analysis_port.write(transh);
end
endtask
endclass

class scoreboard extends uvm_component;


`uvm_component_utils(scoreboard)
uvm_tlm_analysis_fifo #(trans) fifoh;
trans transh;

function new(string name = "scoreboard", uvm_component parent);


super.new(name,parent);
fifoh = new("fifoh",this);
endfunction

task run_phase(uvm_phase phase);


forever begin
fifoh.get(transh);
transh.print();
$display("printing from the scoreboard");
end
endtask
endclass

class subscriber extends uvm_subscriber #(trans);


`uvm_component_utils(subscriber)

function new(string name="subscriber",uvm_component parent);


super.new(name,parent);
endfunction

virtual function void write(trans t); // name should be "t" only.


t.print();
`uvm_info(get_type_name,"in the sub class",UVM_NONE)
endfunction
endclass

class test extends uvm_test;


`uvm_component_utils(test)
monitor monh;
scoreboard sbh;
subscriber subh;

function new(string name = "test", uvm_component parent);


super.new(name,parent);
monh = monitor::type_id::create("monh",this);
sbh = scoreboard::type_id::create("sbh",this);
subh = subscriber::type_id::create("subh",this);
endfunction

task run_phase(uvm_phase phase);

endtask
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
monh.analysis_port.connect(sbh.fifoh.analysis_export);
monh.analysis_port.connect(subh.analysis_export);
endfunction
endclass

UVM Configuration:
Configuration data base -> maintains associative memory; associative array look up table.

‘uvm_config_db’ it is a parameterized class, here the parameter may be any type like class object or builtin data
types like int, bit, byte --- etc,.
If we use the ‘uvm_config_db’ with same name then the simulator will consider the last one only.

We have to use the ‘set’ and ‘get’ static methods in the ‘build_phase’ only. Here the ‘set’ method is to set the data
base and get method is to access the data base.

Syntax of config data base:


void uvm_config_db #(type T=int)::set(uvm_component cntxt, string inst_name, string field_name, T value);

bit uvm_config_db #(type T=int)::get(uvm_component cntxt, string inst_name, string field_name, ref T value);

Here consider first 3 arguments we can store in associative array, the 3 arguments are uvm_component context,
string instance_name, string field_name, lastly variable.

Eg: uvm_config_db #(ram_cfg)::set(this,”*”,”ram_config”,tb_cfg); // if we set in the module then use ‘null’ as scope.

uvm_config_db #(ram_cfg)::get(this,””,”ram_config”,tb_cfg_drv);

Hierarchical path:
*.agent* // visibility from agent[1] onwards to bottom of the test bench

*.agent[1]* // visibility will be after agent[1]

*.agent[1].* // visibility will be inside the agent[1]

* // visibility will bottom to the test bench components from where the config_db is set.

Virtual interface configuration:


In the top module ‘set’ the virtual interface configuration and ‘get’ it at test class and again ‘set’ this in the agent’s
configuration objects.
interface vif();
// interface code here
endinterface
class drv_cfg extends uvm_object;
`uvm_object_utils(drv_cfg)
virtual vif vifh;

function new(string name="drv_cfg");


super.new(name);
endfunction
endclass
//-----------------------------------------------------------------------------------------------------------
class driver extends uvm_driver;
`uvm_component_utils(driver)
drv_cfg cfgh;
virtual vif vifh;

function new(string name="driver", uvm_component parent);


super.new(name,parent);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
if(! uvm_config_db #(drv_cfg)::get(this,"","cfgh",cfgh)) `uvm_fatal("config","failed to get")
endfunction

function void connect_phase(uvm_phase phase);


super.connect_phase(phase);
this.vifh = cfgh.vifh;
endfunction
endclass
//---------------------------------------------------------------------------------------------------------
class test extends uvm_test;
`uvm_component_utils(test)
driver drvh;
drv_cfg cfgh;
function new(string name = "test", uvm_component parent);
super.new(name,parent);
drvh = driver::type_id::create("drvh",this);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
cfgh = drv_cfg::type_id::create("cfgh");
if(! uvm_config_db #(virtual vif)::get(this,"","vif",cfgh.vifh)) `uvm_fatal("config","failed to get")

uvm_config_db #(drv_cfg)::set(this,"*drvh","cfgh",cfgh);
endfunction

function void end_of_elaboration_phase(uvm_phase phase);


super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction

endclass
//------------------------------------------------------------------------------------------------------
module top;
vif vifh();
// dut instantiation

initial begin
uvm_config_db #(virtual vif)::set(null,"*","vif",vifh);
run_test("test");
end
endmodule

TB components:
UVM Sequencers:
It is a non-virtual class, so we can make our own sequencer (which extends from uvm_sequencer #(trans) ) or we can
use predefined uvm_sequencer in the agent. Here bidirection communication is possible.

It routes the sequence_item’s to the driver. It also does the sequence arbitration (it means when multiple sequences
are starting, that time sequencer randomly pick one and first send it to the driver.) it manages the sequences.

Sequencer and driver connection through the built-in TLM ports, driver has default ‘sequence_item_port’, sequencer
has the ‘sequence_item_export’. the connection is -> drvh.seq_item_port.connect(seqrh.seq_item_export);

In sequencer code, factory registration and parameterization of uvm_sequencer with ‘transaction’ class is enough.

To create user sequencer extends ‘uvm_sequencer’ base class, which is parameterized by request and response item
types. So, that the arbitration and routing logics will be defined in the base class.

UVM Driver:
Fetches data repeatedly from sequencer. Driver dut based on the protocol using the virtual interface.
To create driver:
1. Derive a ‘driver’ from the ‘uvm_driver’ base class and parameterized with ‘transaction’ class.
2. Declare ‘virtual interface’ to connect the driver to the ‘dut’.
3. Obtain the next data item from the sequencer and execute it according to the protocol this is written in
driver ‘run_phase’.
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive_to_dut(req);
seq_item_port.item_done();
end
endtask
The uvm_seq_item_pull_port class of driver has two methods

1. get_next_item() – Blocking method – It will wait until arrival of packet.


2. try_next_item() – Non-Blocking method – generally it is not used.
3. get() – blocking method – it will implicity call the ‘item_done’, where as in ‘get_next_item’ and
‘try_next_item’ we have to explicitly call the ‘item_done’.

Sending data back to the sequencer:


When we want to send the data from the driver to the sequencer. It is used when some sequences, a generated
value depends on the response to previously generated data.

This can be achieved by: (here rsp is handle of transaction class type)

1. .item_done(); -> Acknowledgement, saying driving completed. Won’t send response to sequencer.
2. .item_done(rsp); -> non-blocking type, sends response to sequencer plus tells driving completed.
3. .put_response(rsp); -> blocking type, sends response to sequencer plus tells driving completed after
sequencer again has to give intermediate response using method ‘.get_response(rsp);’
4. rsp_port.write(rsp) -> built-in analysis port in driver, so driver can broad cast the response back to the
sequencer.

Note: before providing the response, the response’s sequence and transaction id must be set to correspond to
the request transaction using rsp.set_id_info(req)

UVM Sequence:
Sequence just randomize the data only.

Minimize the protocol/environment knowledge requirements. In sequence we are not following any protocol for
operation. So, it is simply we separating it from components and we call it as object.

Generating stimulus based on constraints, stimulus generation on the fly or at time zero.

Can nest sequences. Here in sequences has body, prebody, postbody tasks. m_sequencer, it is the handle of
sequence base class. Inside the uvm_sequence, uvm_driver, uvm_sequencer has rsp & req which are handles of
transaction class.

We have to create the p_sequencer if we want to use it. p_sequencer handle not even present in out TB until we
declare it manually.

The main difference between m_seqeuncer and p_sequencer in one line is the parent and child class handle types.
m_sequencer is of parent type and p_sequencer is of child type.

m_seqeuncer is the handle of a base class type “uvm_sequencer_base” , which is parent class of "uvm_sequencer".

For p_sequencer handle to declare we call macro inside sequence like this, `uvm_declare_p_sequencer(sequencer).
This macro declares p_sequncer handle of type sequencer which is passed and dynamically casts p_sequencer to
m_sequencer. If we don’t call this macro p_sequencer does not exist. We use p_sequencers inside
our sequences to access the methods or properties of user defined sequencer if needed.
Sequence item flow:
Sequence sequencer driver:

task body(); task run_phase(uvm_phase phase);


repeat(10) begin forever begin
start_item(req); // wait for request from driver seq_item_port.get_next_item(req); // wait for seq’s item
assert(req.randomize()); user_defined_task(req);
finish_item(req); seq_item_port.item_done(); //it is only non-blocking method.
end end
endtask endtask
In test class:

task run_phase(uvm_phase phase);


phase.raise_objection(this);
seqh.start(envh.agt_toph.agth.seqrh); // it will start the sequence to the driver.
phase.drop_objection(this);
endtask
Starting sequence item:
Using m_sequencer we can start one sequence or multiple sequences.
class seq extends uvm_sequence #(trans);
`uvm_object_utils(seq)

function new(string name="seq");


super.new(name);
endfunction
task pre_body();
req = trans::type_id::create("req");
endtask
task body(); //////////////////////////////////////////// step:1
repeat(2) begin
start_item(req);
assert(req.randomize());
finish_item(req);
end
endtask
task post_body();
`uvm_info(get_type_name,"sequences finished",UVM_NONE)
endtask
endclass
///////////////////////////////////////////////////////////////
class main_seq extends uvm_sequence #(trans); // example of starting the sequence_item
`uvm_object_utils(main_seq)
seq seqh;

function new(string name="main_seq");


super.new(name);
seqh=seq::type_id::create("seqh");
endfunction

task body();
repeat(2) begin
`uvm_info(get_type_name,"starting the seq class to the m_sequencer",UVM_NONE) ///////////// step 2.1
seqh.start(m_sequencer); // it will call the seqh, or we can call multiple sequences.
end
endtask
endclass
/////////////////////////////////////////////////////////////
class driver extends uvm_driver #(trans);
`uvm_component_utils(driver)

function new(string name="driver",uvm_component parent=null);


super.new(name,parent);
endfunction

task run_phase(uvm_phase phase); //////////////////////////////// step: 2


forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name,"printing from the driver class",UVM_NONE)
seq_item_port.item_done();
end
endtask
endclass
///////////////////////////////////////////////////////////
// class sequencer extends uvm_sequencer #(trans); // method 1 of using the uvm_sequencer
// `uvm_component_utils(sequencer)
// function new(string name="sequencer",uvm_component parent=null);
// super.new(name,parent);
// endfunction
// endclass
class env extends uvm_env;
`uvm_component_utils(env)
driver drvh;
// sequencer seqrh; // method 1 of using the uvm_sequencer
uvm_sequencer #(trans) seqrh; //method 2 of using the uvm_sequencer, another approach

function new(string name="env",uvm_component parent=null);


super.new(name,parent);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
drvh = driver::type_id::create("drvh",this);
// seqrh = sequencer::type_id::create("seqrh",this);//method-1 of using uvm_sequencer
seqrh = uvm_sequencer #(trans)::type_id::create("seqrh",this); // method 2 of using the uvm_sequencer
endfunction

function void connect_phase(uvm_phase phase);


super.connect_phase(phase);
drvh.seq_item_port.connect(seqrh.seq_item_export); ////////// step: 3
endfunction
endclass
//////////////////////////////////////
class test extends uvm_test;
`uvm_component_utils(test)
env envh;
seq seqh;
main_seq main_seqh;

function new(string name="test",uvm_component parent=null);


super.new(name,parent);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
envh = env::type_id::create("envh",this);
main_seqh=main_seq::type_id::create("main_seqh");
endfunction

task run_phase(uvm_phase phase);


phase.raise_objection(this);
main_seqh.start(envh.seqrh);//////////////////////////////////////// step 2.2
#2;
phase.drop_objection(this);
endtask
endclass

Using macros: starting sequence item without using the ‘start method’
If the user doesn’t want to use ‘start_item & finish_item’ in the sequence class that time we use these
“`uvm_do(req); & `uvm_do_with(req,<condition>);”. Here the disadvantage is it doesn’t call the ‘pre_body’ and
‘post_body’ methods of the sequence. So, it is not recommended.

For a sequence_item to start on a sequencer we have two methods:

1. `uvm_do();
2. `uvm_do_with(); // here we can pass the in-line command.
3. class do_macro_seq extends uvm_sequence #(trans);
4. `uvm_object_utils(do_macro_seq)
5. seq seqh;
6. function new(string name="do_macro_seq");
7. super.new(name);
8. endfunction
9.
10. task body();
11. repeat(2) begin
12. `uvm_info(get_type_name,"starting the seq class using do_macro",UVM_NONE)
13. `uvm_do(seqh); //////////////////// step 3.1
14. end endtask
15. endclass
16.
17. class do_macro_seq1 extends uvm_sequence #(trans);
18. `uvm_object_utils(do_macro_seq1)
19.
20. function new(string name="do_macro_seq1");
21. super.new(name);
22. endfunction
23.
24. task body();
25. repeat(2) begin
26. // `uvm_do(req); //////////////////// step 4.1, without using the inline constraint.
27. `uvm_do_with(req,{a == 0;}); //////////////////// step 4.1, using inline constraint.
28. `uvm_info(get_type_name,"sequence item using do_macros",UVM_NONE)
29. req.print();
30. end endtask
31. endclass
Sequence configuration:
If we want to use the configuration data base in the sequence that time we go for this method.

Here we set the config_db in the ‘test class’ and we get the config_db in the sequence class, where we want to
implement. In sequence class in ‘pre_body’ method we get the config_db.
class seq_config extends uvm_object;
`uvm_object_utils(seq_config)

int repeat_count;

function new(string name="seq_config");


super.new(name);
endfunction
endclass
///////////////////////////////////////////////////////////
class seq_config_sequence extends uvm_sequence #(trans);
`uvm_object_utils(seq_config_sequence)
seq_config m_seq_configh;

function new(string name="seq_config_sequence");


super.new(name);
endfunction

task pre_body();
if(! uvm_config_db #(seq_config)::get(null,get_full_name(),"seq_config",m_seq_configh)) //here we use 'null'
as there is no hierarchy for the sequence class. Here 'get_full_name' fetches the instance path for the sequence based
on the sequencer on which this sequence is started.(like envh.agth.seqrh)
`uvm_info(get_type_name(),"failed to get",UVM_NONE)
endtask
task body();
repeat(m_seq_configh.repeat_count) begin
`uvm_do(req);
`uvm_info(get_type_name,"getting the configuration in the sequence class",UVM_NONE)
req.print();
end
endtask
endclass
///////////////////////////////////////////////////////////
class test5 extends test; // getting configuration to the transient class(objects), where there is no hierarchy
`uvm_component_utils(test5)
seq_config seq_configh;

function new(string name="test5", uvm_component parent=null);


super.new(name,parent);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
seq_configh=seq_config::type_id::create("seq_configh");
seq_configh.repeat_count = 3;
uvm_config_db #(seq_config)::set(this,"*","seq_config",seq_configh);

seq_config_sequenceh = seq_config_sequence::type_id::create("seq_config_sequenceh");
endfunction

task run_phase(uvm_phase phase);


phase.raise_objection(this);
seq_config_sequenceh.start(envh.seqrh); ////////////////////////////////// step 4.2
#2;
phase.drop_objection(this);
endtask
endclass

Setting default sequence:


Without calling the ‘start’ method from ‘test’ we can start, using default sequence. For this we ‘set default
sequences in the test class of ‘start_of_simulation phase’. Here we don’t use ‘get’ method

Note: we should ‘set’ it before ‘run_phase’ that’s why we are using start_of_simulation_phase.

Disadvantage is we don’t have time control, because this default sequence start, then after ‘run_phase’ we start
execution parallely.
So, if we want to implement this ‘default sequence’ then in the driver use ‘sub run_phases’ instead of run_phases.
And in the default sequence, give path to ‘reset phase’ and for remaning code write in the ‘main_phase’
class default_seq extends uvm_sequence #(trans);
`uvm_object_utils(default_seq)

function new(string name="default_seq");


super.new(name);
endfunction

task body();
if(starting_phase != null) starting_phase.raise_objection(this,"starting my_seq"); // sequence objection
`uvm_info(get_type_name,"inside of the default sequence",UVM_NONE) // here we have to write the body of the
sequence.
`uvm_info(get_type_name,"outside of the default sequence",UVM_NONE)
if(starting_phase != null) starting_phase.drop_objection(this,"ending my_seq");
endtask
endclass
///////////////////////////////////////////////////////
class test extends uvm_test; // here default sequence setting and topology
`uvm_component_utils(test)
env envh;
default_seq default_seqh;

function new(string name="test",uvm_component parent=null);


super.new(name,parent);
endfunction

function void build_phase(uvm_phase phase);


super.build_phase(phase);
envh = env::type_id::create("envh",this);
endfunction

function void start_of_simulation_phase(uvm_phase phase); // here we will start the default sequence
super.start_of_simulation_phase(phase);
// uvm_config_db
#(uvm_object_wrapper)::set(this,"envh.seqrh.run_phase","default_sequence",default_seq::type_id::get()); // using
wrapper

default_seqh = default_seq::type_id::create("default_seqh"); // using instance then object has to be created.


uvm_config_db #(uvm_sequence_base)::set(this,"envh.seqrh.run_phase","default_sequence",default_seqh); // using
instance
endfunction

function void end_of_elaboration_phase(uvm_phase phase); // for topology printing.


super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
endclass

sequence library: collection of sequences/transactions that are registered in sequence library.


UVM provides a class for randomly creating and running sequences. This class is called uvm_sequence_library

The uvm_sequence_library is a sequence that contains a list of registered sequence types.

The uvm_sequnece_library class is inherited from uvm_sequence which means that an instance of a sequence library
is also a sequence.

Similar, to uvm_sequence, uvm_sequence_library is also a parameterized class which is parametrized with req and
rsp which is of uvm_sequence_item.

It can be configured to create and execute the registered sequences any number of times using one of several
modes of operation, including a user-defined mode.

Note: The sequences that do not require driver access or execute sequence_items using “start_item” and
“finish_item”. It do not have to undergo arbitration. So, if multiple plain sequences are started on a sequencer, they
will be executed in parallel similar to default sequence.
Sequence library mode:
type_def enum{UVM_SEQ_LIB_RAND, UVM_SEQ_LIB_RANDC,

UVM_SEQ_LIB_ITEM, UVM_SEQ_LIB_USER} uvm_sequence_lib_mode; // *_RAND is default.

Here, selection_mode is the handle of uvm_sequence_lib_mode.

*_ITEM is for emit only items, no sequence execution.

*_USER is for apply a user defined random selection algorithm, user can override this method using built-in method
‘select_sequence’ function inside the ‘sequence_library’ class.

Eg: function int unsigned select_sequence(int unsigned max);


------
return ----; // it returns int value
Endfunction
seqlib = seq_library::type_id::create("seqlib"); // seqlib = new("seq_lib");
seqlib.selection_mode = UVM_SEQ_LIB_RANDC; // inside 'test class' build_phase, write this
setting the sequence mode:
1. method-1: when there is no instance of library.

uvm_config_db#(uvm_object_wrapper)::set(this,”env.agent.sequencer.main_phase”,”default_
seqence”,my_seq_lib::get_type());
uvm_config_db(uvm_sequence_lib_mode)::set(this,”env.agent.sequencer.main_phase”,”defaul
t_sequence.selection_mode”,UVM_SEQ_LIB_RANDC);
This above code is written in build_phase of test class or run_phase.

It is used when we want to set it as default sequence library, like without using “seq_lib.start(---)”, without
creating object I want to set default sequence.

2. method-2: when there is instance of a library, with creating object I want to set a default sequence.
class test extends uvm_test; // for sequence_library
`uvm_component_utils(test)
env e;
seq_library seqlib;

function new(input string inst = "test", uvm_component c);


super.new(inst,c);
endfunction

virtual function void build_phase(uvm_phase phase);


super.build_phase(phase);
e = env::type_id::create("e", this);
seqlib = seq_library::type_id::create("seqlib"); // seqlib = new("seq_lib");

seqlib.selection_mode = UVM_SEQ_LIB_RANDC;
seqlib.min_random_count = 5;
seqlib.max_random_count = 9;

seqlib.add_sequence(seq6::get_type()); // it is the extra sequence that we are going to add in the


'run time' or in run_phase of the 'sequence library'
seqlib.print();
endfunction

virtual task run_phase(uvm_phase phase);


uvm_config_db#(uvm_sequence_base)::set(this,"e.a.seqr.run_phase", "default_sequence",seqlib); //
DEFAULT SEQUENCE
// above line or below lines of code either one we have to use.
phase.raise_objection(this);
assert(seqlib.randomize());
seqlib.start(e.a.seqr);
phase.drop_objection(this);
endtask
endclass
3. method-3: without using macro and default sequence. It is generally used.
class seq1 extends uvm_sequence#(transaction);

`uvm_object_utils(seq1)
//uvm_add_to_seq_lib(seq1, seq_library)

function new(string name = "seq1");


super.new(name);
endfunction

virtual task body();


req = transaction::type_id::create("req");
start_item(req);
req.a = 4;
req.b = 4;
finish_item(req);
endtask
endclass

class seq2 extends uvm_sequence#(transaction);


`uvm_object_utils(seq2)
//uvm_add_to_seq_lib(seq2, seq_library)

function new(string name = "seq2");


super.new(name);
endfunction

virtual task body();


req = transaction::type_id::create("req");
start_item(req);
req.a = 5;
req.b = 5;
finish_item(req);
endtask
endclass

class seq3 extends uvm_sequence #(transaction);


`uvm_object_utils(seq3)

function new(string name="seq3");


super.new(name);
endfunction
virtual task body();
req=transaction::type_id::create("req");
start_item(req);
req.a=6;
req.b = 6;
finish_item(req);
endtask
endclass

class seq4 extends uvm_sequence #(transaction);


`uvm_object_utils(seq4)

function new(string name="seq4");


super.new(name);
endfunction
virtual task body();
req=transaction::type_id::create("req");
start_item(req);
req.a=7;
req.b = 7;
finish_item(req);
endtask
endclass

class seq5 extends uvm_sequence #(transaction);


`uvm_object_utils(seq5)

function new(string name="seq5");


super.new(name);
endfunction
virtual task body();
req=transaction::type_id::create("req");
start_item(req);
req.a=8;
req.b = 8;
finish_item(req);
endtask
endclass

class seq6 extends uvm_sequence #(transaction); // it is the extra sequence that we are going to add in the 'run time'
`uvm_object_utils(seq6)

function new(string name="seq6");


super.new(name);
endfunction
virtual task body();
req=transaction::type_id::create("req");
start_item(req);
req.a=9;
req.b = 9;
finish_item(req);
endtask
endclass
////////////////////////////////////////////////////////////
class seq_library extends uvm_sequence_library #(transaction);
`uvm_object_utils(seq_library)
`uvm_sequence_library_utils(seq_library) //needed to populate the sequenc library with any sequences that were
statically registered with it or any of its base classes

function new(string name = "seq_library");


super.new(name);
add_typewide_sequence(seq1::get_type());
add_typewide_sequence(seq2::get_type());
add_typewide_sequence(seq3::get_type());
add_typewide_sequence(seq4::get_type());
add_typewide_sequence(seq5::get_type());

init_sequence_library(); //needed to populate the sequenc library with any sequences that were statically
registered with it or any of its base classes
// assert(seqlib.randomize());
endfunction

endclass
/////////////////////////////////////////////////

class driver extends uvm_driver #(transaction);


`uvm_component_utils(driver)

function new(string name, uvm_component parent);


super.new(name, parent);
endfunction

virtual task run_phase(uvm_phase phase);


forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(), $sformatf("a : %0d b : %0d",req.a,req.b), UVM_NONE);
#10;
seq_item_port.item_done();
end
endtask
endclass
///////////////////////////////////////////////////////////////////////////////////
class agent extends uvm_agent;
`uvm_component_utils(agent)

function new(input string inst = "agent", uvm_component parent = null);


super.new(inst,parent);
endfunction

driver d;
uvm_sequencer#(transaction) seqr;

virtual function void build_phase(uvm_phase phase);


super.build_phase(phase);
d = driver::type_id::create("d",this);
seqr = uvm_sequencer#(transaction)::type_id::create("seqr", this);
endfunction

virtual function void connect_phase(uvm_phase phase);


super.connect_phase(phase);
d.seq_item_port.connect(seqr.seq_item_export);
endfunction

endclass
/////////////////////////////////////////////////////////////////////////////
class env extends uvm_env;
`uvm_component_utils(env)

function new(input string inst = "env", uvm_component c);


super.new(inst,c);
endfunction

agent a;

virtual function void build_phase(uvm_phase phase);


super.build_phase(phase);
a = agent::type_id::create("a",this);
endfunction

endclass
///////////////////////////////////////////////////////////////////
class test extends uvm_test; // for sequence_library
`uvm_component_utils(test)

env e;
seq_library seqlib;

function new(input string inst = "test", uvm_component c);


super.new(inst,c);
endfunction

virtual function void build_phase(uvm_phase phase);


super.build_phase(phase);
e = env::type_id::create("e", this);
seqlib = seq_library::type_id::create("seqlib"); // seqlib = new("seq_lib");

seqlib.selection_mode = UVM_SEQ_LIB_RANDC;
seqlib.min_random_count = 5;
seqlib.max_random_count = 9;
// seqlib.init_sequence_library();
seqlib.add_sequence(seq6::get_type()); // it is the extra sequence that we are going to add in the 'run time' or
in run_phase of the 'sequence library'
seqlib.print();
endfunction

virtual task run_phase(uvm_phase phase);


phase.raise_objection(this);
assert(seqlib.randomize());
seqlib.start(e.a.seqr);
phase.drop_objection(this);
endtask
endclass

sequencer arbitration:
when sequences are executed in parallel, sequencer will arbitrate and pick the sequences accordingly.

function void start_of_simulation_phase(uvm_phase phase); // inside the test class


super.start_of_simulation_phase(phase);
// envh.agth.seqrh.set_arbitration(SEQ_ARB_FIFO); //requests are granted in FIFO Order (default).
// envh.agth.seqrh.set_arbitration(SEQ_ARB_STRICT_FIFO); // requests at highest priority granted in
FIFO Order
// envh.agth.seqrh.set_arbitration(SEQ_ARB_RANDOM); // requests are granted randomly.
envh.agth.seqrh.set_arbitration(SEQ_ARB_STRICT_RANDOM); // requests at highest priority granted in
randomly.
// envh.agth.seqrh.set_arbitration(SEQ_ARB_WEIGHTED); // requests are granted randomly by weight.
// envh.agth.seqrh.set_arbitration(SEQ_ARB_USER); // Arbitration is delegated to the user-defined
function, user_priority_arbitration. That function will specify the next sequence to grant.
`uvm_info(get_type_name(), $sformatf("The current arbitration set is:
%s",envh.agth.seqrh.get_arbitration()),UVM_NONE)
endfunction

task run_phase(uvm_phase phase); // inside the test class


`uvm_info(get_type_name(), $sformatf("The current arbitration set is:
%s",envh.agth.seqrh.get_arbitration()),UVM_MEDIUM) // for priniting the arbitration type.
phase.raise_objection(this);
seq1h = seq1::type_id::create("seq1h");
seq2h = seq2::type_id::create("seq2h");
seq3h = seq3::type_id::create("seq3h");

fork
seq1h.start(envh.agth.seqrh,,100); // here the 3rd argument is ‘priority’
seq2h.start(envh.agth.seqrh,,400); // second argument is ‘parent sequence’ defaultly pointed
to null
seq3h.start(envh.agth.seqrh,,700); // if priority is -1, it indicates no priority.
join
phase.drop_objection(this);
endtask

Sequencer lock and grab methods.

If two sequences have same priority then based on arbitration one is chosen.

Parallel sequence: the sequence that activates the two sequence in parallel.
class concurrent_seq extends uvm_sequence #(write_xtn);//parallel or nested sequences.
`uvm_object_utils(concurrent_seq)
seq1 seq1h;
seq2 seq2h;

function new(string name = "concurrent_seq");


super.new(name);
endfunction

task body();
seq1h = seq1::type_id::create("seq1h");
seq2h = seq2::type_id::create("seq2h");
fork
seq1h.start(m_sequencer);
seq2h.start(m_sequencer);
join
endtask
endclass

uvm_callbacks:
Consider a case, in which the driver is implemented to drive the stimulus generated in the sequence.

Most of the test cases requires the normal behavior of the driver. So, we can make use of this driver.

But, some special test cases may require to delay the transaction driving to the DUT or injecting the errors in the
transactions or sending back the responses to the sequences after completion of driving.

So, these special test cases require different type of driver. i.e., we need to change the functionality of the driver's
run_phase().

Callback mechanism will help us for altering the behavior of the transactor without modifying the transactor.
By, using callbacks we can change the behavior of existing component or object without having a necessary to
modify its source code, we can modify its behavior using call back mechanism. Eg: post_randomize, pre_randomize
etc.

uvm events:
In uvm, events are linked with call back methods, it is the difference in system Verilog events and uvm events.

1. I have 2 agents A & B. agent A drives the control signal and agent B drives data packets according to that
control signal.
2. Now, I want to use event – based synchronization between agent A and B, so that data packet is driven only
after driving the control signals.
3. Consider a case, where there should be a synchronism between driver and monitor.
4. We can manage to establish such synchronism between multiple components in ‘env’ by using
‘uvm_event’(scalar) and ‘uvm_event_pool’(vector).

You might also like