Difficulty: Easy · Puzzle 1 of 6 · Topic: Deadlock Scenario Puzzles: Objection and Phase Stalls
run_phase never ends. \`+UVM_OBJECTION_TRACE\` shows one sequence objection remains high. Pinpoint where objection symmetry is broken.
class traffic_seq extends uvm_sequence #(my_item);
\`uvm_object_utils(traffic_seq)
bit enable_traffic; // defaults to 0
virtual task body();
my_item req;
if (starting_phase != null)
starting_phase.raise_objection(this);
if (!enable_traffic)
return; // bug: drop is skipped
repeat (50) \`uvm_do(req)
if (starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclassAny return/disable path after raise must still execute drop.
1) Enable objection trace and identify owner component/object that never drops.
2) Trace sequence control flow for config paths where enable_traffic is 0.
3) Confirm return bypasses drop_objection.
4) Wrap raise/drop with a raised flag and guaranteed cleanup block.
5) Add lint/checklist item: no naked return after raise_objection.Answer: Bug: raise_objection executes but return bypasses drop_objection. Fix: structurally guarantee drop in all exits (raised flag + final cleanup).
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: the early return leaves the count at 1, the guarded version leaves it at 0. Plain-SV model of an objection counter, not the UVM library: the early return leaves the count at 1, the guarded version leaves it at 0.
Checking only the config where traffic is enabled misses it; enable_traffic defaults to 0, so every default run takes the leaking path. (starting_phase is deprecated in UVM 1.2, but the symmetry rule is the same for any objection owner.)
Would you enforce objection symmetry with a reusable utility wrapper around sequence body? Model answer: Yes: put the raise/drop in a base-class body() that calls a virtual do_body(), so the drop runs after do_body returns no matter which path it took; and move guards like enable_traffic before the raise.
The default run takes the path where traffic is disabled, so I read that exit path first. The root cause is that raise_objection executes but an early return bypasses drop_objection, so the phase never ends. I confirm it with the objection trace showing a raise with no matching drop. The fix is to guarantee the drop on every exit, for example with a raised flag and a final cleanup that drops it.