Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
    • SVA Fundamentals
      • Immediate vs Concurrent Assertions
      • The Four Layers of SVA
      • Clocks & the Sampling Model
      • Evaluation Attempts & Threads
      • assert, assume, cover, restrict & Action Blocks
    • Sequences: Temporal Building Blocks
    • Implication and Property Operators
    • Sampled Value Functions
    • Protocol Assertion Cookbook
    • Advanced SVA: Local Variables, Multiclock & More
    • Assertion Debug & Methodology
    • SVA Interview Drills
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹SVA Fundamentals
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Assertions — Interview-Critical Deep Dive
  4. SVA Fundamentals
  5. assert, assume, cover, restrict & Action Blocks

Part 4 · Assertions (SVA) · Intermediate

assert, assume, cover, restrict & Action Blocks

What assert, assume, cover and restrict instruct in simulation and formal, plus actions and severity.

On this page

    Why this matters

    A formal proof passed and the same RTL failed in simulation the next day. An assume written for convenience had excluded the very trace simulation produced.

    After this lesson you can: say what assert, assume, cover and restrict do in simulation and in formal, and write action blocks that make failures useful.

    Four verbs for one property

    The directive is the top layer: it takes a property and tells the tool what to do with it. The same text can be checked, used as a constraint, counted as a scenario, or used to prune the formal state space.

    The four verbs mean different things to a simulator and a formal tool. Knowing both columns is what the interview question asks for.

    Directive

    Simulation

    Formal

    assert

    Checks, reports violations

    Proves the property for all legal traces

    assume

    Checked exactly like assert

    Constrains inputs to legal traces

    cover

    Counts matches, never reports

    Searches for a witness trace

    restrict

    Ignored entirely, no cost

    Constrains like assume

    The listing below runs. Two changes from the textbook form: the cover writes full ##1 ~full, because Verilator 5.048 rejects ! after ##1, and every directive counts in its action block so a bench can plant bugs and keep going.

    fifo_verbs.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    module fifo_verbs (
      input  logic clk,
      input  logic rst_n,
      input  logic push, full, pop, empty,
      input  logic [1:0] mode,
      output int   overflows, bad_pops, full_drains
    );
      // 1. assert: the DUT obligation
      ap_no_overflow: assert property (@(posedge clk) disable iff (!rst_n)
        !(push && full)) else overflows++;
      // 2. assume: an environment contract; simulation checks it like an assert
      am_pop_legal: assume property (@(posedge clk) disable iff (!rst_n)
        !(pop && empty)) else bad_pops++;
      // 3. cover: the interesting scenario happened ("~full": Verilator rejects "!" after ##1)
      cp_full_drain: cover property (@(posedge clk) disable iff (!rst_n)
        full ##1 ~full) full_drains++;
      // 4. restrict: formal case split; simulation ignores it
      rs_mode: restrict property (@(posedge clk) mode == 2'b01);
      initial begin overflows = 0; bad_pops = 0; full_drains = 0; end
    endmodule
    
    module tb_verbs;
      logic clk = 0;
      always #5 clk = ~clk;
      logic rst_n = 0, push = 0, full = 0, pop = 0, empty = 1;
      logic [1:0] mode = 2'b11;                     // breaks the restrict on purpose
      int overflows, bad_pops, full_drains;
      fifo_verbs chk (.*);
      initial begin
        repeat (2) @(negedge clk);
        rst_n = 1;
        @(negedge clk); empty = 0; full = 1;        // full, nothing pushed
        @(negedge clk); push = 1;                   // planted: push while full
        @(negedge clk); push = 0; full = 0;         // full drains
        @(negedge clk); empty = 1; pop = 1;         // planted: pop while empty
        @(negedge clk); pop = 0;
        repeat (2) @(negedge clk);
        $display("assert: %0d overflow; assume: %0d illegal pop; cover: %0d full-then-drain; restrict: ignored (mode 3 all along)",
                 overflows, bad_pops, full_drains);
        if (overflows != 1 || bad_pops != 1 || full_drains != 1) $fatal(1, "FAIL: directive counts");
        $display("PASS: assert and assume each caught their planted bug, the cover counted once, and restrict stayed silent");
        $finish;
      end
    endmodule
    Terminal
    assert: 1 overflow; assume: 1 illegal pop; cover: 1 full-then-drain; restrict: ignored (mode 3 all along)
    PASS: assert and assume each caught their planted bug, the cover counted once, and restrict stayed silent

    Assume versus restrict in one line: both constrain formal, while simulation checks assume and ignores restrict. Use restrict for case splits that simulation should not be forced to obey.

    Action blocks and severity

    An assert or assume takes an optional action block: a pass statement, an else fail statement, or both. Action blocks are procedural code in the Reactive region, so they can call tasks, count, or dump state.

    Actions run with current values, while the attempt was decided on sampled values. Signals may have moved on, so print sampled values with $sampled when exactness matters. Fail actions of windowed properties run at the window end, so include the attempt start time in the message.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // snippet
    // fail actions carry the triage payload: count, timestamp, and cause
    // ap_req_ack: assert property (@(posedge clk) disable iff (!rst_n)
    //   req |-> ##[1:3] ack)
    //   else begin
    //     err_count++;
    //     $error("req with no ack in 3 cycles (count %0d)", err_count);
    //   end

    The bench above already uses counting actions. Restrict needs no planted bug: mode sits at 3 for the whole run, and simulation never looks at it.

    Severity answers what a nightly regression should do. Error is the default severity of a failing assertion with no action block, and fatal stops a simulation whose later output would be noise.

    Task

    Simulation

    Regression meaning

    $info

    Continues

    Never fails a test

    $warning

    Continues

    Counted, usually non-fatal

    $error

    Continues on most simulators; Verilator 5.048 stops at the first one unless run with +verilator+error+limit+N

    Fails the test

    $fatal(1, msg)

    Stops immediately

    Aborts a meaningless run; the first argument is the $finish diagnostic level (0, 1 or 2), not the exit code. Tools typically exit non-zero on $fatal

    Common mistakes

    The identifier before the colon is the label. It appears in failure messages, coverage reports, and tool on-off controls, so treat labels as interface: stable, greppable, and prefixed by directive type.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // snippet
    // prefix encodes the directive: ap_ assert, am_ assume, cp_ cover, rs_ restrict
    // ap_fifo_no_overflow: assert property (p_no_overflow);
    // cp_fifo_wrap: cover property (p_wrap_around);
    // labels enable surgical simulation control around error injection
    // initial begin
    //   $assertoff(0, top.dut_wrap.ap_fifo_no_overflow);
    //   // ... injection scenario ...
    //   $asserton(0, top.dut_wrap.ap_fifo_no_overflow);
    // end

    Common pitfall

    Assume is checked in simulation, not ignored. A violated assumption fails the simulation run, and a convenience assumption that is too tight makes a formal proof hold only for inputs that never happen.

    Interview

    The high-yield question is the assume, assert and restrict triangle. In simulation assume is checked exactly like assert, so it verifies the stimulus honors the environment contract. In formal it constrains the input space the prover explores, while restrict constrains formal the same way but is ignored in simulation.

    • Trap: a formal proof passed but the same RTL fails in simulation. An over-constraining assumption excluded the trace simulation generated.

    • Trap: a failing cover is a contradiction in terms. Covers never fail; a never-hit cover means the scenario was never exercised.

    • Follow-up: turn assertions off during error injection with $assertoff on labeled paths, or a testbench-controlled disable iff term.

    Check yourself What does assume do in simulation?

    Exactly what assert does: it checks that the stimulus honors the environment contract. Only in formal does it switch roles to a constraint on the explored traces.

    Check yourself A formal proof passed but simulation fails on the same RTL. What happened?

    Over-constraining. An assume or restrict excluded the trace simulation generated, so the proof holds for a smaller input space than the testbench uses.

    Remember

    • assume is checked in simulation, exactly like assert.

    • restrict is ignored in simulation; cover counts and never fails.

    • Labels are interface: stable, greppable, prefixed by directive.

    Rule of thumb: every assume is a claim about the environment that simulation must confirm.

    How this was checked

    Run with Verilator 5.048 (--binary --timing --assert, plus --coverage when a listing has a cover property) on 2026-10-18; every bench printed the PASS line shown. Verilator stops at the first $error or failing assertion without an action block, so the benches count failures in the action block. Icarus 13 has no concurrent assertions.

    Verilator 5.048 rejected full ##1 !full; the bench writes full ##1 ~full.

    Related topics

    • Assertion debug

    • Advanced SVA

    Previous

    105 / 259

    Next: Sequences: Temporal Building Blocks

    Up next

    Up nextSequences: Temporal Building Blocks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Immediate vs Concurrent Assertions
    • The Four Layers of SVA
    • Clocks & the Sampling Model