A uvm_object
is the base class from which all other UVM classes for data and components are derived. So it is logical for this class to have a common set of functions and features that can be availed by all its derived classes.
Some of the common functions usually required is the ability to print its contents, copy contents from one object to another, and possibly compare two objects. UVM has many automation macros that get expanded into full code during compilation and implements these functions for all classes with ease.
Example
The purpose of this example is to show how UVM automation macros can be used to print variable contents easily.
A class called Object is derived from uvm_object
thereby inheriting all functions within the parent class. A few variables of different data types are declared and an attempt is made to print the content of these variables after randomization.
There are different variations of `uvm_field_*
macros for each data type and the variables have to be registered with the macro corresponding to its data type. UVM_DEFAULT
indicates that the given variable should be used for all the default UVM automation macros implemented by the object which are copy, print, compare and record.
typedef enum {FALSE, TRUE} e_bool;
class Object extends uvm_object;
rand e_bool m_bool;
rand bit[3:0] m_mode;
rand byte m_data[4];
rand shortint m_queue[$];
string m_name;
constraint c_queue { m_queue.size() == 3; }
function new(string name = "Object");
super.new(name);
m_name = name;
endfunction
// Each variable has to be registered with a macro corresponding to its data
// type. For example, "int" types use `uvm_field int, "enum" types use
// `uvm_field_enum, and "string" use `uvm_field_string
`uvm_object_utils_begin(Object)
`uvm_field_enum(e_bool, m_bool, UVM_DEFAULT)
`uvm_field_int (m_mode, UVM_DEFAULT)
`uvm_field_sarray_int(m_data, UVM_DEFAULT)
`uvm_field_queue_int(m_queue, UVM_DEFAULT)
`uvm_field_string(m_name, UVM_DEFAULT)
`uvm_object_utils_end
endclass
To test the above code, we'll simply use a base test class that will print contents of the object.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction
// In the build phase, create an object, randomize it and print
// its contents
function void build_phase(uvm_phase phase);
Object obj = Object::type_id::create("obj");
obj.randomize();
obj.print();
endfunction
endclass
module tb;
initial begin
run_test("base_test");
end
endmodule
By default the UVM printer prints content of any object in a table format in which it specifies the name of the variable, data type of the variable, its size and the value. In the simulation output shown below, it can be seen that the object obj was randomized to the given values. It's quite interesting to note that even arrays (both static and queues) are printed with content of all their indices.
ncsim> run UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES] ---------------------------------------------------------------- UVM-1.2 (C) 2007-2014 Mentor Graphics Corporation (C) 2007-2014 Cadence Design Systems, Inc. (C) 2006-2014 Synopsys, Inc. (C) 2011-2013 Cypress Semiconductor Corp. (C) 2013-2014 NVIDIA Corporation ---------------------------------------------------------------- UVM_INFO @ 0: reporter [RNTST] Running test base_test... ------------------------------------- Name Type Size Value ------------------------------------- obj Object - @1899 m_bool e_bool 32 TRUE m_mode integral 4 'hd m_data sa(integral) 4 - [0] integral 8 'h6c [1] integral 8 'hf4 [2] integral 8 'he [3] integral 8 'h58 m_queue da(integral) 3 - [0] integral 16 'h3cb6 [1] integral 16 'h9ae9 [2] integral 16 'hd31d m_name string 3 obj ------------------------------------- UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] --- UVM Report Summary ---
Using do_print()
Using automation macros are not recommended these days because it introduces quite a lot of additional code and reduces simulator performance. Instead it is recommended to use the do_*
callback hooks to implement the requirement. For example, to print the contents, the user can implement the function called do_print
inside the derived object and not use the automation macro at all.
The do_print
function is called by the print
function by default and hence whatever is defined in do_print
will be printed out. In this case we'll simply print three selected variables although you can print all variables as required.
class Object extends uvm_object;
rand e_bool m_bool;
rand bit[3:0] m_mode;
rand byte m_data[4];
rand shortint m_queue[$];
string m_name;
constraint c_queue { m_queue.size() == 3; }
function new(string name = "Object");
super.new(name);
m_name = name;
endfunction
// Use "do_print" instead of the automation macro
`uvm_object_utils(Object)
// This function simply uses the printer functions to print variables based on their
// data types. For example, "int" variables are printed using function "print_field_int"
virtual function void do_print(uvm_printer printer);
super.do_print(printer);
printer.print_string("m_bool", m_bool.name());
printer.print_field_int("m_mode", m_mode, $bits(m_mode), UVM_HEX);
printer.print_string("m_name", m_name);
endfunction
endclass
This results in a very similar output like the one by `uvm_object_utils_*
. Note that it prints only three variables because only three variables were implemented inside the do_print
function.
ncsim> run UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES] ---------------------------------------------------------------- UVM-1.2 (C) 2007-2014 Mentor Graphics Corporation (C) 2007-2014 Cadence Design Systems, Inc. (C) 2006-2014 Synopsys, Inc. (C) 2011-2013 Cypress Semiconductor Corp. (C) 2013-2014 NVIDIA Corporation ---------------------------------------------------------------- UVM_INFO @ 0: reporter [RNTST] Running test base_test... ------------------------------- Name Type Size Value ------------------------------- obj Object - @1898 m_bool string 4 TRUE m_mode integral 4 'hd m_name string 3 obj ------------------------------- UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] --- UVM Report Summary ---
Using sprint
UVM objects have another function called sprint
that returns the formatted contents in a string format. It is a substitute of the print
function and can be used as shown in the example below. Assume the class declaration is the same as the first example shown above without the use of UVM automation macros.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
Object obj = Object::type_id::create("obj");
obj.randomize();
// Instead of calling print() function, let us call "sprint"
`uvm_info(get_type_name(), $sformatf("Contents: %s", obj.sprint()), UVM_LOW)
endfunction
endclass
module tb;
initial begin
run_test("base_test");
end
endmodule
As you can see, it produced the same table format as a string but prefixed by the custom text called "Contents:".
ncsim> run
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES]
----------------------------------------------------------------
UVM-1.2
(C) 2007-2014 Mentor Graphics Corporation
(C) 2007-2014 Cadence Design Systems, Inc.
(C) 2006-2014 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
(C) 2013-2014 NVIDIA Corporation
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO testbench.sv(98) @ 0: uvm_test_top [base_test] Contents:
-------------------------------
Name Type Size Value
-------------------------------
obj Object - @1902
m_bool string 4 TRUE
m_mode integral 4 'he
m_name string 3 obj
-------------------------------
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
Using convert2string
There is another function available in uvm_object
called convert2string
that will return a string instead of printing the contents in a predefined format. This allows you to define the output into a format you like. For example, we can simply print contents into a single line as shown.
class Object extends uvm_object;
rand e_bool m_bool;
rand bit[3:0] m_mode;
rand byte m_data[4];
rand shortint m_queue[$];
string m_name;
constraint c_queue { m_queue.size() == 3; }
function new(string name = "Object");
super.new(name);
m_name = name;
endfunction
// Use "do_print" instead of the automation macro
`uvm_object_utils(Object)
virtual function string convert2string();
string contents = "";
$sformat(contents, "%s m_name=%s", contents, m_name);
$sformat(contents, "%s m_bool=%s", contents, m_bool.name());
$sformat(contents, "%s m_mode=0x%0h", contents, m_mode);
foreach(m_data[i]) begin
$sformat(contents, "%s m_data[%0d]=0x%0h", contents, i, m_data[i]);
end
return contents;
endfunction
In the test class we will have to call convert2string
instead of print
or sprint
. But remember that this function returns a string and hence it has to be within a `uvm_info
or similar report macro.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
Object obj = Object::type_id::create("obj");
obj.randomize();
`uvm_info(get_type_name(), $sformatf("convert2string: %s", obj.convert2string()), UVM_LOW)
endfunction
endclass
module tb;
initial begin
run_test("base_test");
end
endmodule
ncsim> run
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES]
----------------------------------------------------------------
UVM-1.2
(C) 2007-2014 Mentor Graphics Corporation
(C) 2007-2014 Cadence Design Systems, Inc.
(C) 2006-2014 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
(C) 2013-2014 NVIDIA Corporation
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO testbench.sv(99) @ 0: uvm_test_top [base_test] convert2string:
m_name=obj m_bool=TRUE m_mode=0xe m_data[0]=0xf4 m_data[1]=0xe m_data[2]=0x58 m_data[3]=0xbd
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
In the previous few articles, we have seen what a register model is and how it can be used to access registers in a given design. Let us see a complete example of how such a model can be written for a given design, how it can be integrated into the environment and how it can be used to write and read into design fields.
Click here to refresh the concept on register models !
Design
The following design has the following registers and fields that are accessible through an APB interface. The design essentially represents a traffic light controller which can be configured by writing into certain control registers.
The ctl register contains fields to start the module, and configure it to be in the blink yellow or blink red mode. The state register is read-only and returns current state of the design - yellow, red or green. The two timer registers stores the time between transition from each state. The profile bit allows the user to choose between the two programmed timer values. This can be useful for peak and off-peak times.

This is not a complete design since our purpose is simply to show how registers in this design can be read/written using a UVM register model. All the signals listed as the module ports belong to APB specification.
module traffic ( input pclk,
input presetn,
input [31:0] paddr,
input [31:0] pwdata,
input psel,
input pwrite,
input penable,
// Outputs
output [31:0] prdata);
reg [3:0] ctl_reg; // profile, blink_red, blink_yellow, mod_en RW
reg [1:0] stat_reg; // state[1:0]
reg [31:0] timer_0; // timer_g2y[31:20], timer_r2g[19:8], timer_y2r[7:0] RW
reg [31:0] timer_1; // timer_g2y[31:20], timer_r2g[19:8], timer_y2r[7:0] RW
reg [31:0] data_in;
reg [31:0] rdata_tmp;
// Set all registers to default values
always @ (posedge pclk) begin
if (!presetn) begin
data_in <= 0;
ctl_reg <= 0;
stat_reg <= 0;
timer_0 <= 32'hcafe_1234;
timer_1 <= 32'hface_5678;
end
end
// Capture write data
always @ (posedge pclk) begin
if (presetn & psel & penable)
if (pwrite)
case (paddr)
'h0 : ctl_reg <= pwdata;
'h4 : timer_0 <= pwdata;
'h8 : timer_1 <= pwdata;
'hc : stat_reg <= pwdata;
endcase
end
// Provide read data
always @ (penable) begin
if (psel & !pwrite)
case (paddr)
'h0 : rdata_tmp <= ctl_reg;
'h4 : rdata_tmp <= timer_0;
'h8 : rdata_tmp <= timer_1;
'hc : rdata_tmp <= stat_reg;
endcase
end
assign prdata = (psel & penable & !pwrite) ? rdata_tmp : 'hz;
endmodule
Interface
Let us declare an interface with signals in the APB protocol and this interface can be passed to the testbench as a virtual interface for the driver to drive some values to the design. To keep things simple, let us not declare clocking blocks and modports, although they are recommended in a real project.
interface bus_if (input pclk);
logic [31:0] paddr;
logic [31:0] pwdata;
logic [31:0] prdata;
logic pwrite;
logic psel;
logic penable;
logic presetn;
endinterface
SystemVerilog also has many other 2-state data types in addition to all the data types supported by Verilog. Most commonly used data types in modern testbenches are bit
, int
, logic
and byte
.
Integer
Integers are numbers without a fractional part or in other words, they are whole numbers. SystemVerilog has three new signed data types to hold integer values each with a different size. The smallest is shortint
which can range from -32768 to 32767, and the largest is longint
. The sign can be explicitly defined using the keywords signed
and unsigned
. Also they can be converted into one another by casting.
// ubyte is converted to signed type and assigned to si
si = signed' (ubyte);
Signed
By default, integer variables are signed in nature and hence can hold both positive and negative values.
module tb;
// By default int data types are signed which means
// that MSB is the sign bit and the integer variables can
// also store negative numbers
shortint var_a;
int var_b;
longint var_c;
initial begin
// Print initial values of the integer variables
$display ("Sizes var_a=%0d var_b=%0d var_c=%0d", $bits(var_a), $bits(var_b), $bits(var_c));
// Assign the maximum value for each of the variables
// MSB of each variable represents the sign bit and is set to 0
// Rest of the bit positions are filled with 1 and hence you
// get the maximum value that these variables can hold
#1 var_a = 'h7FFF;
var_b = 'h7FFF_FFFF;
var_c = 'h7FFF_FFFF_FFFF_FFFF;
// When added a 1, the sign changes to negative because this is a signed variable
#1 var_a += 1; // Value becomes 'h8000 => which is a rollover from + sign to - sign
var_b += 1; // Value becomes 'h8000_0000 => which is a rollover from + sign to - sign
var_c += 1;
end
// Start a monitor to print out values of each variables as they change
initial
$monitor ("var_a=%0d var_b=%0d var_c=%0d", var_a, var_b, var_c);
endmodule
The $bits
system task returns the number of bits in a variable. Note that var_a, var_b and var_c roll over to the negative side.
Sizes var_a=16 var_b=32 var_c=64 var_a=0 var_b=0 var_c=0 var_a=32767 var_b=2147483647 var_c=9223372036854775807 var_a=-32768 var_b=-2147483648 var_c=-9223372036854775808
SystemVerilog is an extension to Verilog and is also used as an HDL. Verilog has reg
and wire
data-types to describe hardware behavior. Since verification of hardware can become more complex and demanding, datatypes in Verilog are not sufficient to develop efficient testbenches and testcases. Hence SystemVerilog has extended Verilog by adding more C like data-types for better encapsulation and compactness.
Click here to refresh Verilog Data Types
The image shown below is a comprehensive list of the major basic data types available in SystemVerilog.

There are many built-in methods in SystemVerilog to help in array searching and ordering.
Array manipulation methods simply iterate through the array elements and each element is used to evaluate the expression specified by the with
clause. The iterator argument specifies a local variable that can be used within the with
expression to refer to the current element in the iteration. If an argument is not provided, item is the name used by default.
Specifying an iterator argument without the
with
clause is illegal.
Array Locator Methods
The with
clause and expresison is mandatory for some of these methods and for some others its optional.
Mandatory 'with' clause
These methods are used to filter out certain elements from an existing array based on a given expression. All such elements that satisfy the given expression is put into an array and returned. Hence the with
clause is mandatory for the following methods.
Method name | Description |
---|---|
find() | Returns all elements satisfying the given expression |
find_index() | Returns the indices of all elements satisfying the given expression |
find_first() | Returns the first element satisfying the given expression |
find_first_index() | Returns the index of the first element satisfying the given expression |
find_last() | Returns the last element satisfying the given expression |
find_last_index() | Returns the index of the last element satisfying the given expression |
module tb;
int array[9] = '{4, 7, 2, 5, 7, 1, 6, 3, 1};
int res[$];
initial begin
res = array.find(x) with (x > 3);
$display ("find(x) : %p", res);
res = array.find_index with (item == 4);
$display ("find_index : res[%0d] = 4", res[0]);
res = array.find_first with (item < 5 & item >= 3);
$display ("find_first : %p", res);
res = array.find_first_index(x) with (x > 5);
$display ("find_first_index: %p", res);
res = array.find_last with (item <= 7 & item > 3);
$display ("find_last : %p", res);
res = array.find_last_index(x) with (x < 3);
$display ("find_last_index : %p", res);
end
endmodule
ncsim> run find(x) : '{4, 7, 5, 7, 6} find_index : res[0] = 4 find_first : '{4} find_first_index: '{1} find_last : '{6} find_last_index : '{8} ncsim: *W,RNQUIE: Simulation is complete.
Optional 'with' clause
Methods | Description |
---|---|
min() | Returns the element with minimum value or whose expression evaluates to a minimum |
max() | Returns the element with maximum value or whose expression evaluates to a maximum |
unique() | Returns all elements with unique values or whose expression evaluates to a unique value |
unique_index() | Returns the indices of all elements with unique values or whose expression evaluates to a unique value |
module tb;
int array[9] = '{4, 7, 2, 5, 7, 1, 6, 3, 1};
int res[$];
initial begin
res = array.min();
$display ("min : %p", res);
res = array.max();
$display ("max : %p", res);
res = array.unique();
$display ("unique : %p", res);
res = array.unique(x) with (x < 3);
$display ("unique : %p", res);
res = array.unique_index;
$display ("unique_index : %p", res);
end
endmodule
ncsim> run min : '{1} max : '{7} unique : '{4, 7, 2, 5, 1, 6, 3} unique : '{4, 2} unique_index : '{0, 1, 2, 3, 5, 6, 7} ncsim: *W,RNQUIE: Simulation is complete.
Array Ordering Methods
These methods operate and alter the array directly.
Method | Description |
---|---|
reverse() | Reverses the order of elements in the array |
sort() | Sorts the array in ascending order, optionally using with clause |
rsort() | Sorts the array in descending order, optionally using with clause |
shuffle() | Randomizes the order of the elements in the array. with clause is not allowed here. |
Example
module tb;
int array[9] = '{4, 7, 2, 5, 7, 1, 6, 3, 1};
initial begin
array.reverse();
$display ("reverse : %p", array);
array.sort();
$display ("sort : %p", array);
array.rsort();
$display ("rsort : %p", array);
for (int i = 0; i < 5; i++) begin
array.shuffle();
$display ("shuffle Iter:%0d = %p", i, array);
end
end
endmodule
ncsim> run reverse : '{1, 3, 6, 1, 7, 5, 2, 7, 4} sort : '{1, 1, 2, 3, 4, 5, 6, 7, 7} rsort : '{7, 7, 6, 5, 4, 3, 2, 1, 1} shuffle Iter:0 = '{6, 7, 1, 7, 3, 2, 1, 4, 5} shuffle Iter:1 = '{5, 1, 3, 4, 2, 7, 1, 7, 6} shuffle Iter:2 = '{3, 1, 7, 4, 6, 7, 1, 2, 5} shuffle Iter:3 = '{6, 4, 7, 3, 1, 7, 5, 2, 1} shuffle Iter:4 = '{3, 6, 2, 5, 4, 7, 7, 1, 1} ncsim: *W,RNQUIE: Simulation is complete.
Using array ordering on classes
class Register;
string name;
rand bit [3:0] rank;
rand bit [3:0] pages;
function new (string name);
this.name = name;
endfunction
function void print();
$display("name=%s rank=%0d pages=%0d", name, rank, pages);
endfunction
endclass
module tb;
Register rt[4];
string name_arr[4] = '{"alexa", "siri", "google home", "cortana"};
initial begin
$display ("
-------- Initial Values --------");
foreach (rt[i]) begin
rt[i] = new (name_arr[i]);
rt[i].randomize();
rt[i].print();
end
$display ("
--------- Sort by name ------------");
rt.sort(x) with (x.name);
foreach (rt[i]) rt[i].print();
$display ("
--------- Sort by rank, pages -----------");
rt.sort(x) with ( {x.rank, x.pages});
foreach (rt[i]) rt[i].print();
end
endmodule
ncsim> run -------- Initial Values -------- name=alexa rank=12 pages=13 name=siri rank=6 pages=12 name=google home rank=12 pages=13 name=cortana rank=7 pages=11 --------- Sort by name ------------ name=alexa rank=12 pages=13 name=cortana rank=7 pages=11 name=google home rank=12 pages=13 name=siri rank=6 pages=12 --------- Sort by rank, pages ----------- name=siri rank=6 pages=12 name=cortana rank=7 pages=11 name=alexa rank=12 pages=13 name=google home rank=12 pages=13 ncsim: *W,RNQUIE: Simulation is complete.
Array Reduction Methods
Method | Description |
---|---|
sum() | Returns the sum of all array elements |
product() | Returns the product of all array elements |
and() | Returns the bitwise AND (&) of all array elements |
or() | Returns the bitwise OR (|) of all array elements |
xor() | Returns the bitwise XOR (^) of all array elements |
module tb;
int array[4] = '{1, 2, 3, 4};
int res[$];
initial begin
$display ("sum = %0d", array.sum());
$display ("product = %0d", array.product());
$display ("and = 0x%0h", array.and());
$display ("or = 0x%0h", array.or());
$display ("xor = 0x%0h", array.xor());
end
endmodule
ncsim> run sum = 10 product = 24 and = 0x0 or = 0x7 xor = 0x4 ncsim: *W,RNQUIE: Simulation is complete.