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.

systemverilog
// 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
diagram
[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 paths
  • The 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.

diagram
[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_phase
systemverilog
class 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
endclass
  • Sequences 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:

systemverilog
// 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
diagram
[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.

bash
# 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)
systemverilog
// 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()))
endtask

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