Part 10 · Advanced Topics · Intermediate
Objection Propagation Tree
How raise/drop operations propagate from leaves to root, how source counts are tracked, and why hierarchy placement matters.
Mental model: one phase, many sources
Every task phase owns an objection object that tracks counts from many sources. A source is typically a component or sequence handle passed as this in raise/drop calls. UVM tracks both who holds objections and how many objections are still active.
A raise at a leaf component increments the count at that node and propagates upward through each parent until the phase root. A drop walks the same path downward in count. The phase ends only when the root aggregate count reaches zero and any configured post-zero logic completes.
[UVM] propagation tree example
uvm_test_top
└── env
├── agt_a
│ ├── drv_a (raises)
│ └── mon_a
├── agt_b
│ ├── drv_b (raises)
│ └── mon_b
└── scb
If drv_a raises:
drv_a +1
agt_a +1
env +1
test +1
root +1
If drv_b raises too:
root count is now 2
Phase cannot end until BOTH drv_a and drv_b drop.Source object and count ownership
The source argument in raise_objection(source, description, count) is not cosmetic. It is a bookkeeping key. Re-raising from one source and dropping from a different source can leave residual counts that are hard to spot unless you inspect objection traces by source.
task run_phase(uvm_phase phase);
phase.raise_objection(this, "start packet burst");
drive_burst();
phase.drop_objection(this, "burst complete");
endtask
// Multi-count form (less common but legal)
task start_many(uvm_phase phase, int n);
phase.raise_objection(this, "reserve n units", n);
// ... perform n independent work items ...
phase.drop_objection(this, "release n units", n);
endtaskDefault count is 1; multi-count APIs exist but need strict accounting discipline.
Use the same source handle for matching raise/drop unless you intentionally centralize accounting.
Descriptions are for humans in logs; source and count are what affect phase semantics.
Walkthrough: two leaves, staggered completion
This timeline shows why phase completion belongs to the entire hierarchy, not one component.
[CHECK] timeline with staggered drops
T0 drv_a.raise(this) -> root count = 1
T1 drv_b.raise(this) -> root count = 2
T2 drv_a.drop(this) -> root count = 1 (phase still alive)
T3 scoreboard still comparing...
T4 drv_b.drop(this) -> root count = 0 (candidate to end)
T5 drain_time window -> monitors may still publish final txn
T6 no new objections -> phase endsThe critical point is T2: one source finished, but the phase is still active because another source remains. This is why objection analysis must always be global when debugging hangs.
Hierarchy placement trade-off
Leaf-level raises give precise ownership and better traceability.
Top-level raises are simpler but can hide which subcomponent actually leaked a drop.
For teams, choose one pattern and enforce it consistently across agents.
Introspection APIs and practical logging
During bring-up, instrument objection state with display_objections() and contextual logs at phase transitions. This quickly distinguishes a real DUT stall from a verification-level objection leak.
function void phase_started(uvm_phase phase);
if (phase.get_name() == "run") begin
`uvm_info("OBJ", "run phase started", UVM_LOW)
end
endfunction
function void phase_ended(uvm_phase phase);
if (phase.get_name() == "run") begin
`uvm_info("OBJ", "run phase ended", UVM_LOW)
end
endfunction
task report_objections(uvm_phase phase);
if (phase.get_name() == "run")
phase.phase_done.display_objections();
endtask[UVM] practical interpretation
display_objections says:
source=uvm_test_top.env.agt_a.drv count=1
then:
that exact component still holds the phase open.
Do not "fix" by force-dropping elsewhere.
Fix the missing drop where ownership began.Key takeaways
Raise/drop propagates through the component ancestry to the phase root.
Source identity matters: mismatched sources can strand counts.
Root count reaching zero is necessary, not always sufficient (drain/ready_to_end may still apply).
Use objection introspection early in bring-up to validate ownership model.
Common pitfalls
Mixing source handles in raise/drop pairs and creating phantom residual counts.
Assuming one component finishing implies phase completion.
Adding force-drops in tests instead of fixing leak at original owner.