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
 Simulation Log
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
 Simulation Log
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]