Difficulty: Easy · Puzzle 1 of 6 · Topic: Handle and Copy Puzzles
You clone a transaction before pushing into an expected queue. Predict the print and name the exact ownership bug.
class payload;
rand bit [7:0] data[];
function new();
data = new[2];
endfunction
endclass
class txn extends uvm_sequence_item;
`uvm_object_utils(txn)
rand bit [31:0] addr;
payload pld;
function new(string name = "txn");
super.new(name);
pld = new();
endfunction
function void do_copy(uvm_object rhs);
txn r;
if (!$cast(r, rhs)) return;
super.do_copy(rhs);
addr = r.addr;
pld = r.pld;
endfunction
endclass
initial begin
txn a = txn::type_id::create("a");
txn b = txn::type_id::create("b");
a.pld.data[0] = 8'h11;
b.copy(a);
a.pld.data[0] = 8'hEE;
$display("a=%0h b=%0h", a.pld.data[0], b.pld.data[0]);
endFocus on what `pld = r.pld` copies: object contents or just the handle value.
1) `b.copy(a)` calls `do_copy`, which assigns `b.pld` to the same handle as `a.pld`.
2) After copy, both `a.pld` and `b.pld` refer to one payload object.
3) Updating `a.pld.data[0]` updates shared storage, so `b.pld.data[0]` changes too.
4) Fix by allocating a new payload inside `do_copy` and copying the contents: `pld = new(); pld.data = r.pld.data;` (and `pld = null` when `r.pld` is null). `payload` is a plain class, not a `uvm_object`, so it has no `type_id::create` and no `clone()`; those only work if you make `payload` extend `uvm_object` (with `uvm_object_utils` and its own `do_copy`).Answer: It prints `a=ee b=ee`; the bug is shallow copy of nested handle `pld` inside `do_copy`.
Checked by running the code in Verilator 5; the printed output supports the answer above. A hand-written model of copy/do_copy (not the UVM library): pld = r.pld copies only the handle, so the edit shows through the copy.
`copy()` is only as deep as your `do_copy`: `pld = r.pld` copies a handle value, and `b`'s own freshly built payload is simply orphaned.
Show a `do_compare` snippet that can detect aliasing by checking handle identity in addition to field equality. Model answer: in `do_compare`, after the field checks, add `if (pld == r.pld && pld != null) uvm_report_warning("ALIAS", "pld shared between objects")`; field equality alone passes for aliased objects, so identity (`==` on the handles) is the only way to see the sharing.
It prints a equals EE and b equals EE. My do_copy assigns the nested payload handle instead of copying the payload, so both transactions share one payload object. The trap is that the top-level copy looks correct while the nested handle is still aliased. The fix is to allocate a new payload inside do_copy and copy the contents, with a null check, and then I would re-run the print to confirm b keeps 11.