Uvm Concepts
Uvm Concepts
Uvm Concepts
Topics
➢ It supports SV based libraries and also contains set of base classes with methods
defined in it, useful for the development of well constructed, reusable SystemVerilog
based Verification environment.
➢UVM was created by Accellera based on OVM version (Open verification methodology)
➢It supports synchronizing the phases, factory concepts, configuration classes, reporting
mechanisms, TLM classes, utility macros, field macros and other useful mechansms.
➢The sequencer-driver handshake mechanism is taken care of under the hood so that
only stimulus needs to be written.
This methodology should support:
Advantages:
uvm_object: Base of all UVM data and hierarchical classes. Primary role is to define a
set of methods for such common operations such as create, compare, print, copy and
record. They are dynamic entities.
uvm_transaction: Used in stimulus generation and analysis. It is the root class for
transactions and extends uvm_object to include timing and recording interfaces. Simple
transactions derived from uvm_transaction and sequence enabled are from
uvm_sequence_item.
uvm_component: All major testbench components are derived from this corresponding
base class. They exists through out the simulation time.
UVM Architecture
Sequence_item: uvm_sequence_item is of object type. It contains data fields for generating
stimulus. These stimulus are randomized in sequence, which are declared as rand. It defines
the pin level activity.
Data fields represent the following types of information,
EX:
class s_seq_item extends uvm_sequence_item; bit r_w, rst;
Ex:
class s_seq extends uvm_sequence #(s_seq_item);
`uvm_object_utils(s_seq) assert(pkt.randomize());
pkt.r_w=1;
s_seq_item pkt; pkt.print();
finish_item(pkt);
function new(string name="s_seq");
super.new(name); #10
endfunction start_item(pkt);
pkt.r_w=0;
task body(); pkt.print();
finish_item(pkt);
pkt=s_seq_item::type_id::create("pkt"); `uvm_info("seq","seq trans",UVM_NONE);
repeat(10) end
begin
#10; endtask
start_item(pkt); endclass
Sequencer: It is written by extending uvm_sequencer parameterized with sequence_item.
It is responsible for routing the sequence_item between sequence and driver. Sequencer
and driver uses TLM interface to communicate using connect method in agent.
drv.seq_item_port.connect(seqr.seq_item_export);
Ex:
`uvm_component_utils(s_seqr);
endclass
UVM Driver: Written by extending uvm_drviver and inherited from uvm_component. It
has TLM ports and methods for communication between driver and sequencer. It is
parameterized with sequence_item for type of request & type of response.
PASSIVE AGENT:
Ex:
class s_ag2 extends uvm_agent;
`uvm_component_utils(s_ag2) virtual function void build_phase(uvm_phase phase);
s_mon2 mon2; super.build_phase(phase);
mon2=s_mon2::type_id::create("mon2",this);
function new(string name="s_ag2",uvm_component parent); endfunction
super.new(name,parent); endclass
endfunction
ACTIVE AGENT:
Ex:
`uvm_component_utils(s_ag1)
drv=s_drv::type_id::create("drv",this);
s_seqr seqr;
mon1=s_mon1::type_id::create("mon1",this);
s_mon1 mon1;
endfunction
s_drv drv;
virtual function void connect_phase(uvm_phase phase);
function new(string name="s_ag1",uvm_component parent);
super.connect_phase(phase);
super.new(name,parent);
drv.seq_item_port.connect(seqr.seq_item_export);
endfunction
endfunction
virtual function void build_phase(uvm_phase phase);
endclass
super.build_phase(phase);
seqr=s_seqr::type_id::create("seqr",this);
UVM Scoreboard: The user-defined scoreboard is extended from uvm_scoreboard,
uvm_scoreboard is inherited by uvm_component. It will check the correctness of the DUT
by comparing the DUT output with the expected values. It will receive the transactions
from the Monitor via TLM ports & exports.
UVM Environment: The user-defined environment is derived from uvm_env, uvm_env is
inherited from uvm_component. The environment is the container class, It contains one or
more agents, as well as other components such as the scoreboard, top-level monitor, and
checker and connects them.
Ex:
class s_env extends uvm_env; ag1=s_ag1::type_id::create("ag1",this);
`uvm_component_utils(s_env) ag2=s_ag2::type_id::create("ag2",this);
sb=s_sb::type_id::create("sb",this);
s_ag1 ag1; endfunction
s_ag2 ag2;
s_sb sb; virtual function void connect_phase(uvm_phase phase);
function new(string name="s_env",uvm_component parent); super.connect_phase(phase);
super.new(name,parent); ag1.mon1.MON12SB.connect(sb.ip_fifo.analysis_export);
endfunction ag2.mon2.MON22SB.connect(sb.op_fifo.analysis_export);
virtual function void build_phase(uvm_phase phase); endfunction
super.build_phase(phase); endclass
Scoreboard Ex:
UVM_COMPONENT UVM_OBJECT
Phases:
Each components goes through predefined set of phases and it cannot proceed to the next phase until all
components finish their execution in current phase.
Phases are represented by callback methods, A set of predefined phases and corresponding callbacks are
provided in uvm_component. The Method can be either a function or task.
Phases are not present in objects as they don’t present throughout the simulation.
Run_phase(): All the phases are functions but it is a task as it contains a delay elements. At this phase it drives
stimulus to DUT and monitor capture information from DUT based on protocol and it executes in
parallel.
task run_phase(uvm_phase phase);
uvm_objection objection;
super.run_phase(phase);
phase.raise_objection(this);
seq.start(env.agent.sequencer);
phase.drop_objection(this);
`uvm_info("RUN", $sformatf("run_phase of %s", get_name()), UVM_LOW)
endtask: run_phase
Extract phase: Extract and process information from scoreboard and functional coverage monitors and it executes
in bottom up manner `uvm_info("EXTRACT", $sformatf("extract_phase of
function void extract_phase(uvm_phase phase); %s", get_name()), UVM_LOW)
super.extract_phase(phase); endfunction: extract_phase
Check phase: Checks if the DUT behaved correctly and identifies errors that may have occurred during the
execution and it executes in bottom-up manner
function void check_phase(uvm_phase phase);
super.check_phase(phase);
`uvm_info("CHECK", $sformatf("check_phase of %s", get_name()), UVM_LOW)
endfunction: check_phase
Report phase: Displays the result of the simulation or to write the result to file
function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("REPORT", $sformatf("report_phase of %s", get_name()), UVM_LOW)
endfunction: report_phase
Final phase: used to complete any outstanding actions that tb didn’t completed executes in top-down manner.
function void final_phase(uvm_phase phase);
super.final_phase(phase);
`uvm_info("FINAL", $sformatf("final_phase of %s", get_name()), UVM_LOW)
endfunction: final_phase
How these phases gets activated:
Once these run_test() method is called it constructs the root components of the uvm
environment and then it initiates the uvm phasing process. It must be called from static part of
testbench means within initial- begin block in top module. A phase starts only when all
components in the previous phase have dropped their objections.
Severity: Severity indicates importance. Ex: Fatal, Error, Warning & Info
Verbosity: Verbosity indicates filter level. Ex: None, Low, Medium, High, Full & Debug
Simulation Handling Behavior: Simulation handling behavior controls simulator behavior.Ex: Exit, Count,
Display, Log, Call Hook & No Action
Simulation Handling Behavior in-fact is the Action taken by the Simulator which is dependent on Severity
being produced by the Verification Environment. We’ll see more details shortly about it
Verbosity levels:
Default is UVM_MEDIUM.
id -- a unique id to form a group of messages.
message -- The message text
verbosity -- the verbosity of the message, indicating its relative importance. If this number is less than or equal to the
effective verbosity level, then the report is issued, subject to the configured action and file descriptor settings.
filename/line -- If required to print filename and line number from where the message is issued, use macros, `__FILE__
and `__LINE__.
UVM verbosity level is required only for uvm_info, cant be used for uvm_warning, uvm_error, uvm_fatal
The built in methods which are used to call inside the reporting macros:
1) get_name(): returns the name of the object
`uvm_error(get_name(),”driver”)//:drv[D]driver
2) get_full_name(): returns the full hierarchial (path) name of the object
`uvm_error(get_full_name(),”driver”)//:top.test.env.ag1.drv[DRV]driver
3) get-type_name(): returns the type name of the object.
FACTORY CONCEPT: The purpose of factory in UVM is to change the behavior of the testbench without
any change in code or without any compilation. The factory registered components & objects are factory
registered for overriding purpose without any further modification. Factory registration helps in reusing of
your own component without changing the actual code.
For this purpose,the factory needs to know all types of classes created within
the testbench by a process called registration.
Basic operations of uvm factory.
Register: Registration is done using utility macros.
Overriding :
SET_INST_OVERRIDE BY TYPE: (set_type_override or Global override)
function void set_inst_override_by_type
(uvm_object_wrapper original_type,uvm_object_wrapper override_type, string full_inst_path);
SET_TYPE_OVERRIDE BY NAME:
function void set_type_override_by_name
(string original_type_name,string override_type_name,bit replace = 1);
When overriding by type, the original_type and override_type are handles to the types’ proxy
objects. Preregistration is not required.
When overriding by name, the original_type_name typically refers to a preregistered type in the factory.
When replace is 1, a previous override on original_type_name is replaced, otherwise a previous override, if
any, remains intact.
factory.print();//It will print the factory configuration.
Uvm_object_wrapper:
The uvm_object_wrapper provides an abstract interface for creating object and component proxies.
virtual class uvm_object_wrapper
Methods:
create_object:
Creates a new object with the optional name.
create_component:
Creates a new component, passing to its constructor the given name and parent.
get_type_name:
Derived classes implement this method to return the type name of the object created by create_component
or create_object.
Utility Macros: These are used to declare either object or component to get register in to the factory. All
classes derived from uvm_transactions and uvm_objects should register with “`uvm_object_utils” and
uvm_component should register with “`uvm_component_utils”.
class ABC extends uvm_object;
class ABC extends uvm_object;
`uvm_component_utils(ABC)
`uvm_object_utils(ABC) function new(string name=“ABC”,uvm_component parent);
function new(string name=“ABC”); super.new(name);
super.new(name); endfunction
endfunction endclass endclass
Field Macros: Macros that were used between *_begin and *_end utility macros are
basically called field macros. They operate on class properties and provides “automatic”
implementations of the core data methods: copy, compare, pack, unpack, record, print, and
sprint. They are used inside utility macros..
1.do_print: Performs deep print of object properties in a format given by its argument.
2.do_record: Records the object properties.
3.do_copy : Returns a deep copy of the object.
4.do_compare: Deep compare this data object provided in the rhs argument, returns 1 if true else 0.
5.do_pack: This method used to perform bitwise concatenation of object properties into array of bits, bytes
& ints.
6.do_unpack: Ths method used to extract property values from array of bits and bytes.
7.clone : This method first create a memory and then copies the data from one object to other.
8.create: This allocate a memory to the created object and to register the corresponding object to the
factory.
Configuration database:
Config db allows passing of objects and data to various components in the testbench with hierarchy. It is built
on top of uvm_resource_db, here resource db doesn’t have hierarchy concept. Note that all the functions are
static and must be called using the :: scope operator in config_db.
The database has both a name table and a type table and each resource is entered into both. Resources are stored
in a database so that each resource can be retrieved by name or by type, and the database is globally accessible.
uvm config db set method:
uvm_config_db#(type T = int)::set(uvm_component_cntxt, string inst_name, string field_name, T value);
Example:
mem_if intf(clk,reset); //interface instance
uvm_config_db#(virtual mem_if)::set(null,"*","mem_intf",intf); //set method
Here mem_if is declared globally so that other components in the hierarchy can access it.
uvm config db get method:
uvm_config_db#(type T=int)::get(uvm_component_cntxt, string inst_name, string field_name, ref T value);
Example:
virtual interface mem_if mem_vif; //virtual interface declaration
if( !uvm_config_db#(virtual mem_if)::get(this,"*", "mem_intf", mem_vif))
`uvm_fatal("virtual interface not connected" ); //get method
Uvm_resource_db: In this if same thing is written at multiple places in the hierarchy then last write wins. It is
recommended to use uvm_config_db. In this there is no hierarchy concept.
get_by_type: This function gets the resource by the type specified by the parameter so the only argument is
the scope.
get_by_name: This function gets a resource by using both the scope and name given when it was added to the
database.
Set: This function creates a new resource in the database with a value, scope, and name that will be
used for retrieval.
read_by_name: This function locates a resource by scope and name and returns the value through an output
argument.
read_by_type: This function locates the resource using only the scope as a lookup and returns the value through
an output argument.
write_by_name: This function locates the resource by scope and name and writes the value to it. If the resource
does not exist then it will be created like the set function.
write_by_type: This function locates the resource by the scope and writes the value to it. If the resource does not
exist it is created.
Stimulus generation
A sequence generates a series of sequence_item’s and sends it to the driver via sequencer, Sequence is written by
extending the uvm_sequence.
▪Derive from uvm_sequence base class with a specified data object type
▪Register the sequence with the factory using `uvm_object_utils
▪Set the default sequencer that should execute this sequence
▪Code the main stimulus inside the body() task.
5&6 are optional. Mid_do & post_do are functions remaining all tasks.
UVM Sequence macros:
These macros are used to start sequences and sequence items on default sequencer, m_sequencer
m_sequencer, p_sequencer: if we want to access anything from the testbench hierarchy (which are
components) - the sequence(object) would need a handle to the sequencer(component) on which the
sequence is running.
p_sequencer: The p_sequencer is a type specific sequencer pointer, created by registering the sequence to a
sequencer using the `uvm_declare_p_sequencer macros. p_sequencer will not exist if the
`uvm_declare_p_sequencer macros isn’t used. P_sequencer doesn’t exist by default in sequences.Implement
a function to set a value.
The port in uvm_driver is connected to the export in uvm_sequencer in the connect phase of uvm_agent.
Creation: Creating the transaction item.
Ready start_item(): By passing seq_item handle as assignment. This class blocks until the sequencer grants the seq &
seq_item access to driver.
Set: seq_item is ready to use, usually through randomization.
Go finish_item(): Blocks the driver until the completion of it’s side of the transfer protocol for the item.
Response get_response(): It is optional & blocks until the response item is available from the sequencer response FIFO.
Virtual sequence: When multiple sequences are there then virtual sequence is used. It is a container to
start multiple sequences on different sequencers in the environment. It is usually executed by a virtual
sequencer which has handles to real sequencers. It is required when different sequences to be run on different
environment. Inside it different sequences, sequencers & virtual sequencers are present. It becomes virtual
because it doesn’t associated with any
particular data type.
Virtual sequencer: It just contains handles of different sequencers. When multiple sequencers are there in
Environment then Virtual sequencer should be required. It is a Sequencer that is not connected to the UVM
Driver itself, but contains the handles of the target Sequencer in the Testbench hierarchy.
➢ A handle called p_sequencer is created within the sequence via macro `uvm_declare_p_sequencer and
assigned to be run with my_virtual_sequencer.
➢ Each sequence is started on its corresponding sequencer using the start() method
➢ Each sequencer is referenced by p_sequencer handle which points to the virtual sequencer.
➢ Virtual sequence extended from uvm_sequence.
➢ Virtual sequencer extended from uvm_sequencer.
➢ Handles of sequencers in virtual sequencer are made to point physical sequencers in the build phase of the
environment.
➢ A virtual sequencer class will have handles of physical sequencers.
➢ The virtual sequence has handles for physical sequencers and virtual sequencer.
UVM Sequence Arbitration: The sequencer supports an arbitration mechanism to ensure that at any point of
time only one sequence has access to the driver. Whenever multiple sequences try to access a single driver,
the sequence will make them in a certain order through arbitration.
2.SEQ_ARB_WEIGHTED
•If this arbitration mode is selected, sequence items from the highest priority sequence are always picked
first until none available, then the sequence items from the next priority sequence, and so on. If two
sequences have equal priority, then the items from them are picked in random order.
3.SEQ_ARB_RANDOM
• If this arbitration mode is selected, sequence items from different sequences are picked in random order by
ignoring all priorities.
4.SEQ_ARB_STRICT_FIFO
• This is similar to SEQ_ARB_WEIGHTED except that if two sequences have the same priority, then the items
from those sequences are picked in a FIFO order rather than in random order.
5.SEQ_ARB_STRICT_RANDOM
•This is similar to SEQ_ARB_RANDOM except that the priorities are NOT ignored.
•The items are picked randomly from sequences with the highest priority first followed by next and in that
order.
6.SEQ_ARB_USER
•This algorithm allows a user to define a custom algorithm for arbitration between sequences.
•This is done by extending the uvm_sequencer class and overriding the user_priority_arbitration() method.
TLM: Transaction-Level Modelling (TLM) is used for communication among modules. TLM is the concept
in which transaction based methods are implemented, these methods can be used for communication between
the modules. They are used like pipes to connect between components so there won’t be any packet loss.
The UVM provides TLM library with transaction-level interfaces, ports, exports, imp ports, and analysis
ports. all these TLM elements are required to send a transaction, receive transaction, and transport from one
component to another.
TRANSPORT IMP CONSTRUCTOR: (new)This is a constructor method used for the creation of TLM Imp Port.
TLM interface: In this scheme data is represented as transactions which flow in and out of component via special ports
called TLM interface.
Blocking Interfaces:
put(): This method is used to send a transaction to another component (<port>.put(trans)).
get(): This method is used to retrieve transaction from another component (<port>.get(trans) ).
peek(): This method obtain a transaction without consuming it (<port>.peek(trans) ).
Non-Blocking Interfaces:
try_put(): This method is used to send a transaction to another component without blocking the execution
(<port>.try_put(trans)). This method returns 1 If the component is ready to accept the transaction, otherwise it returns 0.
can_put(): This method call returns 1 if the component is ready to accept the transaction, otherwise, it returns 0
no argument must be passed to can_put, this method is used to see whether the other component is ready to accept the
transaction.
try_get: method is used to retrieve transaction from another component without blocking the execution. Calling
<port>.try_get(trans) retrieve transaction from another component, if possible. This method returns 1 if the
transaction is available, otherwise, it returns 0
can_get(): This method call returns 1 if the component can get transaction immediately, otherwise, it returns 0. No
argument must be passed to can_get, this method is used to see whether any transaction is available to get
transport(): Calling <port>.transport(req,resp) method executes the given request and returns the response in the
given output argument. It is blocking, it may block until the operation is complete.
nb_transport(): Calling <port>.nb_transport(req,resp) the method executes the given request and returns the
response in the given output argument, If for any reason the operation could not be executed immediately, then a
0 must be returned, otherwise 1.
TLM FIFO
The TLM FIFO provides storage for the transactions between two independently running processes.
In TLM FIFO, the sender pushes the transactions to FIFO and whenever it required reiver pops it out or fetches from the
FIFO
Transactions are put into the FIFO via the put_export method
Transactions are fetched from the FIFO via
the get_peek_export method
TLM FIFO METHODS:
new:
This is a constructor method used for the creation of TLM FIFO
function new(string name, uvm_component parent,int size=1);
size:
Calling size() returns the size of the FIFO
A return value of 0 indicates the FIFO capacity has no limit
used:
Returns the number of entries put into the FIFO
is_empty:
Returns 1 when there are no entries in the FIFO, 0 otherwise
is_full:
Returns 1 when the number of entries in the FIFO is equal to its size, 0 otherwise
flush:
Calling flush method will Remove all entries from the FIFO
after the flush method call used method returns 0 and the is_empty method returns 1
analysis_port.write(trans);
TLM ANALYSIS FIFO:
TLM MAILBOX
We don’t need to pass the handle of tlm It requires the user to pass the mailbox
port to any component, we just need to handle to the components which need to
instantiate with seq_item. be connected.
Ex:
class agent extends uvm_agent;
task run_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
endclass
In this the phase, the objection is raised and continue in the run_phase till #100 delay and then the objection is
dropped and control moves to next phase called extract phase.
UVM RAL MODEL:
When a design contains number of registers and memories, then to verify them effectively UVM provides a
base class library for the register management & access. This approach is known as RAL(Register Abstraction
Layer).
In general it is a high level object oriented abstraction layer to access design registers. RAL mimics the design
registers using UVM base class libraries.
Predictor:
A component called predictor can be placed on the target bus interface to monitor for any transactions and
update the register model accordingly.
That’s the reason the Back Door Access is faster than Front Door Access.
Thank you