Part 2 · Phases & Lifecycle · Intermediate
Raise & Drop Basics: The Objection Reference Count
The raise_objection/drop_objection API, reason strings, global counting semantics, and the rules every verification engineer must follow.
Objections are a reference count
An objection tells UVM: 'I have work that requires this phase to stay alive.' Each raise increments a global counter for that phase; each drop decrements it. While the counter is greater than zero, the phase cannot end.
// API (called on the phase handle passed into run_phase)
phase.raise_objection(uvm_component caller, string description = "");
phase.drop_objection(uvm_component caller, string description = "");
// Typical usage in a test
task run_phase(uvm_phase phase);
phase.raise_objection(this, "running main sequence");
main_seq.start(env.v_sqr);
phase.drop_objection(this, "main sequence complete");
endtask[PHASE] objection counter state machine
count = 0 phase CAN end (subject to drain_time)
count > 0 phase MUST stay alive
raise(this, "reason") → count++
drop(this, "reason") → count--
invariant: every raise needs a matching drop on ALL exit pathsThe caller argument identifies who raised — used in traces and hierarchy propagation.
The description string appears in +UVM_OBJECTION_TRACE output — always provide one.
Raise before time-consuming work; drop after it completes.
Global counting across components
The objection count is global for the phase — not per-component. If the test raises (count=1) and a background sequence also raises (count=2), the phase stays alive until both drop.
[PHASE][RUN] multi-raiser timeline
time event count phase state
──── ───────────────────────────────── ───── ───────────
0ns test raises "main seq" 1 alive
0ns bg_traffic_seq raises in pre_body 2 alive
200ns test drops "main seq" 1 alive (bg still running)
800ns bg_traffic_seq drops in post_body 0 ENDS → extract_phaseclass bg_traffic_seq extends uvm_sequence #(pkt_item);
`uvm_object_utils(bg_traffic_seq)
task pre_body();
if (starting_phase != null)
starting_phase.raise_objection(this, "background traffic");
endtask
task body();
repeat (cfg.num_bg_pkts) send_packet();
endtask
task post_body();
if (starting_phase != null)
starting_phase.drop_objection(this, "background traffic done");
endtask
endclassSequences use starting_phase.raise/drop in pre_body/post_body for self-contained duration.
Multiple simultaneous raisers are normal — the phase ends only when all drop.
Document who owns which objection to keep traces readable.
Safe patterns: drop on every exit path
The #1 objection bug is a raise without a matching drop on an error or timeout branch. Structure code so drop always executes:
// PATTERN 1: drop after fork/join regardless of outcome
task run_phase(uvm_phase phase);
phase.raise_objection(this, "traffic");
fork
run_stimulus();
join
phase.drop_objection(this, "traffic done"); // always reached
endtask
// PATTERN 2: flag + unified drop point
task run_phase(uvm_phase phase);
bit done = 0;
phase.raise_objection(this, "traffic");
fork
begin
if (!run_stimulus()) `uvm_error("RUN", "stimulus failed");
done = 1;
end
begin #(timeout_ns); if (!done) `uvm_error("RUN", "timeout"); end
join_any
disable fork;
phase.drop_objection(this, "traffic exit");
endtask
// PATTERN 3: sequence pre_body/post_body (automatic pairing)
// raise in pre_body, drop in post_body — UVM calls these reliably[PHASE] exit-path checklist
success path → drop ✓
uvm_error path → drop ✓ (errors do NOT auto-drop)
timeout path → drop ✓
return/break → drop ✓
disable fork → drop ✓ (drop AFTER disable fork)uvm_error does not drop objections — you must still drop explicitly.
Prefer pre_body/post_body in sequences for automatic raise/drop pairing.
Watchdog tasks should log convert2string(), not forcibly drop others' objections.
Reason strings and traceability
Always pass a descriptive reason string. When simulation hangs, +UVM_OBJECTION_TRACE shows exactly which component and reason still holds the phase open.
# Enable objection tracing at simulation command line
simv +UVM_OBJECTION_TRACE +UVM_VERBOSITY=UVM_MEDIUM
# Example trace output (simplified)
# @ 900ns: DROP base_test.main sequence complete count=1
# @ 900ns: count=1 base_test.bg_traffic_seq (background traffic)// Programmatic snapshot when watchdog fires
task objection_watchdog(uvm_phase phase);
#(cfg.timeout_ns * 1ns);
`uvm_error("WD", $sformatf("phase stuck — objections:\n%s",
phase.phase_done.convert2string()))
endtaskKey takeaways
raise_objection increments, drop_objection decrements the global phase counter.
The phase stays alive while count > 0; it ends when count reaches zero.
Always drop on every exit path — errors and timeouts included.
Use descriptive reason strings and +UVM_OBJECTION_TRACE for debug.
Common pitfalls
raise in an if-block but drop outside without guarding — count mismatch.
Using starting_phase without checking for null — crashes in non-phase contexts.
Dropping more times than raising — negative count warnings or early phase end.
Generic reason strings like 'done' — useless when six components object.