Difficulty: Medium · Puzzle 3 of 6 · Topic: Handle and Copy Puzzles
A monitor reuses one transaction object every cycle and sends it over analysis port. Why does scoreboard history show all entries with the final sample value?
class mon extends uvm_component;
`uvm_component_utils(mon)
uvm_analysis_port #(tr) ap;
virtual bus_if vif;
tr t;
function new(string name, uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
endfunction
task run_phase(uvm_phase phase);
t = tr::type_id::create("t");
forever begin
@(posedge vif.clk);
t.addr = vif.addr;
ap.write(t);
end
endtask
endclass
function void sb::write(tr t);
exp_q.push_back(t);
endfunctionAsk whether `write()` transfers a new object or the same handle.
1) `ap.write(t)` passes a handle, not a deep-copied object.
2) Scoreboard stores that same handle each cycle, so queue entries alias one object.
3) Next cycle updates `t.addr`, and all previously stored handles appear to change.
4) Clone in monitor before write, or clone in subscriber on receipt.Answer: All entries alias the same reused object handle; the scoreboard never captured immutable snapshots.
Checked by running the code in Verilator 5; the printed output supports the answer above. A hand-written model of a monitor and scoreboard (not the UVM library): write() stores a handle, so a reused object fills the queue with its last value.
Analysis `write()` is a function call that passes a handle: the one object `t` sits in the queue N times, so the history shows the most recent `addr` N times.
Where should cloning happen for best performance and ownership clarity: monitor, analysis FIFO, or scoreboard? Model answer: in the producer. The monitor owns the object and knows when it is finished with it, so it should create a fresh `tr` per sample (cheapest, no clone) and never touch it after `write()`. Cloning in each subscriber costs one copy per subscriber and relies on every subscriber remembering to do it.
Every queued entry ends up looking the same, because the monitor reuses one object and the write call only passes a handle. The scoreboard stores that one handle every cycle, so when the next cycle updates the address, all stored entries appear to change. The trap is thinking the analysis port makes a snapshot, and it does not. I would clone in the monitor before writing, or clone in the subscriber on receipt, and then check the queue entries are distinct objects.