Difficulty: Easy · Puzzle 2 of 6 · Topic: Testbench Debug Puzzles: Why Did the Test Hang?
No PASS banner is printed and the CI harness waits for it, so every seed looks like a 'hang'. Simulation actually ends almost immediately (time 0) and the traffic sequence did not complete. Identify the testbench bug.
class smoke_test extends uvm_test;
\`uvm_component_utils(smoke_test)
virtual task run_phase(uvm_phase phase);
sanity_seq seq;
seq = sanity_seq::type_id::create("seq");
seq.start(env.agent.seqr);
\`uvm_info("TEST", "run_phase done", UVM_LOW)
endtask
endclassDecide who owns end-of-test lifetime while seq is running, and look at the simulation end time.
1) Check phase timeline and see run_phase can complete before downstream monitors/scoreboard finish.
2) Confirm no raise_objection/drop_objection around the sequence body.
3) Add explicit objection in test run_phase or use sequence automatic phase objections intentionally.
4) Re-run and verify test exits only after completion criteria is satisfied.
5) Add a guard log that prints objection count at run_phase entry/exit.Answer: Bug: test run_phase has no objection ownership, so runtime can end before intended completion flow. Fix: bracket seq.start with phase.raise_objection/drop_objection (or equivalent sequence objection policy).
Checked by running the code in Verilator 5 and Icarus Verilog 13; the printed output supports the answer above. Plain-SV model of an objection counter, not the UVM library: with no raise the phase ends at the first check before the sequence finishes. Plain-SV model of an objection counter, not the UVM library: with no raise the phase ends at the first check before the sequence finishes.
It fails on every seed, not some: the phase ends at time 0 because nobody holds an objection. Candidates who call it a scoreboard deadlock never look at the end time, which is the giveaway.
When would you prefer automatic objections on the sequence versus explicit objections in the test? Model answer: Use explicit objections in the test when it orchestrates several sequences and owns end-of-test (one raise before the first, one drop after the last). Use the sequence's automatic phase objection (set_automatic_phase_objection(1), or a default_sequence) for a self-contained sequence that should own its own lifetime so every test that uses it needs no boilerplate. Avoid mixing both on the same sequence without understanding the objection count.
First I check the simulation end time, because a failure on every seed that ends at time zero means nobody is holding the phase open. The root cause is that the test run_phase starts the sequence without raising an objection, so the phase ends before the traffic finishes. I confirm it by turning on objection tracing and seeing no raise at all. The fix is to bracket the sequence start with raise_objection and drop_objection.