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. Immediate vs Concurrent Assertions

Part 4 · Assertions (SVA) · Intermediate

Immediate vs Concurrent Assertions

Instant procedural checks versus clocked temporal properties, deferred variants and where each belongs.

On this page

    Why this matters

    A check that ack follows req within three cycles was written as an immediate assert in a task. It compiled, ran, and could never fail: an immediate assertion has no clock and sees one instant.

    After this lesson you can: choose between immediate and concurrent assertions, and know when an immediate check needs a deferred form.

    Two different machines

    An immediate assertion is a procedural statement. It runs when the surrounding code reaches it, evaluates its expression instantly with current values, and is done.

    A concurrent assertion is a clocked declaration. It samples signals on a clock edge, can span many cycles, and runs for the whole simulation. An immediate assertion cannot say ack within 3 cycles, and a concurrent assertion is overkill for a cast check.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet
    // IMMEDIATE: runs when the task executes, sees current values
    task drive_burst(input bit is_burst, input int id);
      a_cast: assert (is_burst)
        else $error("item %0d is not a burst item", id);
    endtask
    
    // IMMEDIATE: re-evaluates on every input change inside combinational code
    // a_onehot: assert ($onehot0(grant)) else $error("grant not one-hot-0");
    design.sv
    1
    2
    3
    4
    5
    6
    // snippet
    // CONCURRENT: declarative, clocked, temporal
    // "every req gets an ack within 1 to 3 cycles"
    // ap_req_ack: assert property (@(posedge clk) disable iff (!rst_n)
    //   req |-> ##[1:3] ack)
    //   else $error("ack missed the 3-cycle window");

    Module, interface or checker scope is the norm for concurrent assertions. A concurrent assertion inside a clocked procedure with an inferred clock is legal but legacy style.

    Instant checks and the glitch problem

    An immediate assertion in combinational code can fire on a delta-cycle glitch. Inputs may settle in several delta steps within one slot, and the assertion sees the transient combination that no flip-flop would capture.

    One time slot, two delta cycles.

    Deferred variants exist for exactly this. IEEE 1800-2009 added assert #0, and 1800-2012 added assert final. Both evaluate the expression when the statement runs, but defer the report: #0 matures in the Observed region, and final in the Postponed region. If the process runs again before the report matures, the pending report is flushed, so a failure cured by later deltas is never printed.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // snippet
    // plain immediate: may fire on delta-cycle glitches
    // a_glitchy: assert (!(ready && error));
    // observed deferred: report matures in the Observed region, glitch-immune
    // a_observed: assert #0 (!(ready && error))
    //   else $error("ready and error both high");
    // final deferred: report matures in the Postponed region, on settled values
    // a_final: assert final (!(ready && error));

    Common pitfall

    A plain immediate assertion inside always_comb fires on delta glitches the hardware never sees. Reach for a deferred variant, or move the check to a clocked concurrent assertion.

    Clocked checks and the ack window

    A concurrent assertion pins evaluation to clock ticks, so it can express relations across cycles. The diagram shows one request answered two cycles later, inside a 1-to-3 window.

    Watch ack against req. The attempt opened in slot 1 closes in slot 3.

    • Slot 0: req 0, ack 0. Idle, and every attempt passes vacuously.

    • Slot 1: req 1. An attempt opens; its window is slots 2 to 4.

    • Slot 2: req 0, ack 0. Cycle 1 of the window, still waiting.

    • Slot 3: ack 1. Answered in cycle 2, so the attempt passes.

    • Slot 4: req 0, ack 0. Idle, with nothing in flight.

    req_ack_props.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    module req_ack_props (
      input logic clk,
      input logic rst_n,
      input logic req,
      input logic ack
    );
      ap_req_ack: assert property (@(posedge clk) disable iff (!rst_n)
        req |-> ##[1:3] ack)
        else $error("ack missed the 3-cycle window");
    
      // Approximate: some req in the 3 cycles before ack. An exact check
      // needs an outstanding-request counter.
      ap_ack_alone: assert property (@(posedge clk) disable iff (!rst_n)
        ack |-> ($past(req, 1) || $past(req, 2) || $past(req, 3)))
        else $error("ack with no req in the previous 3 cycles");
    endmodule

    The second assertion matches the 1-to-3 window of the first. Requiring $past(req) alone would fail the waveform above, where ack comes two cycles after req.

    Decision rules

    Choose by the question, not by habit. Instant value checks belong in procedural code, and anything with time in it needs a clock.

    Immediate

    Concurrent

    Where it lives

    Procedural code

    Module, interface or checker scope

    When it evaluates

    When code reaches it

    Every clock tick

    Values used

    Current (delta-live)

    Preponed (pre-edge)

    Time span

    One instant

    Many cycles

    Typical home

    Testbench checks

    RTL, interfaces, bound checkers

    Glitch sensitivity

    Yes, use deferred forms

    No, clock-sampled

    Interview

    A complete thirty-second answer: immediate assertions are procedural and evaluate instantly with current values, while concurrent assertions are clocked, sample in the preponed region, and express multi-cycle behavior. Strong candidates add the deferred variants and the placement rule: result checks are immediate, protocol rules are concurrent.

    • Trap: an immediate assertion cannot check that ack follows req in 2 cycles. It has no clock and no temporal extent.

    • Trap: an always_comb assertion that fires while the waveform looks fine saw a delta-cycle glitch. The fix is a deferred assertion.

    Testbench

    The bench runs both kinds for real. An immediate $onehot0 check runs once for each of the 16 grant values. The two concurrent checks above watch an answered request, then a planted unanswered request and a planted ack with no request.

    Predict first How many of the 16 grant values fail $onehot0? And how many failures should each concurrent check report?

    11 of 16: only 0 and the four one-hot values pass. Each concurrent check should report exactly one failure, for its own planted bug.

    design.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 tb_imm_conc;
      logic clk = 0;
      always #5 clk = ~clk;
      logic rst_n = 0;
      logic req = 0, ack = 0;
      logic [3:0] grant;
      int imm_fails = 0, win_fails = 0, alone_fails = 0;
    
      // Concurrent: clocked, sampled before each edge, spans cycles.
      // A real bench would report with $error; Verilator stops at the first $error,
      // so these benches count failures in the action block and keep running.
      ap_req_ack: assert property (@(posedge clk) disable iff (!rst_n)
        req |-> ##[1:3] ack) else win_fails++;
      ap_ack_alone: assert property (@(posedge clk) disable iff (!rst_n)
        ack |-> ($past(req, 1) || $past(req, 2) || $past(req, 3))) else alone_fails++;
    
      // Immediate: runs when the code reaches it, with the values of that moment.
      task automatic check_grant(logic [3:0] g);
        a_onehot0: assert ($onehot0(g)) else imm_fails++;
      endtask
    
      initial begin
        for (int g = 0; g < 16; g++) check_grant(4'(g));      // 5 of 16 values are one-hot-0
        $display("immediate $onehot0 over all 16 grant values: %0d failures (11 expected)", imm_fails);
        repeat (2) @(negedge clk);
        rst_n = 1;
        @(negedge clk); req = 1;                               // answered 2 cycles later
        @(negedge clk); req = 0;
        @(negedge clk); ack = 1;
        @(negedge clk); ack = 0;
        repeat (3) @(negedge clk);
        $display("answered request: %0d window failures", win_fails);
        @(negedge clk); req = 1;                               // planted bug: never answered
        @(negedge clk); req = 0;
        repeat (5) @(negedge clk);
        @(negedge clk); ack = 1;                               // planted bug: an ack with no request
        @(negedge clk); ack = 0;
        repeat (2) @(negedge clk);
        $display("unanswered request: %0d window failures; lone ack: %0d failures", win_fails, alone_fails);
        if (imm_fails != 11 || win_fails != 1 || alone_fails != 1) $fatal(1, "FAIL: counts %0d %0d %0d", imm_fails, win_fails, alone_fails);
        $display("PASS: the immediate check caught 11 bad grants, and each concurrent check caught its one planted bug");
        $finish;
      end
    endmodule
    Terminal
    immediate $onehot0 over all 16 grant values: 11 failures (11 expected)
    answered request: 0 window failures
    unanswered request: 1 window failures; lone ack: 1 failures
    PASS: the immediate check caught 11 bad grants, and each concurrent check caught its one planted bug
    Check yourself An always_comb assertion fires but the settled waveform looks fine. What happened?

    A delta-cycle glitch. The immediate assertion saw a transient combination, while the viewer shows settled values. A deferred assertion removes the false fire.

    Check yourself Can an immediate assertion check that ack follows req within 3 cycles?

    No. It has no clock and no temporal extent. That rule needs a concurrent assertion such as req |-> ##[1:3] ack.

    Remember

    • Immediate: procedural, one instant, current values.

    • Concurrent: clocked, sampled before the edge, spans cycles.

    • Plain immediate asserts in always_comb can fire on delta glitches.

    Rule of thumb: anything with time in it gets a clock.

    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.

    Related topics

    • Sequences

    • Implication and properties

    Previous

    101 / 259

    Next: The Four Layers of SVA

    Up next

    Up nextThe Four Layers of SVA

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Clocks & the Sampling Model
    • Evaluation Attempts & Threads
    • assert, assume, cover, restrict & Action Blocks
    clkreqack
    A request answered two cycles later, inside the 1-to-3 window