Part 2 · Phases & Lifecycle · Intermediate

Objection Hierarchy Propagation: Up the Component Tree

How a raise at a leaf component propagates to the phase root, what total vs source count means, and propagation implications for debug.

Raises propagate upward

When a component calls phase.raise_objection(this, desc), UVM increments the objection count not only for that component but propagates the raise up through its parent chain — agent → env → test — until it reaches the phase root objection.

diagram
[PHASE][UVM] propagation chain

  driver.raise(this, "driving")
       │
       ▼
  agent  (agent's total count now includes driver's objection)
       │
       ▼
  env    (env's total count includes agent's)
       │
       ▼
  test   (test's total count includes env's)
       │
       ▼
  phase root  (global count for this phase)

  phase ends ONLY when phase root count = 0
  • Every raise at any hierarchy level contributes to the same phase root count.

  • The caller argument (this) identifies the source component for tracing.

  • Parents don't need to raise separately — child raises propagate automatically.


Source count vs total count

UVM tracks two counts per component: source count (raises made directly by this component) and total count (includes all descendants' propagated raises).

systemverilog
// driver raises directly
driver.raise_objection(this, "driving");  // driver source=1, total=1
                                           // agent source=0, total=1
                                           // env  source=0, total=1

// agent ALSO raises directly
agent.raise_objection(this, "agent active"); // driver source=1, total=1
                                             // agent source=1, total=2
                                             // env  source=0, total=2

// driver drops
driver.drop_objection(this, "driving");    // driver source=0, total=0
                                           // agent source=1, total=1  ← still alive!
                                           // env  source=0, total=1
diagram
[PHASE][UVM] source vs total

  source count: raises/drops made BY this component directly
  total count:  source + all children's propagated counts

  phase root total count = sum of all source counts in tree
  phase ends when root total = 0
  • A child drop does NOT end the phase if parent or sibling still objects.

  • convert2string() shows source components with outstanding objections.

  • set() can set source count directly — advanced, use with caution.


Propagation and the component tree

systemverilog
// Typical testbench hierarchy
// test
//   └── env
//         ├── axi_agent
//         │     ├── driver
//         │     └── monitor
//         ├── apb_agent
//         │     ├── driver
//         │     └── monitor
//         └── scoreboard

// Only test and sequences typically raise top-level objections
class my_test extends uvm_test;
  task run_phase(uvm_phase phase);
    fork super.run_phase(phase); join_none
    phase.raise_objection(this, "test duration");  // propagates: test→root
    main_seq.start(env.v_sqr);
    phase.drop_objection(this, "test done");
  endtask
endclass

// Driver does NOT raise — it's a forever loop killed at phase end
class my_driver extends uvm_driver #(item_t);
  task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      drive(req);
      seq_item_port.item_done();
    end
  endtask
endclass
diagram
[PHASE][RUN] who should raise?

  SHOULD raise:  test, sequences (pre_body/post_body), phase-specific tasks
  should NOT:    driver forever loop, monitor forever loop, passive scoreboard
  MAY raise:     env (for config sequences), agent (for sub-phase activity)

  rule: the component that owns DURATION raises
  • Test/sequence owns duration — clearest propagation path.

  • Drivers/monitors as forever loops don't need objections.

  • Env/agent raises only when they own a bounded task (config, reset).


Debug implications of propagation

When reading +UVM_OBJECTION_TRACE, the count shown is the phase root total. convert2string() walks the tree and shows which source components still have non-zero source counts.

diagram
[PHASE][UVM] trace interpretation

  OBJTN RAISE my_test "main seq" count=1
     source: my_test (source=1)
     root total=1

  OBJTN RAISE bg_seq "bg traffic" count=2
     source: bg_seq (source=1), my_test (source=1)
     root total=2

  OBJTN DROP my_test "main done" count=1
     source: bg_seq (source=1) only
     root total=1  ← phase STILL alive

  look for source components with count > 0 in convert2string()
systemverilog
// Dump full objection tree
`uvm_info("OBJ", phase.phase_done.convert2string(), UVM_LOW)

// Output shows hierarchy:
//   my_test: source=0 total=0
//   env: source=0 total=0
//   bg_agent.sqr.bg_seq: source=1 total=1  ← culprit

Key takeaways

  • Raises propagate up the parent chain to the phase root objection.

  • Phase ends when root total count reaches zero — not when any single component drops.

  • Source count = direct raises; total count = source + descendants.

  • convert2string() reveals the exact source component blocking phase end.

Common pitfalls

  • Dropping at child level expecting phase to end — parent/sibling may still object.

  • Raising in driver 'just to keep sim alive' — unclear ownership, messy traces.

  • Misreading trace count as per-component instead of global root total.

  • Deep hierarchy without clear duration owner — multiple unrelated raisers.