Part 7 · Environment & Tests · Intermediate
Raise/Drop Policy: Deterministic Objection Ownership
Define objection ownership so tests neither finish early nor hang forever: top-level raise/drop contract, helper-task safety wrappers, and ownership anti-patterns.
Why ownership policy is mandatory
A run can end too early when nothing raises objections, or never end when something raises and never drops. The fix is a strict ownership contract : the test owns top-level lifetime; helper components may use bounded local objections only when justified.
[UVM][ENV] ownership model
recommended:
test.run_phase:
raise once before scenario
drop once after scenario + local completion checks
optional:
component-local transient objections for bounded activity
forbidden:
background thread raises without guaranteed drop path[TEST] policy principle
if a component can raise:
it must prove every control path drops
including timeout/error/disable paths
if it cannot prove this:
it should not raiseOne owner model is easier to audit than distributed objection ownership.
If distributed objections are required, enforce wrappers and metrics.
Always log raise/drop reasons to support post-failure triage.
Reference implementation with safety wrappers
class base_test extends uvm_test;
`uvm_component_utils(base_test)
my_env env;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task automatic with_objection(uvm_phase phase, string reason, task body_t);
phase.raise_objection(this, reason);
body_t();
phase.drop_objection(this, {reason, " done"});
endtask
task run_main();
main_vseq vseq = main_vseq::type_id::create("vseq");
vseq.start(env.v_sqr);
endtask
task run_phase(uvm_phase phase);
phase.raise_objection(this, "main scenario");
begin
run_main();
end
phase.drop_objection(this, "main scenario done");
endtask
endclasstask run_phase(uvm_phase phase);
phase.raise_objection(this, "error-injection campaign");
fork
begin
run_injector_sequence();
end
begin
watchdog_seq w = watchdog_seq::type_id::create("w");
w.start(env.v_sqr);
end
join
phase.drop_objection(this, "error-injection campaign done");
endtask[UVM][ENV] ownership instrumentation
log at UVM_LOW:
test objection raised reason + timestamp
test objection dropped reason + timestamp
phase.is_ended check in report hooks
this makes objection leaks visible quicklyKeep the number of top-level objection scopes small and intentional.
Use wrapper tasks to standardize raise/drop and reduce missed drops.
Never hide objection control inside deeply nested utility methods.
Anti-patterns and governance checks
Common anti-patterns
Raise in one thread, drop in another with weak synchronization.
Drop only on success path, forgetting timeout/error/disable branches.
Allowing monitors or scoreboards to own long-lived objections by default.
Using objections to mask missing readiness checks or checker deadlocks.
[TEST] code review gates
Gate 1: exactly where is objection raised?
Gate 2: prove drop executes in all exits
Gate 3: is ownership documented in class header?
Gate 4: does +UVM_OBJECTION_TRACE produce expected timeline?simv +UVM_TESTNAME=smoke_test +UVM_OBJECTION_TRACE +UVM_VERBOSITY=UVM_LOWKey takeaways
Objection ownership is a first-class API contract of each test class.
Centralized top-level ownership prevents the majority of end-of-test leaks.
Wrapper patterns and trace logs make ownership auditable.
Review gates catch objection leaks before long regressions.
Common pitfalls
Copy-paste tests with inconsistent objection style across suite.
Allowing utility threads to raise objections without lifecycle ownership.
Assuming timeout will clean up objection leaks without root-causing them.
Not enabling objection trace when triaging hangs.