uvm_comparer
is the standalone class used to set a policy for doing comparisons and determines how miscompares are counted. Every uvm_object
instance has a compare()
method for performing comparisons with another object. A policy object can be passed along to set parameters like depth of comparison, verbosity, maximum number of miscompares etc - an extra layer of flexibility. uvm_comparer
also has a set of comparison methods for integers, strings, real numbers and objects.
Note that the number of miscompares is stored in an internal variable called "result", which gets incremented upon every such occurrence. Hence, if you are using the same object to compare two different items, the number of miscompares shown in the end might not be correct. It is advised to clear the "result" variable for every new comparison. This is only valid if you are directly using an object like in the example shown below.
When the uvm_comparer
object is passed onto uvm_object::compare()
method, the comparison policy will be set, and result will be cleared. If no uvm_comparer
object is passed along, then it will take the default global level uvm_comparer
policy object.
Example
This example will build upon the How to use uvm_printer set of data objects.
class base_test extends uvm_test;
`uvm_component_utils (base_test)
my_data obj0, obj1;
derivative dv0, dv1;
uvm_comparer uc0;
function new (string name = "base_test", uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
obj0 = my_data::type_id::create ("obj0");
obj1 = my_data::type_id::create ("obj1");
uc0 = new();
endfunction
virtual task run_phase (uvm_phase phase);
cfg_comparer();
// Do not use the same uvm_compare object to do multiple comparisons, like shown below
// The example is a demonstration of the different methods of uvm_compare
// NOTE: There's an internal variable "result" that stores the number of miscompares.
// This variable will continue to be incremented for every mismatch, until cleared manually
`uvm_info ("COMPARE", "Trying out compare_field", UVM_MEDIUM)
uc0.compare_field ("compare_field1", 7, 7, 2); // pass
uc0.compare_field ("compare_field2", 8'd45, 8'd8, 2); // fail
`uvm_info ("COMPARE", "Trying out field_int", UVM_MEDIUM)
uc0.compare_field_int ("field_int1", 64'hface_deaf_feed_cafe, 64'hfabe_deaf_feed_cafe, 64); // fail
uc0.compare_field_int ("field_int2", 64'habcd_ef12_3456_7890, 64'habcd_ef12_3456_7890, 64); // pass
uc0.compare_field_int ("field_int3", 64'hface_deaf_feed_cafe, 64'h7ace_deaf_feed_cafe, 63); // pass
uc0.compare_field_int ("field_int4", 64'hface_deaf_feed_cafe, 64'h8abe_deaf_feed_cafe, 64); // fail
uc0.compare_field_int ("field_int5", 68'hbbbb_face_deaf_feed_cafe, 68'haaaa_8abe_deaf_feed_cafe, 68); // won't work; nothing happens
uc0.compare_field ("field", 68'hb_face_deaf_feed_cafe, 68'haa_8abe_deaf_feed_cafe, 68); // fail: will work with field, because size > 64
`uvm_info ("COMPARE", "Trying out compare_object", UVM_MEDIUM)
void'(obj0.randomize());
void'(obj1.randomize());
uc0.compare_object ("object1", obj0, obj1); // fail
obj1.copy (obj0);
uc0.compare_object ("object2", obj0, obj1); // pass
`uvm_info ("COMPARE", "Trying out compare_string", UVM_MEDIUM)
uc0.compare_string ("string1", "Hello", "World"); // fail
uc0.compare_string ("string2", "Hello", "Hello"); // pass
// Proper Usage: Set the configuration for uvm_comparer and pass it to compare()
// The "result" variable is cleared before comparison starts within uvm_object::compare()
obj0.name = "Apple";
obj0.m_format0.m_color.fav = "magenta";
obj0.m_format0.m_color.unfav = "yellow";
obj1.name = "Orange";
obj1.m_format0.m_color.fav = "magenta";
obj1.m_format0.m_color.unfav = "green";
cfg_comparer();
void'(obj0.randomize());
obj1.compare (obj0, uc0);
// randomize and compare again
void'(obj0.randomize());
obj1.compare (obj0, uc0);
endtask
virtual function cfg_comparer();
uc0.show_max = 20; // total number of miscompares to be printed
uc0.verbosity = UVM_MEDIUM;
endfunction
endclass
Simulation Log UVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO ./tb/test_pkg.sv(43) @ 0: uvm_test_top [COMPARE] Trying out compare_field UVM_INFO @ 0: reporter [MISCMP] Miscompare for compare_field2: lhs = 'h1 : rhs = 'h0 UVM_INFO ./tb/test_pkg.sv(47) @ 0: uvm_test_top [COMPARE] Trying out field_int UVM_INFO @ 0: reporter [MISCMP] Miscompare for field_int1: lhs = 'hfacedeaffeedcafe : rhs = 'hfabedeaffeedcafe UVM_INFO @ 0: reporter [MISCMP] Miscompare for field_int4: lhs = 'hfacedeaffeedcafe : rhs = 'h8abedeaffeedcafe UVM_INFO @ 0: reporter [MISCMP] Miscompare for field: lhs = 'hbfacedeaffeedcafe : rhs = 'ha8abedeaffeedcafe UVM_INFO ./tb/test_pkg.sv(57) @ 0: uvm_test_top [COMPARE] Trying out compare_object UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.data: lhs = 'h5a : rhs = 'h47 UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.addr: lhs = 'h1 : rhs = 'h2 UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.header: lhs = 'hb : rhs = 'h2 UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.footer: lhs = 'h0 : rhs = 'h7 UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.body: lhs = 'h1 : rhs = 'h2 UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.m_color.color: lhs = 'h8 : rhs = 'h2 UVM_INFO @ 0: reporter [MISCMP] 10 Miscompare(s) for object [email protected] vs. [email protected] UVM_INFO @ 0: reporter [MISCMP] 10 Miscompare(s) for object [email protected] vs. [email protected] UVM_INFO ./tb/test_pkg.sv(64) @ 0: uvm_test_top [COMPARE] Trying out compare_string UVM_INFO @ 0: reporter [MISCMP] Miscompare for string1: lhs = "Hello" : rhs = "World" UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.data: lhs = 'h5a : rhs = 'hee UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.addr: lhs = 'h1 : rhs = 'h2 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.header: lhs = 'hb : rhs = 'hf UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.body: lhs = 'h1 : rhs = 'h2 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.color: lhs = 'h8 : rhs = 'h7 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.enable: lhs = 'h0 : rhs = 'h1 UVM_INFO @ 0: reporter [MISCMP] 6 Miscompare(s) for object [email protected] vs. [email protected] UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.data: lhs = 'h5a : rhs = 'h2d UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.addr: lhs = 'h1 : rhs = 'h3 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.header: lhs = 'hb : rhs = 'h9 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.footer: lhs = 'h0 : rhs = 'h6 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.enable: lhs = 'h1 : rhs = 'h0 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.color: lhs = 'h8 : rhs = 'h0 UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.enable: lhs = 'h0 : rhs = 'h1 UVM_INFO @ 0: reporter [MISCMP] 7 Miscompare(s) for object [email protected] vs. [email protected] --- UVM Report catcher Summary ---
We have seen the scenario in TLM - Put, where data sent to componentB is executed using the put()
method defined in B. Let us consider the case where there are two components A and C connected to B's export. Then, any data object sent by either componentA or componentC will be received by componentB and operated upon by the same put()
method. If there's a need to be able to process them separately, you would need to have two separate put()
methods.

In a random verification environment where data objects are being continuously generated and operated upon by different components, debug would become easier if contents of an object can be displayed.
Old-school style
Traditionally this is done by printing out values to either a logfile or screen via $display
statements and custom print functions.
class Packet;
bit [31:0] addr;
int count_id;
bit [3:0] length;
...
function display ();
$display ("addr=0x%0h count_id=0x%0h length=0x%0h", p1.addr, p1.count_id, p1.length);
endfunction
endclass
module tb;
initial begin
Packet p1 = new();
p1.display();
end
endmodule
There was no built-in print mechanisms for a class which made testbench developers to write customized versions of print functions for every class like the display()
method in the snippet above.
How its done in UVM
UVM avoids the need for customized print functions by incorporating its own uvm_printer
class that takes care of all the required configurations to print class properties. It makes sense to include print features in uvm_object
so that all child classes will automatically gain access to those features.
Every class item derived from uvm_object
will have a printer
instance within it. So, a data class derived from uvm_sequence_item
or uvm_component
will have access to the print()
function as well. The major advantage UVM provides are the `uvm_field_*
macros that automates the process of writing special print methods.
virtual class uvm_object extends uvm_void;
...
function void print (uvm_printer printer=null);
if (printer==null)
printer = uvm_default_printer;
...
endfunction
...
endclass
Styles
There are three main printers in UVM and have the following display formats.
- Table printer
--------------------------------------------------- Name Type Size Value --------------------------------------------------- c1 container - @1013 d1 mydata - @1022 v1 integral 32 'hcb8f1c97 e1 enum 32 THREE str string 2 hi value integral 12 'h2d ---------------------------------------------------
c1: ([email protected]) { d1: ([email protected]) { v1: 'hcb8f1c97 e1: THREE str: hi } value: 'h2d }
c1: ([email protected]) { d1: ([email protected]) { v1: 'hcb8f1c97 e1: THREE str: hi } value: 'h2d }
Usage
By default, UVM assigns table printer to handle every print()
function, and hence is the uvm_default_printer
. For convenience, global instances of each printer type are available for direct reference in your testbenches.

uvm_table_printer uvm_default_table_printer = new();
uvm_tree_printer uvm_default_tree_printer = new();
uvm_line_printer uvm_default_line_printer = new();
uvm_printer uvm_default_printer = uvm_default_table_printer;
You can pass any of the three printer instance to the print()
function to make it print in that particular style. Remember that the print()
function receives a printer object.
class my_data extends uvm_sequence_item;
bit [2:0] mode;
endclass
my_data obj0;
obj0.print (); // Calls table printer by default
obj0.print (uvm_default_line_printer); // Calls line printer
Conditions
Calling print()
is only possible if you do either of the two things.
- Add any member that needs to be printed, within
`uvm_object_utils_begin
and`uvm_object_utils_end
class my_data extends uvm_sequence_item;
rand bit [7:0] data;
rand bit [7:0] addr;
constraint c_addr { addr > 0; addr < 8;}
`uvm_object_utils_begin (my_data)
`uvm_field_int (data, UVM_ALL_ON)
`uvm_field_int (addr, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "my_data");
super.new (name);
endfunction
endclass
By applying variables to the macros, UVM automates these members to be included in the common utilties defined in uvm_object
class namely, print()
, copy()
, compare()
, etc
do_print()
function for the class
class derivative extends my_data;
`uvm_object_utils (derivative)
rand bit [2:0] mode;
function new (string name="derivative");
super.new (name);
endfunction
virtual function void do_print (uvm_printer printer);
printer.print_int ("mode", mode, $bits(mode));
`uvm_info ("DVR", "do_print called", UVM_MEDIUM)
endfunction
endclass
If you did not use the utils_begin/end
macros, you should define the do_print()
method. Remember to use functions within the uvm_printer
class to print.
Note that you can also do both the approaches together. The do_print()
method will simply append whatever it is provided with to the macro.
class derivative extends my_data;
rand bit [2:0] mode;
rand bit [1:0] format;
`uvm_object_utils_begin (derivative)
`uvm_field_int (format, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name="derivative");
super.new (name);
endfunction
virtual function void do_print (uvm_printer printer);
printer.print_int ("mode", mode, $bits(mode));
`uvm_info ("DVR", "do_print called", UVM_MEDIUM)
endfunction
endclass
Knobs
There is an instance of a class called uvm_printer_knobs
within every printer. This can be used to change some of the settings and parameters used by the print() function.
uvm_default_printer.knobs.size = 0; // Will not display the size column
uvm_default_printer.knobs.indent = 4; // Indents to the right by 4 spaces instead of the default 2
uvm_default_printer.hex_radix = "0x"; // Replaces the hex radix of 'h with 0x
Refer Class Definitions for full usage.Example

First, we'll define a few data item objects. colors is nested inside format, which is inturn nested in the class my_data to create a nested hierarchy.
//---------------------- colors --------------------------
class colors extends uvm_sequence_item;
rand bit [3:0] color;
rand bit enable;
`uvm_object_utils_begin (colors)
`uvm_field_int (color, UVM_ALL_ON)
`uvm_field_int (enable, UVM_ALL_ON)
`uvm_object_utils_end
endclass
//---------------------- format --------------------------
class format extends uvm_sequence_item;
rand bit [3:0] header;
rand bit [2:0] footer;
rand bit enable;
rand bit [1:0] body;
rand colors m_color;
`uvm_object_utils_begin (format)
`uvm_field_int (header, UVM_ALL_ON)
`uvm_field_int (footer, UVM_ALL_ON)
`uvm_field_int (enable, UVM_ALL_ON)
`uvm_field_int (body, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "format");
super.new (name);
m_color = colors::type_id::create ("m_color");
endfunction
virtual function void do_print (uvm_printer printer);
printer.print_object ("m_color", m_color);
endfunction
endclass
//---------------------- my_data --------------------------
class my_data extends uvm_sequence_item;
rand format m_format0;
rand bit [7:0] data;
rand bit [7:0] addr;
constraint c_addr { addr > 0; addr < 8;}
`uvm_object_utils_begin (my_data)
`uvm_field_int (data, UVM_ALL_ON)
`uvm_field_int (addr, UVM_ALL_ON)
`uvm_field_object (m_format0, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "my_data");
super.new (name);
m_format0 = format::type_id::create ("m_format0");
endfunction
endclass
//---------------------- derivative --------------------------
class derivative extends my_data;
`uvm_object_utils (derivative)
rand bit [2:0] mode;
function new (string name="derivative");
super.new (name);
endfunction
virtual function void do_print (uvm_printer printer);
printer.knobs.depth=0;
printer.print_int ("mode", mode, $bits(mode));
`uvm_info ("DVR", "do_print called", UVM_MEDIUM)
endfunction
endclass
Note the following for the code shown above:
- All the members of the class colors are added to the
`uvm_object_utils_begin/end
macro - All the class objects have to be instantiated either using
new()
ortype_id::create()
- In class format, all members except m_color is used in the macro, while m_color is used inside
do_print()
method - Class derivative adds the mode via
do_print()
method.
class base_test extends uvm_test;
`uvm_component_utils (base_test)
my_data obj0;
derivative dv0;
uvm_table_printer tprinter;
function new (string name = "base_test", uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
obj0 = my_data::type_id::create ("obj0");
dv0 = derivative::type_id::create ("dv0");
tprinter = new();
cfg_printer();
endfunction
virtual task run_phase (uvm_phase phase);
void'(obj0.randomize());
`uvm_info ("TST", "print() called with no arguments", UVM_MEDIUM)
obj0.print (); // uses uvm_default_printer
`uvm_info ("TST", "Calling print (uvm_default_line_printer)", UVM_MEDIUM)
obj0.print (uvm_default_line_printer);
`uvm_info ("TST", "Calling print (uvm_default_tree_printer)", UVM_MEDIUM)
obj0.print (uvm_default_tree_printer);
`uvm_info ("TST", "Calling print (uvm_default_table_printer)", UVM_MEDIUM)
obj0.print (uvm_default_table_printer);
`uvm_info ("TST", "Calling print (tprinter)", UVM_MEDIUM)
obj0.print (tprinter);
`uvm_info ("TST", "Print derivative (tprinter)", UVM_MEDIUM)
dv0.print (uvm_default_table_printer);
endtask
// Configuration to change display settings for the printer
virtual function void cfg_printer ();
tprinter.knobs.full_name = 1; // Show full name of the variable relative to class instance
tprinter.knobs.size = 0; // Do not show size column
tprinter.knobs.depth = 1; // Only show upto 1 level of the nested hierarchy
tprinter.knobs.reference = 2; // Do not print object ID handles
tprinter.knobs.type_name = 0; // Do not show Type column
tprinter.knobs.indent = 4; // Indent from the left by 4
tprinter.knobs.hex_radix = "0x"; // Replace 'h radix by 0x - observe Value field to see this
endfunction
endclass
Simulation Log ---------------------------------------------------------------- CDNS-UVM-1.1d (14.22-s009) (C) 2007-2013 Mentor Graphics Corporation (C) 2007-2013 Cadence Design Systems, Inc. (C) 2006-2013 Synopsys, Inc. (C) 2011-2013 Cypress Semiconductor Corp. ---------------------------------------------------------------- UVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO ./tb/test_pkg.sv(34) @ 0: uvm_test_top [TST] print() called with no arguments ----------------------------------- Name Type Size Value ----------------------------------- obj0 my_data - @2687 data integral 8 'h5a addr integral 8 'h1 m_format0 format - @2700 header integral 4 'hb footer integral 3 'h0 enable integral 1 'h1 body integral 2 'h1 m_color colors - @2711 color integral 4 'h8 enable integral 1 'h0 ----------------------------------- UVM_INFO ./tb/test_pkg.sv(37) @ 0: uvm_test_top [TST] Calling print (uvm_default_line_printer) obj0: ([email protected]) { data: 'h5a addr: 'h1 m_format0: ([email protected]) { header: 'hb footer: 'h0 enable: 'h1 body: 'h1 m_color: ([email protected]) { color: 'h8 enable: 'h0 } } } UVM_INFO ./tb/test_pkg.sv(40) @ 0: uvm_test_top [TST] Calling print (uvm_default_tree_printer) obj0: ([email protected]) { data: 'h5a addr: 'h1 m_format0: ([email protected]) { header: 'hb footer: 'h0 enable: 'h1 body: 'h1 m_color: ([email protected]) { color: 'h8 enable: 'h0 } } } UVM_INFO ./tb/test_pkg.sv(43) @ 0: uvm_test_top [TST] Calling print (uvm_default_table_printer) ----------------------------------- Name Type Size Value ----------------------------------- obj0 my_data - @2687 data integral 8 'h5a addr integral 8 'h1 m_format0 format - @2700 header integral 4 'hb footer integral 3 'h0 enable integral 1 'h1 body integral 2 'h1 m_color colors - @2711 color integral 4 'h8 enable integral 1 'h0 ----------------------------------- UVM_INFO ./tb/test_pkg.sv(46) @ 0: uvm_test_top [TST] Calling print (tprinter) ------------------------- Name Value ------------------------- obj0 - obj0.data 0x5a obj0.addr 0x1 obj0.m_format0 - ------------------------- UVM_INFO ./tb/test_pkg.sv(49) @ 0: uvm_test_top [TST] Print derivative (tprinter) UVM_INFO ./tb/my_pkg.sv(94) @ 0: reporter@@dv0 [DVR] do_print called ------------------------------------- Name Type Size Value ------------------------------------- dv0 derivative - @2722 data integral 8 'h0 addr integral 8 'h0 m_format0 format - @2732 header integral 4 'h0 footer integral 3 'h0 enable integral 1 'h0 body integral 2 'h0 m_color colors - @2741 color integral 4 'h0 enable integral 1 'h0 mode integral 3 'h0 ------------------------------------- --- UVM Report catcher Summary ---
UVM factory is a mechanism to improve flexibility and scalability of the testbench by allowing the user to substitute an existing class object by any of its inherited child class objects.
For this purpose, the factory needs to know all the types of classes created within the testbench by a process called as registration. There are UVM macros that allow classes to be registered with the factory, and methods that allow certain types and instances of class objects to be overridden by its derived types.

Factory Override Methods
// Override all the objects of a particular type
set_type_override_by_type ( uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
bit replace=1);
set_type_override_by_name ( string original_type_name,
string override_type_name,
bit replace=1);
// Override a type within a particular instance
set_inst_override_by_type (uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
string full_inst_path);
set_inst_override_by_name (string original_type_name,
string override_type_name,
string full_inst_path);
Method Examples
Shown below is an example testbench structure comprising of a base agent inside a base environment that is instantiated within the test class. We will try to use factory override methods to override the base class with child class.
// Define a base class agent
class base_agent extends uvm_agent;
`uvm_component_utils(base_agent)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
// Define child class that extends base agent
class child_agent extends base_agent;
`uvm_component_utils(child_agent)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
// Environment contains the agent
class base_env extends uvm_env;
`uvm_component_utils(base_env)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
// 'm_agent' is a class handle to hold base_agent
// type class objects
base_agent m_agent;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Use create method to request factory to return a base_agent
// type of class object
m_agent = base_agent::type_id::create("m_agent", this);
// Now print the type of the object pointing to by the 'm_agent' class handle
`uvm_info("AGENT", $sformatf("Factory returned agent of type=%s, path=%s", m_agent.get_type_name(), m_agent.get_full_name()), UVM_LOW)
endfunction
endclass
1. Type override by Type/Name
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
base_env m_env;
virtual function void build_phase(uvm_phase phase);
// Get handle to the singleton factory instance
uvm_factory factory = uvm_factory::get();
super.build_phase(phase);
// Set factory to override 'base_agent' by 'child_agent' by type
set_type_override_by_type(base_agent::get_type(), child_agent::get_type());
// Or set factory to override 'base_agent' by 'child_agent' by name
// factory.set_type_override_by_name("base_agent", "child_agent");
// Print factory configuration
factory.print();
// Now create environment
m_env = base_env::type_id::create("m_env", this);
endfunction
endclass
The following log shows that a type override is configured and replaces base_agent with child_agent. This is also evident from the info statement that prints type of the returned object.
Simulation LogUVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_factory.svh(1645) @ 0: reporter [UVM/FACTORY/PRINT] #### Factory Configuration (*) No instance overrides are registered with this factory Type Overrides: Requested Type Override Type -------------- ------------- base_agent child_agent All types registered with the factory: 54 total Type Name --------- base_agent base_env base_test child_agent (*) Types with no associated type name will be printed as#### UVM_INFO testbench.sv(32) @ 0: uvm_test_top.m_env [AGENT] Factory returned agent of type=child_agent, path=uvm_test_top.m_env.m_agent UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] --- UVM Report Summary ---
2. Instance override by Type/Name
This is most useful when only a few instances of the given type has to be overriden. A type override by type will replace all instances of the given type by the new type.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
base_env m_env;
virtual function void build_phase(uvm_phase phase);
// Get handle to the singleton factory instance
uvm_factory factory = uvm_factory::get();
super.build_phase(phase);
// Set factory to override all instances under m_env of type 'base_agent' by 'child_agent'
set_inst_override_by_type("m_env.*", base_agent::get_type(), child_agent::get_type());
// Or set factory to override all instances under 'm_env' called 'base_agent' by 'child_agent' by name
// factory.set_inst_override_by_name("base_agent", "child_agent", {get_full_name(), ".m_env.*"});
// Print factory configuration
factory.print();
// Now create environment
m_env = base_env::type_id::create("m_env", this);
endfunction
endclass
Simulation Log UVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_factory.svh(1645) @ 0: reporter [UVM/FACTORY/PRINT] #### Factory Configuration (*) Instance Overrides: Requested Type Override Path Override Type -------------- -------------------- ------------- base_agent uvm_test_top.m_env.* child_agent No type overrides are registered with this factory All types registered with the factory: 54 total Type Name --------- base_agent base_env base_test child_agent (*) Types with no associated type name will be printed as#### UVM_INFO testbench.sv(32) @ 0: uvm_test_top.m_env [AGENT] Factory returned agent of type=child_agent, path=uvm_test_top.m_env.m_agent UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] --- UVM Report Summary ---
Why is UVM factory required ?
The new
function is used in SystemVerilog to create a class object and is perfectly valid to be used in UVM as well. Assume that an existing testbench uses Wishbone v1.0 protocol data packet class and is used throughout the testbench in components like driver, monitor, scoreboard, and many other sequences. If Wishbone v2.0 is released and the testbench is required to update and start using packet definition for the new protocol, there would be many places in the testbench that would require an update in code which can prove to be cumbersome.
class wb_seq extends uvm_sequence_item;
...
virtual task body();
// new() function allocates space for the new class object
// and assigns the handle 'm_wb_pkt' to new object
wb_pkt m_wb_pkt = new();
// This new object may be used everywhere in the sequence
start_item(m_wb_pkt);
m_wb_pkt.randomize();
...
endtask
endclass
UVM has a feature called factory which allows users to modify or substitute type of the item created by the factory without having to modify existing class instantiations. Instead of text substitution of class name of existing data packet, a child class object can be created that makes necessary modifications for 2.0 and the factory can be used to return the newly defined class object in all places within the testbench instead of the first one. So, the preferred method of object creation in UVM testbench is by using create
method.
class wb_seq extends uvm_sequence_item;
...
virtual task body();
// By calling create() method, the factory will create an instance of the requested
// type and assign the handle 'm_wb_pkt' to the new object. This override of type
// can be done at a higher level like the test class
wb_pkt m_wb_pkt = wb_pkt::type_id::create("wb_pkt", this);
// This new object may be used everywhere in the sequence
start_item(m_wb_pkt);
m_wb_pkt.randomize();
...
endtask
endclass
Practical Example
To illustrate the example of a factory override, we'll create an environment as shown above with a couple of different drivers, sequences, and data object types. The image shown above is the default configuration.

Components: - Agents - my_agent (Base) 1. my_agent_v2 (child) - Drivers base_driver (Base) 1. eth_driver (child) 2. spi_driver (child) - Sequencer - my_seqeuncer - Sequences - base_sequence (Base) 1. seq1 (child) 2. seq2 (child) 3. seq3 (child) - Data - eth_packet (Basse) - eth_v2_packet (child) Sequencer within m_agnt2 operate on seq3, while the other two operate on seq1 by default.

Let us first define a base data packet and derive a child class for it.
//------------------ eth_packet-----------------------------------
class eth_packet extends uvm_sequence_item;
`uvm_object_utils (eth_packet)
...
function new (string name = "eth_packet");
super.new (name);
`uvm_info (get_type_name(), "Packet created", UVM_MEDIUM)
endfunction
endclass
//------------------ eth_v2_packet-----------------------------------
class eth_v2_packet extends eth_packet;
`uvm_object_utils (eth_v2_packet)
...
function new (string name = "eth_v2_packet");
super.new (name);
endfunction
endclass
Now let us create a base driver class and extend two more driver classes eth_driver and spi_driver. To show the effect of a factory override, we'll keep these classes to have minimum data and methods.
//------------------ base_driver-----------------------------------
class base_driver #(type T=eth_packet) extends uvm_driver;
`uvm_component_utils (base_driver #(T))
T pkt;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
pkt = T::type_id::create ("pkt0");
endfunction
virtual task run_phase (uvm_phase phase);
super.run_phase (phase);
`uvm_info (get_type_name(), $sformatf("Driver running ...with packet of type : %s", pkt.get_type_name()), UVM_MEDIUM)
endtask
endclass
//----------------- eth_driver-----------------------------------
class eth_driver #(type T=eth_packet) extends base_driver #(T);
`uvm_component_utils (eth_driver #(T))
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
endclass
//----------------- spi_driver-----------------------------------
class spi_driver #(type T=eth_packet) extends base_driver #(T);
`uvm_component_utils (spi_driver #(T))
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
endclass
Coming to the sequences which will be executed by our sequencer
//----------------- base_sequence-----------------------------------
class base_sequence extends uvm_sequence;
`uvm_object_utils (base_sequence)
endclass
//----------------- seq1 -------------------------------------------
class seq1 extends base_eth_sequence;
`uvm_object_utils (seq1)
...
endclass
//----------------- seq2 -------------------------------------------
class seq2 extends base_eth_sequence;
`uvm_object_utils (seq2)
...
endclass
//----------------- seq3 -------------------------------------------
class seq3 extends base_eth_sequence;
`uvm_object_utils (seq3)
...
endclass
We'll create two agents as described below.
//----------------- my_agent -------------------------------------------
class my_agent extends uvm_agent;
`uvm_component_utils (my_agent)
base_driver m_drv0;
my_sequencer m_seqr0;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_drv0 = base_driver::type_id::create ("m_drv0", this);
m_seqr0 = my_sequencer::type_id::create ("m_seqr0", this);
endfunction
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
endclass
//----------------- my_agent_v2 -------------------------------------------
class my_agent_v2 extends uvm_agent;
`uvm_component_utils (my_agent_v2)
eth_driver m_drv0;
my_sequencer m_seqr0;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_drv0 = eth_driver::type_id::create ("m_drv0", this);
m_seqr0 = my_sequencer::type_id::create ("m_seqr0", this);
endfunction
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
endclass
Note that my_agent has base_driver, while my_agent_v2 contains eth_driver. All drivers and sequencers are instantiated within the build_phase()
and connected in connect_phase()
.
The top container "my_env" is still left.
class my_env extends uvm_env ;
`uvm_component_utils (my_env)
my_agent m_agnt0;
my_agent m_agnt1;
my_agent_v2 m_agnt2;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction : new
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_agnt0 = my_agent::type_id::create ("m_agnt0", this);
m_agnt1 = my_agent::type_id::create ("m_agnt1", this);
m_agnt2 = my_agent_v2::type_id::create ("m_agnt2", this);
endfunction : build_phase
endclass : my_env
We have put our components into boxes, and arranged everything. It's time to write the test, and this is where factory override helps. We'll be able to substitute drivers, data packets and sequences by calling a few factory methods.

class feature_test extends base_test;
`uvm_component_utils (feature_test)
function new (string name, uvm_component parent = null);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
`ifdef PKT_OVERRIDE
// Substitute all eth_packets with eth_v2_packet
set_type_override_by_type (eth_packet::get_type(), eth_v2_packet::get_type());
`endif
// These are the three different styles to override something
`ifdef DRV_STYLE1
// Substitute all instances of base_driver with driver2
set_type_override_by_type (base_driver::get_type(), spi_driver::get_type());
`elsif DRV_STYLE2
// Substitute only eth_driver in agnt2 with spi_driver - by calling the component to be replaced method
eth_driver::type_id::set_inst_override (spi_driver::get_type(), "m_top_env.m_agnt2.m_drv0", this);
`elsif DRV_STYLE3
// Substitute base_driver only in agnt0 - by calling the factory method
factory.set_inst_override_by_type (base_driver::get_type(), eth_driver::get_type(), {get_full_name(), ".m_top_env.m_agnt0.*"});
`endif
// Trying to override a sequence
`ifdef SEQ_TYPE
// Substitute seq1 with seq2
set_type_override_by_type (seq1::get_type(), seq3::get_type());
`elsif SEQ_INST
// Substitute seq1 with seq2 only for agnt1
set_inst_override_by_type ("m_top_env.m_agnt1.m_seqr0.*", seq1::get_type(), seq2::get_type());
`else
`endif
factory.print();
endfunction
// Enter test code for feature here
endclass
These results are for running the test with defines PKT_OVERRIDE
, DRV_STYLE3
and SEQ_INST
. Also, note that the factory configuration is shown in the log.
---------------------------------------------------------------- CDNS-UVM-1.1d (14.10-s013) (C) 2007-2013 Mentor Graphics Corporation (C) 2007-2013 Cadence Design Systems, Inc. (C) 2006-2013 Synopsys, Inc. (C) 2011-2013 Cypress Semiconductor Corp. ---------------------------------------------------------------- UVM_INFO @ 0: reporter [RNTST] Running test feature_test... #### Factory Configuration (*) Instance Overrides: Requested Type Override Path Override Type ---------------- ---------------------------------------- --------------- base_driver #(T) uvm_test_top.m_top_env.m_agnt0.* eth_driver #(T) seq1 uvm_test_top.m_top_env.m_agnt1.m_seqr0.* seq2 Type Overrides: Requested Type Override Type ---------------- ---------------------------------------- eth_packet eth_v2_packet All types registered with the factory: 51 total (types without type names will not be printed) Type Name --------- base_driver #(T) base_eth_sequence base_test eth_driver #(T) eth_packet eth_v2_packet feature_test my_agent my_agent_v2 my_env my_sequencer reg_test seq1 seq2 seq3 (*) Types with no associated type name will be printed as#### UVM_INFO ./tb/my_pkg.sv(45) @ 0: reporter@@eth_v2_packet [eth_v2_packet] Packet created UVM_INFO ./tb/my_pkg.sv(45) @ 0: reporter@@eth_v2_packet [eth_v2_packet] Packet created UVM_INFO ./tb/my_pkg.sv(45) @ 0: reporter@@eth_v2_packet [eth_v2_packet] Packet created UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology: -------------------------------------------------------------- Name Type Size Value -------------------------------------------------------------- uvm_test_top feature_test - @2657 m_top_env my_env - @218 m_agnt0 my_agent - @2772 m_drv0 eth_driver #(T) - @2770 rsp_port uvm_analysis_port - @2963 seq_item_port uvm_seq_item_pull_port - @2912 m_seqr0 my_sequencer - @2996 rsp_export uvm_analysis_export - @3054 seq_item_export uvm_seq_item_pull_imp - @3602 arbitration_queue array 0 - lock_queue array 0 - num_last_reqs integral 32 'd1 num_last_rsps integral 32 'd1 m_agnt1 my_agent - @2751 m_drv0 base_driver #(T) - @3676 rsp_port uvm_analysis_port - @3773 seq_item_port uvm_seq_item_pull_port - @3725 m_seqr0 my_sequencer - @3754 rsp_export uvm_analysis_export - @3859 seq_item_export uvm_seq_item_pull_imp - @4399 arbitration_queue array 0 - lock_queue array 0 - num_last_reqs integral 32 'd1 num_last_rsps integral 32 'd1 m_agnt2 my_agent_v2 - @2802 m_drv0 eth_driver #(T) - @4455 rsp_port uvm_analysis_port - @4553 seq_item_port uvm_seq_item_pull_port - @4505 m_seqr0 my_sequencer - @3803 rsp_export uvm_analysis_export - @4639 seq_item_export uvm_seq_item_pull_imp - @5179 arbitration_queue array 0 - lock_queue array 0 - num_last_reqs integral 32 'd1 num_last_rsps integral 32 'd1 -------------------------------------------------------------- UVM_INFO ./tb/my_pkg.sv(94) @ 0: uvm_test_top.m_top_env.m_agnt2.m_drv0 [eth_driver #(T)] Driver running ...with packet of type : eth_v2_packet UVM_INFO ./tb/my_pkg.sv(94) @ 0: uvm_test_top.m_top_env.m_agnt1.m_drv0 [base_driver #(T)] Driver running ...with packet of type : eth_v2_packet UVM_INFO ./tb/my_pkg.sv(94) @ 0: uvm_test_top.m_top_env.m_agnt0.m_drv0 [eth_driver #(T)] Driver running ...with packet of type : eth_v2_packet UVM_INFO ./tb/my_pkg.sv(278) @ 0: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [seq3] Executing pre_body UVM_INFO ./tb/my_pkg.sv(250) @ 0: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [seq2] Executing pre_body UVM_INFO ./tb/my_pkg.sv(282) @ 0: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [SEQ3] Starting seq3 UVM_INFO ./tb/my_pkg.sv(255) @ 0: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [SEQ2] Starting seq2 UVM_INFO ./tb/my_pkg.sv(232) @ 0: uvm_test_top.m_top_env.m_agnt0.m_seqr0@@seq1 [SEQ1] Starting seq1 UVM_INFO ./tb/my_pkg.sv(284) @ 10000: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [SEQ3] Ending seq3 UVM_INFO ./tb/my_pkg.sv(257) @ 10000: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [SEQ2] Ending seq2 UVM_INFO ./tb/my_pkg.sv(234) @ 10000: uvm_test_top.m_top_env.m_agnt0.m_seqr0@@seq1 [SEQ1] Ending seq1 UVM_INFO ./tb/my_pkg.sv(288) @ 10000: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [seq3] Executing post_body UVM_INFO ./tb/my_pkg.sv(262) @ 10000: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [seq2] Executing post_body UVM_INFO ./tb/my_pkg.sv(292) @ 10000: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [seq3] Executing post_start --- UVM Report catcher Summary ---
A typical verification environment will have hundreds of tests with random seeds and configurations, which can be made simpler by having a variety of sequences to perform individual tasks. Now, you may create top level sequences that can spawn off smaller ones within its body()
task, or create a randomized way of calling multiple sequences. A cleaner way would be to use the sequence library provided by UVM as uvm_sequence_library
. As the name suggests, it keeps a track of the sequences that are registered with it, and calls them a number of times in a random fashion.

Let's take an example of three sequences, all derived from the same base_sequence
class, as we have seen before. Here, these three are individual sequences without being nested inside each other.