Difficulty: Medium · Puzzle 4 of 6 · Topic: Debugging Riddles
Scoreboard queue entries appear to change after insertion. Locate the monitor bug.
task run_phase(uvm_phase phase);
tr t = tr::type_id::create("t");
forever begin
collect_fields(t);
ap.write(t);
end
endtaskAnalysis ports pass object handles, not deep copies.
1) The same object t is created once and reused every iteration, and no new transaction is created per sample.
2) Subscribers store the handle; the next collect_fields overwrites the same object.
3) Scoreboard then sees previously queued entries mutate (in a simulation of the same pattern every queued entry showed the latest value), appearing as random corruption.
4) Best fix: create a new object inside the loop: forever begin tr t = tr::type_id::create("t"); collect_fields(t); ap.write(t); end.
5) Cloning also works, but clone() returns uvm_object, so ap.write(t.clone()) does not compile. Cast it: tr c; collect_fields(t); if (!$cast(c, t.clone())) `uvm_fatal("CAST", "clone type"); ap.write(c);
6) Make sure do_copy deep-copies nested objects (class handles) so a clone is independent; dynamic arrays and queues of plain values are copied by value.Answer: The monitor reuses one transaction handle; create a new tr per sample (or $cast the result of clone()) before ap.write.
Checked by running the code in Verilator 5; the printed output supports the answer above. A plain-SV model of the mechanism (no UVM library): a reused handle leaves every queue entry equal to the last value; a new object or a deep clone per sample keeps 10 20 30; a shallow copy still shares nested objects.
Assuming write() copies the object (it passes a handle) and then 'fixing' it with ap.write(t.clone()), which fails to compile because clone() returns uvm_object and needs $cast.
Which fields require explicit deep-copy logic in `do_copy`? Model answer: only fields that hold class handles. Dynamic arrays, queues, associative arrays and strings are value types, so a plain copy is independent, but an array or queue OF handles still shares the objects. Nested uvm_objects must be created and copied (or cloned), otherwise the copy shares the member with the original.
Entries in the scoreboard queue change after insertion, which tells me they share one object. The monitor creates the transaction once outside the loop and reuses it, and write passes a handle, not a copy, so every queued entry points at the latest data. The fix is to create a new transaction inside the loop on every sample before the write. Cloning also works, but clone returns a uvm_object, so you need a cast. The trap is assuming write copies the object, or writing write of t dot clone, which does not compile.