Difficulty: Medium · Puzzle 4 of 6 · Topic: Testbench Debug Puzzles: Why Did the Test Hang?
Seeds that hit the retry path die with a sequencer fatal complaining about item_done with no outstanding get_next_item. Intent of the retry path: complete the first attempt, then re-drive the same item. Find the handshake bug.
virtual task run_phase(uvm_phase phase);
my_item req;
forever begin
seq_item_port.get_next_item(req);
drive_req(req);
if (req.retry) begin
seq_item_port.item_done(); // release first attempt
drive_req(req); // re-drive the same item
end
seq_item_port.item_done(); // second done on retry path
end
endtaskCount handshakes by transaction id: grant, get, done.
1) Enable handshake logging around get_next_item/item_done with the item id, or count calls.
2) A retry item shows one get_next_item and two item_done calls.
3) The first item_done closes the handshake; the second has no outstanding get_next_item, which UVM 1.2 reports as a UVM_FATAL (SQRBADITMDN); older versions may differ. It is a fatal, not a hang.
4) Fix: exactly one item_done at a single exit point, and do the re-drive before it: drive_req(req); if (req.retry) drive_req(req); seq_item_port.item_done();
5) Add a directed retry test and a pairing counter (get_next_item count equals item_done count).Answer: Bug: the retry branch calls item_done and the common exit calls it again, so one get_next_item is closed twice and UVM 1.2 reports a protocol fatal. Fix: re-drive inside the branch and keep a single item_done after it.
Checked by running the code in Verilator 5; the printed output supports the answer above. Plain-SV model of the get_next_item/item_done pairing counters, not the UVM library: the retry path closes one handshake twice.
Dropping the retry behaviour makes the fatal vanish but silently changes the DUT stimulus; also do not expect a hang here, the symptom is the sequencer protocol fatal on the second item_done.
Would you use assertions, sequencer callbacks, or counters to enforce one-to-one handshake pairing? Model answer: A simple counter pair in the driver (opened on get_next_item, closed on item_done) with an end-of-test check and an immediate error if item_done is called at zero is the cheapest and works in every simulator; a sequencer/driver callback gives a reusable hook for all agents; SVA is a poor fit because the handshake is software-level, not signal-level.
I start with the sequencer protocol fatal, because the symptom here is an error on the second item_done, not a hang. The root cause is that the retry branch calls item_done and the common exit calls it again, so one get_next_item is closed twice. I confirm it by counting get_next_item against item_done in the driver log for the failing item. The fix is to re-drive inside the retry branch and keep a single item_done after it, without dropping the retry behaviour.