Part 4 · Assertions (SVA) · Intermediate
Assertion Control & System Functions
$assertoff/$asserton/$assertkill scoping, reset methodology, $assertcontrol, assertion coverage extraction, and global clocking.
Why you need to switch assertions off
During reset, signals are X, FSMs are mid-initialization, and protocols are deliberately violated. disable iff handles this per-property, but it must be written into every property and it cannot help with phases the property author never anticipated — error-injection tests, retention power-down, post-silicon pattern replay. The assertion control system tasks act from the outside: testbench code switches whole subtrees of assertions off and on at runtime, without touching the properties themselves.
// The three classic controls — all take (levels, list_of_scopes)
$assertoff (0, top.dut); // stop NEW attempts in dut and below;
// attempts already running continue
$assertkill(0, top.dut); // kill running attempts too — instant silence
$asserton (0, top.dut); // re-enable from here on
// levels argument:
// 0 = the named scope and EVERYTHING below it
// 1 = the named scope only
// n = n levels down from the named scope
// scope list can name modules, instances, or individual assertions:
$assertoff (0, top.dut.u_fifo.ap_no_overflow); // one assertionThe reset/init methodology pattern
initial begin : assertion_reset_ctrl
// Silence everything before reset is applied
$assertkill(0, tb_top.dut);
wait (tb_top.rst_n === 1'b1);
repeat (2) @(posedge tb_top.clk); // settle margin after deassertion
$asserton(0, tb_top.dut);
$display("[%0t] assertions enabled", $time);
end
// Error-injection test: silence ONLY the protocol checker being violated
task automatic inject_bad_parity();
$assertoff(0, tb_top.dut.u_link.par_chk);
drive_corrupt_frame();
@(posedge clk);
$asserton (0, tb_top.dut.u_link.par_chk);
endtaskASSERTION CONTROL THROUGH A TEST — timeline
time ──────────────────────────────────────────────────────────►
│ reset (X soup) │ settle │ main test │ inject │ rest │
rst_n ____________________┌──────────────────────────────────────
ctrl $assertkill $asserton $assertoff $asserton
(whole DUT) (whole DUT) (par_chk) (par_chk)
attempts:
off ──── none started, running ones killed
on ████████████████████████████ (every edge, all props)
selective off par_chk only: ──────
all others: ██████████████████████
Why kill (not off) at time 0: attempts that started before reset
could still be running and would fire on X garbage; kill removes them.The difference between $assertoff and $assertkill matters exactly here: $assertoff stops new attempts but lets in-flight attempts finish — an attempt spawned one cycle before the off call can still fail three cycles later. $assertkill also terminates the in-flight threads. At time zero and around reset, kill is what you want.
$assertcontrol — the general mechanism
SystemVerilog 2012 generalized the three tasks into $assertcontrol, which adds control over pass/fail action blocks and over which directive types are affected. The classic tasks are now shorthands for specific control values.
// $assertcontrol(control_type [, assertion_types
// [, directive_types [, levels [, scopes...]]]]);
$assertcontrol(4); // Off — same effect as $assertoff(0)
$assertcontrol(3); // On — same as $asserton(0)
$assertcontrol(5); // Kill — same as $assertkill(0)
$assertcontrol(8); // PassOff: suppress PASS action blocks
$assertcontrol(10); // FailOff: suppress FAIL action blocks
// (attempts still counted!)
// Common use: keep checking but silence pass-action printing noise
initial $assertcontrol(8); // PassOff globallyControl types you should recognize: 3=On, 4=Off, 5=Kill, 8=PassOn-off pair (8 PassOff/7 PassOn), 10 FailOff/9 FailOn — exact numbers are LRM table material; know On/Off/Kill and that action-block printing is separately controllable.
assertion_types argument selects concurrent vs simple/deferred immediate assertions — you can silence immediate assertions in imported VIP while keeping your concurrent ones live.
FailOff suppresses the action block, not the failure itself — tools still count it; useful for rate-limiting a known noisy failure during triage.
Vendor flags (e.g. +assert disable) do similar things per-tool; $assertcontrol is the portable, runtime, scope-aware way.
Assertion coverage extraction
Tools record, for every assertion: attempts started, real passes, vacuous passes , and failures — and for every cover property, hit counts. This is assertion-based coverage: it answers "did my checks actually exercise?" independently of functional covergroups. The LRM provides a conceptual API ($coverage_control, $coverage_get, $coverage_save) for querying and saving this data from testbench code, though in practice most teams use tool commands and merged coverage databases rather than the system tasks directly.
// Conceptual runtime query (tool support varies; know the idea):
initial begin
// ... at end of test ...
void'($coverage_save(`SV_COV_ASSERTION, "assert_cov", "run1.db"));
end
// What the report tells you per assertion:
// ap_req_ack: attempts 100000 passes 142 vacuous 99858 fails 0
// ^^^^^^^^^^^^^
// antecedent almost never true → add cover property:
cp_req_seen: cover property (@(posedge clk) $rose(req));Global clocking and $global_clock
A global clocking @(posedge clk); endclocking declaration nominates one clock as the design's formal reference clock ; properties can then write @($global_clock) instead of naming a clock. This matters mainly in formal flows and for the LRM's *_gclk sampled-value functions ($rose_gclk, $past_gclk and friends). For simulation-focused work you only need to recognize the syntax and say what it is for: a single agreed time base that formal tools and clock-abstracted properties reference.
Interview angle
The high-yield question is the off/kill distinction: "You called $assertoff at reset but still got failures — why?" Answer: attempts already in flight keep running under $assertoff; $assertkill terminates them. Second probe: "disable iff vs $assertoff — when each?" — disable iff is per-property, declarative, reacts to a signal automatically, and is part of the spec; $assertoff is testbench policy, scope-wide, and handles cases the property author never knew about (error injection, power phases). Senior loop bonus: mention that FailOff suppresses action blocks while still counting failures — useful for noise triage without losing data.
Key takeaways
$assertoff stops new attempts; $assertkill also terminates in-flight ones — use kill around reset.
Levels argument: 0 = scope and all below; scopes can be modules, instances, or single assertions.
disable iff is per-property spec; $assertoff/$asserton is runtime testbench policy — both have a place.
$assertcontrol generalizes on/off/kill and separately controls pass/fail action-block execution.
Assertion coverage (attempts/passes/vacuous/fails per assertion) is the audit trail proving your checks ran.
Common pitfalls
Using $assertoff at time 0 and wondering why in-flight attempts still fail — that is what $assertkill is for.
Forgetting to call $asserton after reset — entire regressions pass with every assertion disabled.
Scoping $assertoff(0, top) for an error-injection test — silences the whole DUT instead of the one checker under test.
Treating FailOff as 'failures gone' — the action block is suppressed but failures are still counted in the database.
Confusing global clocking with default clocking — default is a per-scope convenience; global nominates the formal reference clock.