Skip to content

SystemVerilog · Module 12

Assertion Control System Tasks

The three runtime assertion-control tasks — $assertoff, $asserton, $assertkill — that enable selective disabling and re-enabling of assertions during reset, scan, low-power, and debug phases. Hierarchical scoping rules, the difference between off and kill, and the production patterns that let one assertion file serve multiple verification modes without code duplication.

Module 12 · Page 12.12

Why Runtime Assertion Control Exists

Modules 12.4–12.7 established the disable iff clause as the canonical mechanism for skipping assertion evaluation during reset and other don't-care windows. disable iff is a compile-time directive — it's part of every property declaration, and the conditions under which the assertion is dormant are fixed when the property is elaborated.

But real verification environments need runtime control too. A regression run that targets normal-operation coverage shouldn't pay the simulation cost of evaluating scan-chain-related assertions; an interactive debug session might want to disable noisy assertions temporarily; a multi-mode SoC verification environment might need different assertion sets active in different test phases. The three assertion-control system tasks$assertoff, $asserton, $assertkill — provide this runtime control.

The three tasks are SystemVerilog system functions that take a scope argument and a "what to control" argument. Called from procedural code (typically inside initial blocks or test sequences), they affect the runtime state of assertions matched by the arguments. The control is orthogonal to disable iff — runtime control and compile-time disable both contribute to whether an assertion evaluates.

The Three Tasks — Behaviour and Semantics

$assertoff(level, scope) — Pause

SystemVerilog — $assertoff usage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    @(posedge clk);
    // ── Disable all assertions in the design hierarchy ───────────
    $assertoff(0);          // 0 means current scope and all below
end
 
// ── Selective disable: just the apb_slave instance's assertions ──
initial begin
    @(posedge clk);
    $assertoff(0, dut.apb_slave);
    // assertions in dut.apb_slave (and its sub-hierarchy) are disabled
end

The semantic. When $assertoff runs, matched assertions stop evaluating new properties — no new antecedent matches start a new evaluation. Existing in-flight evaluations are paused — they retain their state and will resume when $asserton runs.

The pause-vs-kill distinction matters for multi-cycle properties. An assertion like assert property (req |=> ack) that has matched its antecedent (req) but hasn't yet checked its consequent (ack next cycle) is "in flight." $assertoff pauses that evaluation; when $asserton restores it, the pause window is treated as if no time had elapsed — the assertion will still expect ack to be high on the cycle after req rose.

$assertkill(level, scope) — Disable and Terminate

SystemVerilog — $assertkill usage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    @(posedge clk);
    // ── Disable assertions AND terminate any in-flight evaluations ──
    $assertkill(0, dut.apb_slave);
 
    // After this, any sequence-based or implication-based assertion
    // in apb_slave that was waiting for its consequent is terminated.
    // No fail action runs; no pass action runs; the evaluation simply
    // ceases to exist.
end

The semantic. Like $assertoff, no new evaluations start. Unlike $assertoff, in-flight evaluations are terminated — they cease to exist with no pass or fail action. This is the right tool when the test is entering a phase where in-flight evaluations would produce false failures (e.g., transitioning to scan mode where the design's protocol-compliance assertions can't be expected to hold their state).

When to choose $assertkill over $assertoff. Use $assertkill when:

  • The test is entering a phase where the design's state will be reset or randomised
  • In-flight evaluations would produce false failures if resumed
  • The test architecture requires clean termination, not pause-and-resume

$asserton(level, scope) — Resume

SystemVerilog — $asserton usage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    $assertoff(0, dut.scan_chain);     // disable scan assertions for normal ops
 
    @(posedge enter_scan_mode);
    $asserton(0, dut.scan_chain);      // re-enable when scan testing begins
    $assertoff(0, dut.normal_ops);     // suppress normal-ops assertions during scan
 
    @(posedge exit_scan_mode);
    $asserton(0, dut.normal_ops);      // restore normal-ops assertions
    $assertoff(0, dut.scan_chain);     // suppress scan assertions again
end

