Part 11 · Senior Prep · Intermediate
Hang Triage: Objections, Handshakes, and drain_time
Completion-bucket playbook for simulations that never end — objection leaks, sequence/driver handshake stalls, fork/join traps, and drain_time extensions.
Completion bucket first moves
A hang means run_phase never completes . The two dominant causes are objection imbalance and sequence/driver handshake deadlock.
[DEBUG][SENIOR][UVM] hang triage flow
1) +UVM_OBJECTION_TRACE +UVM_PHASE_TRACE
2) find last phase activity — did run_phase start?
3) display_objections — who holds count > 0?
4) grep "get_next_item" / "item_done" — driver stuck?
5) check fork/join_any without disable forksimv +UVM_TESTNAME=stress_test \
+UVM_OBJECTION_TRACE \
+UVM_PHASE_TRACE \
+UVM_VERBOSITY=UVM_MEDIUM \
-l hang.log
grep -E "OBJECTION|get_next_item|item_done|run_phase" hang.log | tail -40// objection audit at strategic points
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this, "test start");
main_vseq.start(env.v_sqr);
phase.drop_objection(this, "test done"); // must always run
endtaskKey takeaways
Objection trace is the highest-leverage hang tool — use it first.
Driver waiting on get_next_item with no running sequence = handshake stall.
Every raise must have a matching drop on all paths including errors.
Common pitfalls
Adding arbitrary # delays instead of fixing objection balance.
Debugging waves before reading +UVM_OBJECTION_TRACE output.
fork/join_any timeout branch that forgets disable fork — zombie threads.
Objection leak patterns
Most objection leaks come from a small set of repeatable patterns. Recognize them from trace output.
Common leak sources
[DEBUG][SENIOR][UVM] objection leak catalog
pattern trace signature
sequence fork without join seq raises, child never drops
early return before drop raise then UVM_FATAL, no drop
drain_time never expires count hits 0 but phase waits
sub-component hidden raise env/agent raises, test unaware
duplicate raise same reason count stuck at 1 forever// safe pattern: single owner of test objection
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this, "main");
fork
begin
run_stimulus();
end
begin
wait_for_quiescence();
end
join
phase.drop_objection(this, "main");
endtask
// anti-pattern: raise in sequence AND test
task body();
if (starting_phase != null)
starting_phase.raise_objection(this); // often forgotten dropHandshake stall triage
Log before and after get_next_item in the driver.
Confirm a sequence is running on that sequencer (not blocked on lock).
Check item_done is called on all driver paths including error.
Verify reset did not kill the sequence mid-handshake.
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
`uvm_info("DRV", $sformatf("item=%s", req.convert2string()), UVM_MEDIUM)
drive_item(req);
seq_item_port.item_done();
end
endtask[DEBUG] handshake evidence
last log = "waiting get_next_item"
and no "got item" within N us
-> sequence not producing OR wrong sequencerCommon pitfalls
Raising objections in both test and sequences without documented ownership.
Using phase_ready_to_end extensions without understanding drain_time.
Ignoring sub-environment components that raise their own objections.