Part 10 · Advanced Topics · Intermediate
Raise/Drop Discipline
Matched objection patterns for tests and sequences, safe handling of asynchronous branches, and anti-patterns that create hangs.
The invariant: every raise must have one drop
Objection correctness is fundamentally an accounting problem. The invariant is strict: each raise contributes a unit of outstanding work and must be balanced by exactly one drop from the same ownership path.
Most hangs are not simulator issues. They are plain accounting leaks: an early return path skipped the drop, a forked branch never completed, or control flow threw an error between raise and drop.
[CHECK] matching discipline
GOOD:
raise -> do work -> drop
raise -> fork ... join -> drop
BAD:
raise -> return (no drop)
raise in parent, drop in child with different source ownership
drop called twice for one raise (underflow / warnings / undefined intent)Canonical test-level pattern
At test level, objection ownership is usually centralized: run-phase test code raises once, starts one or more top sequences, waits for completion, then drops.
task run_phase(uvm_phase phase);
main_vseq vseq;
phase.raise_objection(this, "main scenario start");
vseq = main_vseq::type_id::create("vseq");
vseq.start(env.v_sqr);
phase.drop_objection(this, "main scenario done");
endtaskWhen to prefer test-level ownership
Top-level scenario orchestration with one entry/exit point.
Predictable runtime envelope in CI where test controls all sequence launches.
Teams with strict convention to avoid mixed ownership across env layers.
When to avoid test-level-only ownership
Deep asynchronous workers may continue after main sequence returns.
Passive scoreboards need conditional hold near phase end.
Per-agent independent background traffic requires local ownership.
Sequence-level objections and async hazards
Sequence-level objections can be valid, but only when ownership is deliberate. The biggest hazard is spawning async work inside sequence body and returning before that work drains.
class traffic_seq extends uvm_sequence #(bus_txn);
`uvm_object_utils(traffic_seq)
task body();
if (starting_phase != null)
starting_phase.raise_objection(this, "traffic_seq running");
repeat (50) begin
req = bus_txn::type_id::create("req");
start_item(req);
assert(req.randomize());
finish_item(req);
end
if (starting_phase != null)
starting_phase.drop_objection(this, "traffic_seq done");
endtask
endclass[UVM] async leak pattern
body():
raise
fork
send_stream_A();
send_stream_B(); // may block on handshake
join_none
drop <-- too early, background still running
Correct:
either join all branches before drop,
or move ownership to manager component that tracks branch completion.If you use join_none, you need explicit completion tracking before dropping.
Do not rely on simulation end timeout to hide missing drops.
Sequence ownership should be documented because it is easy to mix with test ownership accidentally.
Defensive templates for robust accounting
SystemVerilog lacks exception-safe finally semantics, so defensive coding means structuring control flow so drop is on every path.
task run_phase(uvm_phase phase);
bit raised = 0;
phase.raise_objection(this, "defensive pattern");
raised = 1;
// Use guarded blocks; keep returns centralized.
begin
if (!cfg.enable_feature) begin
`uvm_warning("CFG", "feature disabled")
end else begin
run_feature_flow();
end
end
if (raised)
phase.drop_objection(this, "defensive pattern complete");
endtask[CHECK] review checklist before commit
1. Is every raise paired with one drop?
2. Can any return/disable/fork path bypass drop?
3. Are source handles consistent?
4. Is ownership location (test vs sequence vs component) documented?
5. Do logs include reason strings for trace readability?Key takeaways
Objection bugs are accounting bugs; design with explicit ownership.
Choose one ownership style per flow: test-level, sequence-level, or component-level.
Asynchronous forks require explicit completion tracking before drop.
Defensive run_phase templates prevent silent control-flow leaks.
Common pitfalls
join_none followed by immediate drop while work is still live.
Mixed ownership where test raises and nested sequence drops unpredictably.
Early returns and error branches that bypass drop logic.