The semantic. Matched assertions resume normal evaluation. Properties that were paused (by a prior $assertoff) restart from where they paused; properties that were killed (by a prior $assertkill) start fresh on the next antecedent match.

The level Argument — Hierarchical Scope

The first argument to all three tasks is the level — controlling how far into the hierarchy the task reaches.

level valueScope
0The named scope and all sub-hierarchy (default for the second-argument case)
1Only the immediate scope (no descent into sub-hierarchy)
n (positive integer)The named scope and n levels below it
SystemVerilog — level argument controls hierarchy descent
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Disable assertions in the entire DUT hierarchy ──────────────
$assertoff(0, dut);          // dut and everything below
 
// ── Disable assertions only in dut itself, not sub-modules ──────
$assertoff(1, dut);          // just dut's own assertions
 
// ── Disable assertions in dut and the immediate child modules ───
$assertoff(2, dut);          // dut + one level of sub-modules

The level argument is what makes the assertion-control tasks compositional — production environments use level=0 for broad enable/disable phases and level=1 for surgical control over specific module instances.

A Complete Working Example — Multi-Phase Test Control

The pattern below shows production discipline: a test that uses $assertoff/$asserton to enable different assertion sets in different phases of the simulation. Each phase has its own active assertion set; the test architecture is reusable across regressions.

SystemVerilog — multi-phase assertion control
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module testbench;
    bit clk, rst, scan_enable, low_power;
 
    // DUT and assertions instantiated here
    dut_with_assertions dut (.*);
 
    // ── Test orchestrator — controls assertion state per phase ──
    initial begin
        // ── Phase 1: System Reset ────────────────────────────────
        // Disable ALL assertions during reset (signals are X)
        rst = 1;
        $assertoff(0);                  // disable everything
 
        @(posedge clk); @(posedge clk);
        rst = 0;
        $asserton(0);                   // re-enable everything
 
        // ── Phase 2: Normal Operation ────────────────────────────
        // All assertions active by default; just disable scan-specific
        $assertoff(0, dut.scan_chain_assertions);
 
        run_normal_traffic();
 
        // ── Phase 3: Scan Mode Entry ────────────────────────────
        // Disable functional assertions; enable scan-chain assertions
        $assertkill(0, dut.protocol_assertions);    // kill in-flight protocol checks
        $asserton(0, dut.scan_chain_assertions);     // enable scan checks
        scan_enable = 1;
 
        run_scan_test();
 
        // ── Phase 4: Scan Mode Exit ─────────────────────────────
        scan_enable = 0;
        $assertoff(0, dut.scan_chain_assertions);    // disable scan
        $asserton(0, dut.protocol_assertions);       // restore protocol
 
        run_normal_traffic();
 
        // ── Phase 5: Low Power ──────────────────────────────────
        // Disable assertions that wouldn't hold in low-power state
        $assertkill(0, dut.bus_protocol_assertions);
        low_power = 1;
 
        run_low_power_sequence();
 
        // ── Phase 6: Wake Up ────────────────────────────────────
        low_power = 0;
        $asserton(0, dut.bus_protocol_assertions);
 
        run_normal_traffic();
 
        $finish;
    end
 
    // ── Task definitions (stimulus) ─────────────────────────────
    task run_normal_traffic();
        $display("[%0t] === Normal traffic phase ===", $time);
        // ... drive normal stimulus ...
    endtask
 
    task run_scan_test();
        $display("[%0t] === Scan-chain test phase ===", $time);
        // ... drive scan-chain stimulus ...
    endtask
 
    task run_low_power_sequence();
        $display("[%0t] === Low-power sequence phase ===", $time);
        // ... drive low-power stimulus ...
    endtask
endmodule

