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

  1. Each raise_objection increments the source count for the caller component.

  2. Each drop_objection decrements the source count for the caller component.

  3. Source count cannot go below zero — excess drops generate UVM warnings.

  4. Total count at any node = own source + sum of children's totals.

  5. Phase root total = 0 is the condition for starting drain time.

diagram
[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:

systemverilog
// 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
diagram
[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 reason
  • set_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

systemverilog
// 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
diagram
[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 drops
  • One 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

systemverilog
phase.drop_objection(this, "oops");
// UVM_WARNING: drop without matching raise — ignored

Raise 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

systemverilog
task pre_body();
  if (starting_phase != null)  // ALWAYS check — null in non-phase context
    starting_phase.raise_objection(this, "seq");
endtask

all_dropped event

systemverilog
// 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);
endtask

Key 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.