Part 2 · Phases & Lifecycle · Intermediate
Objection Counting Rules: set, get, and Edge Cases
Source vs total counting, set_objection direct manipulation, all_dropped event, multiple drops, and counting edge cases that cause subtle bugs.
Basic counting rules
Each raise_objection increments the source count for the caller component.
Each drop_objection decrements the source count for the caller component.
Source count cannot go below zero — excess drops generate UVM warnings.
Total count at any node = own source + sum of children's totals.
Phase root total = 0 is the condition for starting drain time.
[PHASE][UVM] counting invariants
raise(caller) → caller.source_count++
drop(caller) → caller.source_count-- (warn if already 0)
root.total = Σ all source_counts in tree
invariant: Σ raises == Σ drops (globally, when phase ends cleanly)Mismatched raise/drop pairs generate warnings but don't crash.
Excess drops (drop without raise) warn and are ignored.
The global invariant: total raises must equal total drops at phase end.
set_objection: direct count manipulation
UVM provides set_objection for advanced scenarios where you need to set the source count directly rather than increment/decrement:
// set_objection: set source count to exact value
phase.set_objection(this, 5, "batch raise"); // source count = 5
// must later:
phase.set_objection(this, 0, "batch clear"); // source count = 0
// Equivalent to 5 raises followed by 5 drops, but atomic
// RARELY needed — prefer explicit raise/drop pairs for clarity[PHASE] raise/drop vs set
raise/drop: incremental, traceable, standard pattern
set(n): direct assignment, harder to trace, advanced only
recommendation: always use raise/drop unless you have a specific reasonset_objection is rarely needed in production testbenches.
If using set, always set back to 0 explicitly.
set bypasses trace granularity — harder to debug.
Multiple raises from same component
// Valid: same component raises multiple times
task run_phase(uvm_phase phase);
phase.raise_objection(this, "part A");
run_part_a();
phase.raise_objection(this, "part B"); // source count now 2
run_part_b();
phase.drop_objection(this, "part B"); // source count now 1
phase.drop_objection(this, "part A"); // source count now 0
endtask
// Each raise/drop pair needs its own drop — order of drops doesn't matter
// as long as all are dropped before phase should end[PHASE][RUN] multi-raise from one component
raise "A" → source=1
raise "B" → source=2
drop "B" → source=1 ← phase STILL alive
drop "A" → source=0 ← NOW drain time starts
trace shows both reason strings — easy to identify partial dropsOne component can have multiple simultaneous objections.
Each raise needs its own matching drop — not one drop for all.
Reason strings distinguish multiple raises from same component.
Edge cases and gotchas
Drop without raise
phase.drop_objection(this, "oops");
// UVM_WARNING: drop without matching raise — ignoredRaise in build_phase (illegal context)
Objections only work in task phases. Calling raise in a function phase (build, connect) has no effect or generates an error.
Sequence starting_phase null
task pre_body();
if (starting_phase != null) // ALWAYS check — null in non-phase context
starting_phase.raise_objection(this, "seq");
endtaskall_dropped event
// Wait for all objections to drain (advanced synchronization)
task wait_for_quiescence(uvm_phase phase);
phase.phase_done.wait_for_objection_count(0);
// or: wait (phase.phase_done.get_objection_count() == 0);
endtaskKey takeaways
Source count per component; root total = sum of all sources.
Excess drops warn and are ignored — won't crash but indicates a bug.
Multiple raises from one component need multiple matching drops.
Always check starting_phase != null before sequence raise/drop.
Common pitfalls
Double drop on error path — first drop in handler, second in normal path.
raise in function phase — silently does nothing.
set_objection without clear back to 0 — opaque count state.
Assuming drop order matters — it doesn't, but ALL raises must be dropped.