What this code does. A six-phase test orchestrator. Each phase has a distinct active assertion set, controlled at runtime via the three system tasks. Reset disables everything (signals are X); normal operation disables only scan-specific assertions; scan-mode entry kills in-flight protocol checks (cleanly terminated, not paused, because scan-mode state won't preserve them); low-power entry kills bus-protocol checks; wakeup restores them. Each phase's stimulus runs against the appropriate assertion set; no false failures from assertions firing during phases where their contracts don't apply.

Expected output (fragment).

Simulation log fragment
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[0] === Normal traffic phase ===
*E,ASRTST: [142] a_pready_within_16: PREADY > 16 cycles after ACCESS
                   (real bug found in normal traffic phase)
 
[2500] === Scan-chain test phase ===
                   (no protocol-violation assertions fire — they're killed)
                   (scan-chain assertions active and reporting any scan violations)
 
[5000] === Normal traffic phase ===
*E,ASRTST: [5142] a_psel_before_penable: PENABLE rose without PSEL
                   (real bug found in second normal traffic phase)
 
# Total assertion failures: 2

The phase-aware assertion control ensures each phase reports only the violations relevant to that phase. Without $assertkill/$asserton, the scan-mode phase would flood the log with protocol-violation false failures, masking the real scan-chain bugs.

$assertvacuousoff and $assertvacuouson — Vacuous-Pass Control

Two additional control tasks affect how the simulator handles vacuous passes — properties whose antecedent never fires.

SystemVerilog — vacuous-pass control
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    // ── Suppress vacuous-pass reports for a specific scope ──────
    $assertvacuousoff(0, dut);
    // Vacuously-true property evaluations in dut don't count as "pass"
    // — they're suppressed from the coverage and pass-count reports.
 
    @(posedge significant_event);
    $assertvacuouson(0, dut);
    // Vacuous passes are reported normally again
end

When to use vacuous-pass control. When a property's antecedent might fire many times during reset or initialization but the post-antecedent checks aren't valid yet. Suppressing vacuous-pass reports during those phases prevents the coverage/pass numbers from being inflated by meaningless evaluations.

These tasks are less commonly used than the main three ($assertoff/$asserton/$assertkill); they're worth knowing for the rare cases where vacuous-pass counts need explicit management.

Common Pitfalls

Pitfall 1 — Confusing $assertoff with $assertkill

SystemVerilog — wrong choice between off and kill
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Scenario: transitioning into scan mode
// Intent: stop functional assertions cleanly
 
// Wrong — pauses; when later resumed, in-flight properties try to continue
$assertoff(0, dut.functional);
// (transition to scan mode — design state changes)
$asserton(0, dut.functional);
// Paused properties resume from their paused state, but the design's state
// has changed; the resumed evaluations produce false failures.
 
// Right — kill; in-flight properties are terminated cleanly
$assertkill(0, dut.functional);
// (transition — design state can change freely)
$asserton(0, dut.functional);
// Properties start fresh on the next antecedent match.

Fix: use $assertkill when the design's state will change during the disable window. Use $assertoff when the design's state will remain consistent through the disable window.

Pitfall 2 — Disabling assertions too broadly

SystemVerilog — over-broad scope
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Wrong — disables ALL assertions in the design, including those that should still fire
$assertoff(0);
 
// Right — disable only what should be disabled
$assertoff(0, dut.scan_chain);     // just scan-chain assertions

Fix: the second argument (scope) is what makes the control precise. Always provide a scope argument unless the intent is genuinely "all assertions in the design."

Pitfall 3 — Forgetting to re-enable

SystemVerilog — assertions disabled but never re-enabled
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    $assertoff(0, dut.protocol);
    run_some_phase();
    // (forgot to re-enable)
    run_another_phase();   // protocol assertions still disabled — silent gap!
end

Fix: every $assertoff/$assertkill is paired with a matching $asserton at the appropriate phase transition. Production discipline uses a try-finally or phase-scoped pattern so re-enable can't be forgotten.

Pitfall 4 — Using runtime control as a substitute for disable iff

SystemVerilog — wrong tool for reset disable
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Wrong — using runtime control for reset window
initial begin
    rst = 1;
    $assertoff(0);
    @(posedge clk); @(posedge clk);
    rst = 0;
    $asserton(0);
end
 
// Right — use disable iff in the property declarations
property p_protocol;
    @(posedge clk) disable iff (rst)
        req |=> ack;
endproperty

Fix: disable iff is the compile-time mechanism for reset-window disable; it's part of every property declaration. Runtime control is for phase-level enable/disable that varies test-to-test. Use the right tool for the right scope: compile-time disable iff for per-property reset; runtime $assertoff/$asserton for phase-level orchestration.

Debug Lab — "Assertions Stopped Firing After Phase 3"

A subtle failure mode that surfaces when runtime control's state-tracking is mismanaged across complex test orchestrators.

Buggy code
SystemVerilog — asymmetric off/on usage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    run_phase_1();              // normal traffic, all assertions active
 
    $assertoff(0, dut.protocol);
    run_phase_2();              // scan mode — protocol assertions disabled
    $asserton(0, dut.protocol);
 
    $assertoff(0, dut.protocol);
    run_phase_3();              // low-power — protocol assertions disabled
    // BUG: missing $asserton(0, dut.protocol) before phase 4
 
    run_phase_4();              // normal traffic returns
    run_phase_5();              // normal traffic continues
end
Symptom

The regression runs all five phases. The dashboard reports 2 errors total — one each from phase 1 and phase 2. The verification engineer sees a clean result for phases 3, 4, and 5 and concludes the design is working in those phases. Integration testing later surfaces three real bugs that the protocol assertions in phases 4 and 5 should have caught.

Root cause

Phase 3 disabled protocol assertions for low-power testing. The corresponding $asserton at the end of phase 3 was missing — the engineer added the disable but forgot the re-enable. Phases 4 and 5 ran with protocol assertions silently disabled; any bugs in those phases were invisible to the regression.

This is a class of bug that the simulator cannot detect. The runtime-control tasks don't track whether their disable/enable counts are balanced — every $assertoff is independent. The verification engineer's discipline of pairing them is the only defense.

Fix

Restore the missing $asserton and adopt a pairing convention that surfaces asymmetries at code review.

SystemVerilog — paired off/on with comment-block convention
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    run_phase_1();
 
    // === Phase 2: scan mode (protocol assertions disabled) ===
    $assertoff(0, dut.protocol);
    run_phase_2();
    $asserton(0, dut.protocol);   // re-enable after phase 2
 
    // === Phase 3: low-power (protocol assertions disabled) ===
    $assertoff(0, dut.protocol);
    run_phase_3();
    $asserton(0, dut.protocol);   // re-enable after phase 3 — FIX
 
    run_phase_4();
    run_phase_5();
end

The discipline rule that prevents recurrence: every $assertoff is paired with a $asserton in the same code block, with a phase-comment around the pair. The visual pairing makes asymmetries immediately apparent at code review. Production codebases often wrap the pair in a helper task that takes a phase name and a body, so the pairing is enforced structurally.

The complementary discipline: pair every spec-critical assertion with a cover property of its antecedent. The cover's hit count confirms the assertion ran in the phases where it should have. If the cover reports zero hits for a phase where the antecedent should have fired, the assertion was silently disabled — surface the gap before sign-off rather than after silicon.

Industry Context — Where Runtime Assertion Control Lives

  • Multi-mode SoC verification environments. Modern SoCs have normal / scan / low-power / debug / calibration modes. Each mode has its own valid signal states and its own assertion contracts. $assertoff/$asserton/$assertkill is the canonical mechanism for switching the active assertion set per mode without code duplication.
  • UVM-based verification. UVM's uvm_test orchestrator uses runtime assertion control to activate per-test assertion sets. The base test enables a "common" set; derived tests selectively enable additional sets for specific scenarios.
  • Scan-chain DFT verification. Scan-mode testing produces signal patterns that violate functional assertions by design. $assertkill is used at scan-mode entry to terminate all functional protocol assertions cleanly; $asserton restores them at scan-mode exit.
  • Low-power verification (UPF/CPF flows). Power-down sequences violate bus-protocol assertions (bus is electrically dead, not honoring protocols). Runtime control is the canonical mechanism for disabling those assertions during power-down phases.
  • Multi-protocol bus-matrix verification. When a bus matrix supports multiple protocols (AXI + APB + AHB), each protocol has its own assertion set. Tests that exercise one protocol can disable the others' assertions to keep the log focused on the relevant violations.
  • Regression-time targeted bug investigation. During interactive debug of a specific failure, runtime control lets the engineer disable noisy assertions temporarily to focus on the assertion under investigation. The discipline is to re-enable everything before reporting "investigation complete."

The pattern across these uses: runtime assertion control is the test-orchestrator layer of the verification architecture. The assertion definitions themselves (the properties and constructs) live in modules and packages; the runtime control logic lives in the test orchestrator that knows which assertions apply in which phase.

Interview Q&A — Ten Questions You Will Be Asked

$assertoff(level, scope) disables matched assertions and pauses their in-flight evaluations. $asserton(level, scope) re-enables matched assertions and resumes paused evaluations. $assertkill(level, scope) disables matched assertions and terminates in-flight evaluations cleanly (no pass or fail action runs). Plus the less-common $assertvacuousoff/$assertvacuouson for vacuous-pass control. The three primary tasks provide runtime enable/disable; disable iff provides compile-time disable.

Coding Guidelines — Seven Rules for Assertion-Control Discipline

  1. Pair every $assertoff/$assertkill with a matching $asserton in the same code block. Comment the pair with the phase context. Code review verifies asymmetries.
  2. Use $assertkill when the design's state will change during the disable window. Scan-mode transitions, low-power entries, configuration changes — the design state isn't preserved, so in-flight assertions can't safely resume.
  3. Use $assertoff when the design's state remains consistent. Brief interactive-debug disables, temporary suppression for log focus, phase-internal pauses where the design continues operating normally.
  4. Always provide a scope argument unless the intent is genuinely "all assertions." The scope is what makes the control precise; broad-scope calls are the most common cause of accidentally disabling assertions that should fire.
  5. Use disable iff for compile-time, per-property reset; use runtime control for test-orchestrator phase management. Don't mix the two — use the right tool for the right scope.
  6. Pair every phase with cover properties of the relevant assertions' antecedents. The covers' hit counts confirm the assertions ran in the phases where they should have, surfacing silent-disable bugs.
  7. Treat runtime control as a deliverable artifact. The test orchestrator's $assertoff/$asserton calls are reviewable code, not setup-and-forget configuration. Code review verifies the runtime-control logic matches the test's phase architecture.

Summary

The three assertion-control system tasks — $assertoff, $asserton, $assertkill — provide runtime enable/disable for SVA assertions. $assertoff pauses; $asserton resumes; $assertkill terminates in-flight evaluations cleanly. The level argument controls hierarchy descent; the scope argument controls which assertions are affected. Runtime control is orthogonal to compile-time disable iff — use disable iff for per-property reset windows and runtime control for test-orchestrator phase management. The single most common production bug is missing $asserton after a phase-level $assertoff — the disabled assertion silently doesn't fire in subsequent phases. Pair every off/kill with an on; comment phases explicitly; use cover properties to detect silent disables.

Closing — What Module 12 Built

Across 12 tutorials, Module 12 built the complete SVA discipline. The two assertion families: immediate and deferred immediate for same-instant checks; concurrent for clock-driven temporal contracts. The property and sequence vocabulary: sequences as named temporal patterns; properties as the named-contract layer; clocking and disable iff as the timing/scope clauses; implication operators and repetition operators as the contract-encoding operators. The four constructs: assert, assume, cover, restrict — the verbs that turn properties into verification evidence. The action layer: severity tasks and action blocks for the consequences of assertion firing. And the runtime layer: this page's $assertoff/$asserton/$assertkill for phase-level test orchestration.

The introduction framed SVA as the temporal-contract layer that complements Module 11's coverage — coverage measures whether scenarios were exercised, SVA enforces whether contracts held while they ran. Module 12 was the build-out of that promise: every layer of the SVA vocabulary, every discipline rule, every common bug class and its diagnostic. Together, Modules 11 and 12 are the verification-engineer's two-handed toolkit — coverage and assertions — for production protocol verification.

The next module (Module 13 — Inter-Process Communication) shifts to the testbench-architecture layer: mailboxes, semaphores, and event objects that let UVM components and threaded testbenches communicate cleanly. The shift is from "what to verify" (Modules 11–12) to "how to structure the verification environment" (Modules 13+).