🔧 UVM do_ Methods#
🚀 Introduction#
In UVM, methods prefixed with do_
are virtual methods designed to be overridden by the user. These methods are defined in UVM base classes and allow you to implement your own logic while keeping the standard UVM flow intact.
This concept typically follows the template method pattern, providing customization points without breaking the default flow.
🗂️ uvm_object do_ Methods#
📝 do_copy()
#
This method handles object copying. It’s automatically called by the copy()
method.
class my_transaction extends uvm_sequence_item;
rand bit [31:0] address;
rand bit [31:0] data;
rand opcode_e opcode;
time timestamp;
function void do_copy(uvm_object rhs);
my_transaction rhs_txn;
super.do_copy(rhs);
if (!$cast(rhs_txn, rhs)) begin
`uvm_error("COPY_ERROR", "Failed to cast object in do_copy")
return;
end
// Custom copy logic
this.address = rhs_txn.address;
this.data = rhs_txn.data;
this.opcode = rhs_txn.opcode;
this.timestamp = $time; // Set current time
endfunction
`uvm_object_utils(my_transaction)
endclass
🧐 do_compare()
#
Handles object comparison. It’s automatically called by the compare()
method.
class my_transaction extends uvm_sequence_item;
rand bit [31:0] address;
rand bit [31:0] data;
rand opcode_e opcode;
bit ignore_timestamp = 1; // Control flag
function bit do_compare(uvm_object rhs, uvm_comparer comparer);
my_transaction rhs_txn;
bit result = super.do_compare(rhs, comparer);
if (!$cast(rhs_txn, rhs)) begin
`uvm_error("COMPARE_ERROR", "Failed to cast object in do_compare")
return 0;
end
// Custom comparison logic
result &= (this.address == rhs_txn.address);
result &= (this.data == rhs_txn.data);
result &= (this.opcode == rhs_txn.opcode);
// Optionally compare timestamp
if (!ignore_timestamp)
result &= (this.timestamp == rhs_txn.timestamp);
return result;
endfunction
`uvm_object_utils(my_transaction)
endclass
📋 do_print()
#
Customizes object printing, used by print()
and sprint()
.
class cpu_instruction extends uvm_sequence_item;
rand bit [31:0] instruction;
rand bit [4:0] rs, rt, rd;
string mnemonic;
function void do_print(uvm_printer printer);
super.do_print(printer);
// Custom print formatting
printer.print_string("mnemonic", mnemonic);
printer.print_field("instruction", instruction, 32, UVM_HEX);
printer.print_field("rs", rs, 5, UVM_DEC);
printer.print_field("rt", rt, 5, UVM_DEC);
printer.print_field("rd", rd, 5, UVM_DEC);
// Decoded instruction
string decoded = decode_instruction();
printer.print_string("decoded", decoded);
endfunction
function string decode_instruction();
return $sformatf("%s r%0d, r%0d, r%0d", mnemonic, rd, rs, rt);
endfunction
`uvm_object_utils(cpu_instruction)
endclass
📦 do_pack()
and do_unpack()
#
For serialization and deserialization.
class network_packet extends uvm_sequence_item;
bit [47:0] dest_mac;
bit [47:0] src_mac;
bit [15:0] ethertype;
byte payload[$];
bit [31:0] crc;
function void do_pack(uvm_packer packer);
super.do_pack(packer);
// Pack in network byte order
packer.pack_field(dest_mac, 48);
packer.pack_field(src_mac, 48);
packer.pack_field(ethertype, 16);
// Pack payload size then payload
packer.pack_field(payload.size(), 32);
foreach (payload[i])
packer.pack_field(payload[i], 8);
packer.pack_field(crc, 32);
endfunction
function void do_unpack(uvm_packer packer);
int payload_size;
super.do_unpack(packer);
dest_mac = packer.unpack_field(48);
src_mac = packer.unpack_field(48);
ethertype = packer.unpack_field(16);
// Unpack payload
payload_size = packer.unpack_field(32);
payload = new[payload_size];
foreach (payload[i])
payload[i] = packer.unpack_field(8);
crc = packer.unpack_field(32);
endfunction
`uvm_object_utils(network_packet)
endclass
💡 Summary#
🔹 do_
methods let you customize object behavior while preserving UVM’s standard workflow.
🔹 They’re typically called automatically by higher-level UVM methods like copy()
, compare()
, print()
, etc.
🔹 Use them to integrate custom behavior, additional fields, or specialized functionality into your verification components.