Part 5 · Sequences · Intermediate
Field Macros: Print, Compare, Copy Automation
uvm_field_* macros inside uvm_object_utils — structured debug, scoreboard compare, and deep copy without hand-written functions.
Why field macros exist
Every verification engineer spends hours printing transactions and comparing expected vs actual. Without automation, each item class needs hand-written print(), compare(), and copy() functions that drift out of sync whenever someone adds a field. UVM field macros register members once inside `uvm_object_utils_begin/end and the library generates consistent implementations for all registered fields.
The investment pays off immediately in debug: a single req.print() at UVM_MEDIUM verbosity shows every payload field with consistent formatting. In scoreboards, exp.compare(act) returns 0 on mismatch and prints the first differing field — no custom compare boilerplate per protocol.
Field macros also enable recording, pack/unpack for replay, and factory integration. Skipping registration on a field means it silently disappears from compare and copy — a common scoreboard bug source.
[ITEM] field macro benefits
ONE registration → print | compare | copy | record | pack/unpack
Add new field? Register with `uvm_field_* — all utilities update
Skip registration? Field invisible to compare/copy — silent scoreboard bugsComplete item with field macros
Here is a realistic AXI-style item with randomized fields, inline constraints, and full field registration:
class axi_item extends uvm_sequence_item;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit write;
rand bit [2:0] burst_len;
rand bit [3:0] id;
constraint legal_addr {
addr[1:0] == 2'b00;
addr inside {[32'h0000:32'hFFFF]};
}
`uvm_object_utils_begin(axi_item)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(write, UVM_ALL_ON)
`uvm_field_int(burst_len, UVM_ALL_ON)
`uvm_field_int(id, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "axi_item");
super.new(name);
endfunction
endclass`uvm_object_utils_begin/end` wraps all field registrations for this class.
`uvm_field_int(field, flags)` for integral members — most common macro.
UVM_ALL_ON enables print, compare, copy, and record — good default for payload.
Constraints live outside macros — they control randomize(), not automation.
Print and sprint for debug
During bring-up, printing items is faster than waveform hunting. The UVM print format is consistent across all registered classes — grep-friendly in regression logs.
axi_item req = axi_item::type_id::create("req");
assert(req.randomize() with { write == 1; addr == 32'h1000; });
req.print(); // prints at current verbosity to log
string s = req.sprint();
`uvm_info("TXN", s, UVM_MEDIUM)
// Typical output:
// axi_item@1234
// addr 'h1000
// data 'h7a3f2b1c
// write 'h1
// burst_len 'h0
// id 'h5[UVM] print in the stimulus path
[SEQ] body():
start_item(req);
req.randomize();
req.print(); ← verify BEFORE finish_item
finish_item(req);
[DRV] run_phase:
get_next_item(req);
req.print(); ← same object, same format
drive(req);
item_done();Verbosity control
print() respects component verbosity — use UVM_HIGH for per-beat, UVM_MEDIUM for summaries.
sprint() returns string — embed in custom messages or write to file.
Set type_print hook for custom formatting on specific classes (advanced).
Compare for scoreboards
Scoreboards call compare on expected vs actual items. The macro-generated compare walks registered fields in order and prints the first mismatch — essential for self-checking at scale.
function void compare_pair(axi_item exp, axi_item act);
if (!act.compare(exp)) begin
mismatch_count++;
`uvm_error("SCB", $sformatf("mismatch exp=%s act=%s",
exp.sprint(), act.sprint()))
end else begin
match_count++;
end
endfunction[ITEM] compare walkthrough — id=5 addr mismatch
exp: addr=0x1000 data=0xAA id=5
act: addr=0x1004 data=0xAA id=5
act.compare(exp):
addr: 'h1000 vs 'h1004 → MISMATCH reported
(data, write, id not checked — first diff stops)
Fix: register ALL payload fields — unregistered addr alias won't compareCommon field flag choices
UVM_ALL_ON — print, compare, copy, record — default for payload fields.
UVM_NOCOMPARE — printed but skipped in compare (e.g., cycle timestamps).
UVM_NOPRINT — compared and copied but hidden from print (large arrays).
UVM_NOPACK — skip in pack/unpack when field not needed for replay.
UVM_DEC — display as decimal instead of hex for small counters.
Copy and clone
Monitors reuse transaction objects; scoreboards queue snapshots. copy() duplicates all registered fields into an existing object; clone() is a convenience wrapper. Never queue the live handle.
axi_item exp = axi_item::type_id::create("exp");
exp.copy(req); // deep copy registered fields req → exp
axi_item snap = axi_item::type_id::create("snap");
req.clone(snap); // equivalent: snap.copy(req) after create
// Scoreboard pattern
function void write_exp(axi_item t);
axi_item snap = axi_item::type_id::create("snap");
snap.copy(t);
exp_queue.push_back(snap);
endfunction[ITEM] copy vs handle queue — silent bug
WRONG: exp_queue.push_back(req); ← monitor overwrites req next cycle
RIGHT: snap = create(); snap.copy(req); exp_queue.push_back(snap);
All queued entries must be independent snapshotscopy/clone duplicate all UVM_FIELD-registered members only.
Unregistered fields are NOT copied — register everything that matters.
For nested objects, use uvm_field_object with UVM_ALL_ON.
APB item field macro reference
Complete APB item showing practical flag choices for a mix of payload and debug-only fields:
class apb_item extends uvm_sequence_item;
rand bit [31:0] addr, data;
rand bit write;
bit [31:0] rdata;
time sample_time; // monitor-only — don't compare
`uvm_object_utils_begin(apb_item)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(write, UVM_ALL_ON)
`uvm_field_int(rdata, UVM_ALL_ON)
`uvm_field_int(sample_time, UVM_ALL_ON | UVM_NOCOMPARE)
`uvm_object_utils_end
endclassKey takeaways
Register every payload field with uvm_field_* — unregistered fields vanish from compare/copy.
UVM_ALL_ON is the right default; use NOCOMPARE/NOPRINT flags surgically.
print/sprint for debug; compare for scoreboards; copy/clone before queueing.
Common pitfalls
Adding a field without uvm_field_* — compare silently passes on mismatched new field.
UVM_NOCOMPARE on payload fields — hides real DUT bugs in scoreboard.
Hand-written compare that diverges from field macros — maintenance nightmare.
copy without type_id::create on destination — factory overrides skipped.