uvm_object
has many common functions like print, copy and compare that are available to all its child classes and can be used out of the box if UVM automation macros are used inside the class definition. In a previous article , copy
, do_copy
and use of automation macros to print were discussed.
Using automation macros
UVM automation macros can be used for automatic implementation of copy
, print
, compare
and more. In this example, a class called Packet is defined with a variable called m_addr that is registered with automation macros `uvm_field_int
to be included for all the default functions like print, copy, compare, etc. An object of this class is used inside another class Object which has a bunch of other variables of different data types that are also registered using appropriate macros. For example, m_bool is of enum
type and is registered with `uvm_field_enum
macro.
typedef enum {FALSE, TRUE} e_bool;
class Packet extends uvm_object;
rand bit[15:0] m_addr;
virtual function string convert2string();
string contents;
contents = $sformatf("m_addr=0x%0h", m_addr);
endfunction
`uvm_object_utils_begin(Packet)
`uvm_field_int(m_addr, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "Packet");
super.new(name);
endfunction
endclass
class Object extends uvm_object;
rand e_bool m_bool;
rand bit[3:0] m_mode;
string m_name;
rand Packet m_pkt;
function new(string name = "Object");
super.new(name);
m_name = name;
m_pkt = Packet::type_id::create("m_pkt");
m_pkt.randomize();
endfunction
`uvm_object_utils_begin(Object)
`uvm_field_enum(e_bool, m_bool, UVM_DEFAULT)
`uvm_field_int (m_mode, UVM_DEFAULT)
`uvm_field_string(m_name, UVM_DEFAULT)
`uvm_field_object(m_pkt, UVM_DEFAULT)
`uvm_object_utils_end
endclass
Let us create a base class and create two objects obj1 and obj2 , randomize both and print them. Then compare both objects to see what the result is. Then copy each variable from obj2 from obj1 including the nested class object of type Packet until the comparison errors are resolved.
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);
// Create two objects, randomize them and print the contents
Object obj1 = Object::type_id::create("obj1");
Object obj2 = Object::type_id::create("obj2");
obj1.randomize();
obj1.print();
obj2.randomize();
obj2.print();
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_bool", UVM_LOW)
obj2.m_bool = obj1.m_bool;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_mode", UVM_LOW)
obj2.m_mode = obj1.m_mode;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_name", UVM_LOW)
obj2.m_name = obj1.m_name;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_pkt.m_addr", UVM_LOW)
obj2.m_pkt.m_addr = obj1.m_pkt.m_addr;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
endfunction
function void _compare(Object obj1, obj2);
if (obj2.compare(obj1))
`uvm_info("TEST", "obj1 and obj2 are same", UVM_LOW)
else
`uvm_info("TEST", "obj1 and obj2 are different", UVM_LOW)
endfunction
endclass
module tb;
initial begin
run_test("base_test");
end
endmodule
UVM_INFO @ 0: reporter [RNTST] Running test base_test... ---------------------------------- Name Type Size Value ---------------------------------- obj1 Object - @1903 m_bool e_bool 32 TRUE m_mode integral 4 'h9 m_name string 4 obj1 m_pkt Packet - @1905 m_addr integral 16 'h3cb6 ---------------------------------- ---------------------------------- Name Type Size Value ---------------------------------- obj2 Object - @1907 m_bool e_bool 32 FALSE m_mode integral 4 'hf m_name string 4 obj2 m_pkt Packet - @1908 m_addr integral 16 'h64c1 ---------------------------------- UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(351) @ 0: reporter [MISCMP] Miscompare for obj2.m_bool: lhs = FALSE : rhs = TRUE UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(382) @ 0: reporter [MISCMP] 1 Miscompare(s) for object obj1@1903 vs. obj2@1907 UVM_INFO testbench.sv(71) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(73) @ 0: uvm_test_top [TEST] Copy m_bool UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(351) @ 0: reporter [MISCMP] Miscompare for obj2.m_mode: lhs = 'hf : rhs = 'h9 UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(382) @ 0: reporter [MISCMP] 1 Miscompare(s) for object obj1@1903 vs. obj2@1907 UVM_INFO testbench.sv(78) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(80) @ 0: uvm_test_top [TEST] Copy m_mode UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(351) @ 0: reporter [MISCMP] Miscompare for obj2.m_name: lhs = "obj2" : rhs = "obj1" UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(382) @ 0: reporter [MISCMP] 1 Miscompare(s) for object obj1@1903 vs. obj2@1907 UVM_INFO testbench.sv(85) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(87) @ 0: uvm_test_top [TEST] Copy m_name UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(351) @ 0: reporter [MISCMP] Miscompare for obj2.m_pkt.m_addr: lhs = 'h64c1 : rhs = 'h3cb6 UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_comparer.svh(382) @ 0: reporter [MISCMP] 1 Miscompare(s) for object obj1@1903 vs. obj2@1907 UVM_INFO testbench.sv(92) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(94) @ 0: uvm_test_top [TEST] Copy m_pkt.m_addr UVM_INFO testbench.sv(97) @ 0: uvm_test_top [TEST] obj1 and obj2 are same UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
Automation macros introduce a lot of additional code and is not generally recommended
Using do_compare
Another way is for the user to implement do_compare
method inside the child class and compare the values of each variable and return the result. Note in the following example that uvm_object_utils_*
macros are not used and hence the print()
method we used above will not work as is. Instead another method called convert2string
is implemented for both classes so that it returns the contents in a string format when called.
Just like the way print
calls do_print
method, a compare
calls the do_compare
method and implementation of the function is all that is required to be done.
typedef enum {FALSE, TRUE} e_bool;
class Packet extends uvm_object;
rand bit[15:0] m_addr;
virtual function string convert2string();
string contents;
contents = $sformatf("m_addr=0x%0h", m_addr);
return contents;
endfunction
`uvm_object_utils(Packet)
virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);
bit res;
Packet _pkt;
$cast(_pkt, rhs);
super.do_compare(_pkt, comparer);
res = super.do_compare(_pkt, comparer) &
m_addr == _pkt.m_addr;
`uvm_info(get_name(), $sformatf("In Packet::do_compare(), res=%0b", res), UVM_LOW)
return res;
endfunction
function new(string name = "Packet");
super.new(name);
endfunction
endclass
class Object extends uvm_object;
rand e_bool m_bool;
rand bit[3:0] m_mode;
string m_name;
rand Packet m_pkt;
function new(string name = "Object");
super.new(name);
m_name = name;
m_pkt = Packet::type_id::create("m_pkt");
m_pkt.randomize();
endfunction
// This task is used to print contents
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);
$sformat(contents, "%s %s", contents, m_pkt.convert2string());
return contents;
endfunction
`uvm_object_utils(Object)
// "rhs" does not contain m_bool, m_mode, etc since its a parent
// handle. So cast into child data type and access using child handle
// Copy each field from the casted handle into local variables
virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);
bit res;
Object _obj;
$cast(_obj, rhs);
res = super.do_compare(_obj, comparer) &
m_name == _obj.m_name &
m_mode == _obj.m_mode &
m_bool == _obj.m_bool &
m_pkt.do_compare(_obj.m_pkt, comparer);
`uvm_info(get_name(), $sformatf("In Object::do_compare(), res=%0b", res), UVM_LOW)
return res;
endfunction
endclass
UVM automation macros were used in the previous example and hence the print
method implementation was already done and available to be used. However, when automation is not used, user has to implement either do_print
or atleast fill the convert2string
function so that contents can be printed. We have chosen to do the latter as evident in the code above. So convert2string
function is called in a UVM info statement instead of calling print
.
Then compare the variables, copy each variable from obj1 into obj2 and keep comparing until both objects are the same.
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 obj1 = Object::type_id::create("obj1");
Object obj2 = Object::type_id::create("obj2");
obj1.randomize();
`uvm_info("TEST", $sformatf("Obj1.print: %s", obj1.convert2string()), UVM_LOW)
obj2.randomize();
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_bool", UVM_LOW)
obj2.m_bool = obj1.m_bool;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_mode", UVM_LOW)
obj2.m_mode = obj1.m_mode;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_name", UVM_LOW)
obj2.m_name = obj1.m_name;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
`uvm_info("TEST", "Copy m_pkt.m_addr", UVM_LOW)
obj2.m_pkt.m_addr = obj1.m_pkt.m_addr;
`uvm_info("TEST", $sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
_compare(obj1, obj2);
endfunction
function void _compare(Object obj1, obj2);
if (obj2.compare(obj1))
`uvm_info("TEST", "obj1 and obj2 are same", UVM_LOW)
else
`uvm_info("TEST", "obj1 and obj2 are different", UVM_LOW)
endfunction
endclass
module tb;
initial begin
run_test("base_test");
end
endmodule
UVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO testbench.sv(96) @ 0: uvm_test_top [TEST] Obj1.print: m_name=obj1 m_bool=TRUE m_mode=0x9 m_addr=0x3cb6 UVM_INFO testbench.sv(98) @ 0: uvm_test_top [TEST] Obj2.print: m_name=obj2 m_bool=FALSE m_mode=0xf m_addr=0x64c1 UVM_INFO testbench.sv(30) @ 0: reporter [m_pkt] In Packet::do_compare(), res=0 UVM_INFO testbench.sv(77) @ 0: reporter [obj2] In Object::do_compare(), res=0 UVM_INFO testbench.sv(128) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(102) @ 0: uvm_test_top [TEST] Copy m_bool UVM_INFO testbench.sv(104) @ 0: uvm_test_top [TEST] Obj2.print: m_name=obj2 m_bool=TRUE m_mode=0xf m_addr=0x64c1 UVM_INFO testbench.sv(30) @ 0: reporter [m_pkt] In Packet::do_compare(), res=0 UVM_INFO testbench.sv(77) @ 0: reporter [obj2] In Object::do_compare(), res=0 UVM_INFO testbench.sv(128) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(107) @ 0: uvm_test_top [TEST] Copy m_mode UVM_INFO testbench.sv(109) @ 0: uvm_test_top [TEST] Obj2.print: m_name=obj2 m_bool=TRUE m_mode=0x9 m_addr=0x64c1 UVM_INFO testbench.sv(30) @ 0: reporter [m_pkt] In Packet::do_compare(), res=0 UVM_INFO testbench.sv(77) @ 0: reporter [obj2] In Object::do_compare(), res=0 UVM_INFO testbench.sv(128) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(112) @ 0: uvm_test_top [TEST] Copy m_name UVM_INFO testbench.sv(114) @ 0: uvm_test_top [TEST] Obj2.print: m_name=obj1 m_bool=TRUE m_mode=0x9 m_addr=0x64c1 UVM_INFO testbench.sv(30) @ 0: reporter [m_pkt] In Packet::do_compare(), res=0 UVM_INFO testbench.sv(77) @ 0: reporter [obj2] In Object::do_compare(), res=0 UVM_INFO testbench.sv(128) @ 0: uvm_test_top [TEST] obj1 and obj2 are different UVM_INFO testbench.sv(117) @ 0: uvm_test_top [TEST] Copy m_pkt.m_addr UVM_INFO testbench.sv(119) @ 0: uvm_test_top [TEST] Obj2.print: m_name=obj1 m_bool=TRUE m_mode=0x9 m_addr=0x3cb6 UVM_INFO testbench.sv(30) @ 0: reporter [m_pkt] In Packet::do_compare(), res=1 UVM_INFO testbench.sv(77) @ 0: reporter [obj2] In Object::do_compare(), res=1 UVM_INFO testbench.sv(126) @ 0: uvm_test_top [TEST] obj1 and obj2 are same UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]