Part 2 · Phases & Lifecycle · Intermediate
Multi-Component Objections: Coordinating Many Raisers
Patterns for testbenches with multiple simultaneous objectors — ownership conventions, trace readability, env-level coordination, and the objector registry pattern.
Many components, one phase counter
Real testbenches often have multiple simultaneous objectors: the test, background sequences, config sequences, reset monitors, and VIP agents. All contribute to the same phase root count.
[PHASE][RUN][UVM] typical multi-raiser testbench
test.run_phase raise "main vseq" ─┐
bg_traffic_seq raise in pre_body ├─ root count = 3
env.configure (earlier) raise "reg config" ─┘
(if still in same phase)
phase ends when ALL THREE dropDocument every objector and its owner in testbench architecture docs.
Use distinctive reason strings — 'main vseq' not 'running'.
Each objector is independent — one dropping doesn't affect others.
Ownership conventions
[PHASE][UVM] objection ownership matrix
Component Owns objection for Pattern
───────────────── ────────────────────────── ─────────────────────
test main virtual sequence test raise/drop
sequence its own duration pre_body/post_body
env register config (sub-phase) configure_phase raise/drop
agent reset activity (sub-phase) reset_phase raise/drop
driver NEVER (forever loop) no objection
monitor NEVER (forever loop) no objection
scoreboard pending drain (optional) phase_ready_to_end
watchdog NEVER (diagnostic only) no raise/drop// GOOD: clear ownership, distinctive reasons
task run_phase(uvm_phase phase);
fork super.run_phase(phase); join_none
phase.raise_objection(this, "TEST:main_vseq");
main_vseq.start(env.v_sqr);
phase.drop_objection(this, "TEST:main_vseq_done");
// bg seq self-manages: "SEQ:bg_traffic" in pre_body/post_body
bg_traffic_seq::type_id::create("bg").start(env.bg_sqr);
endtaskPrefix reason strings with component role: TEST:, SEQ:, ENV:, AGT:.
One owner per objection — don't split ownership across components.
Passive components (driver/monitor) should not raise.
Env-level coordination pattern
class soc_env extends uvm_env;
// Env owns configure_phase objection — not the test
task configure_phase(uvm_phase phase);
reg_cfg_seq cfg;
phase.raise_objection(this, "ENV:ral_configure");
cfg = reg_cfg_seq::type_id::create("cfg");
cfg.regmodel = regmodel;
cfg.start(reg_sqr);
phase.drop_objection(this, "ENV:ral_configure_done");
endtask
// Env owns reset_phase for all agents
task reset_phase(uvm_phase phase);
phase.raise_objection(this, "ENV:wait_all_reset");
reset_ev.trigger(); // signal all agents
wait (all_agents_reset_done);
phase.drop_objection(this, "ENV:all_reset_done");
endtask
endclass
class soc_test extends uvm_test;
// Test owns ONLY main_phase duration
task main_phase(uvm_phase phase);
fork super.main_phase(phase); join_none
phase.raise_objection(this, "TEST:main_vseq");
soc_vseq.start(env.v_sqr);
phase.drop_objection(this, "TEST:main_vseq_done");
endtask
endclass[PHASE][RUN] layered ownership
reset_phase: ENV owns (coordinates all agents)
configure_phase: ENV owns (RAL programming)
main_phase: TEST owns (virtual sequence)
shutdown_phase: ENV owns (drain all agents)
test stays thin — env handles structural phasesEnv owns structural phases (reset, configure, shutdown).
Test owns stimulus duration (main sequence).
Clear separation makes traces readable and ownership obvious.
Objector registry pattern (advanced)
For complex testbenches, maintain a registry of active objectors for debug visibility:
class objection_registry extends uvm_component;
`uvm_component_utils(objection_registry)
static objection_registry inst;
string active_objectors[$];
function void register(string id, string reason);
active_objectors.push_back({id, ":", reason});
`uvm_info("OBJ_REG", $sformatf("+%s (%0d active)", id, active_objectors.size()), UVM_HIGH)
endfunction
function void unregister(string id);
// remove matching entry
`uvm_info("OBJ_REG", $sformatf("-%s (%0d active)", id, active_objectors.size()), UVM_HIGH)
endfunction
function void report_phase(uvm_phase phase);
if (active_objectors.size() > 0)
`uvm_error("OBJ_REG", $sformatf("leaked objectors: %p", active_objectors))
endfunction
endclass
// Wrap raise/drop with registry calls in a macro or helper
`define RAISE(phase, comp, reason) \
phase.raise_objection(comp, reason); \
objection_registry::inst.register(comp.get_full_name(), reason);
`define DROP(phase, comp, reason) \
phase.drop_objection(comp, reason); \
objection_registry::inst.unregister(comp.get_full_name());Key takeaways
Multiple simultaneous objectors are normal — phase ends when ALL drop.
Establish ownership conventions: test=main, env=structural, seq=self.
Use prefixed reason strings (TEST:, ENV:, SEQ:) for trace readability.
Objector registry pattern catches leaks at report_phase.
Common pitfalls
No ownership convention — six components raise with generic 'running' reason.
Test and env both raise for configure — double ownership confusion.
Background sequence without pre_body raise — sim ends when test drops.
Registry not updated on error path — false leak report at end.