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]