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
    • 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
      • Handshake Drills
      • Pulse & Stability Drills
      • Ordering & Counting Drills
      • Data & Reset Drills
      • FSM & Arbiter Drills
      • Q&A: Assertion Fundamentals
      • Q&A: Operators & Semantics
      • Q&A: Vacuity, Debug & Formal
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹SVA Interview Drills
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Assertions — Interview-Critical Deep Dive
  4. SVA Interview Drills
  5. Q&A: Operators & Semantics

Part 4 · Assertions (SVA) · Intermediate

Q&A: Operators & Semantics

Implication anchors, delay ranges, repetition endpoints, window operators, first_match.

On this page

    Why this matters

    A candidate wrote $rose(req) |=> ##2 ack for 'ack two cycles after req rises' and defended it for five minutes. It checks cycle T+3.

    After this lesson you can: state precisely what each implication, delay, repetition and window operator means, cycle by cycle.

    Note

    Revision page. Answer each question before opening it; the full lessons are Sequences and Implication and Property Operators.

    Predict first (a ##1 b) |-> c: on which cycle is c checked? And with |=>?

    On b's cycle, where the antecedent ends; with |=>, one cycle later.

    Predict first Why can $rose(x) |-> !x never pass a real pulse?

    $rose(x) means x is 1 in this cycle, so !x in the same cycle is false. It fails on every pulse.

    Predict first On b = 0 1 0 1 1, does b [*3] match? Does b [->3]?

    b [*3] does not: the three 1s are not adjacent. b [->3] does, ending on the third 1.

    Implication anchors

    |-> and |=> are both implications: a non-matching antecedent ends the attempt vacuously, a matching one obliges the consequent. |-> begins consequent evaluation in the same cycle the antecedent sequence completes. |=> begins one cycle later, and is exactly equivalent to |-> ##1.

    “Same cycle” means the cycle the antecedent ends, which for a multi-cycle antecedent is its last cycle, not its first. Quote the spec to choose: “ack with the last beat” takes |->, “ack after the last beat” takes |=>.

    cycle

    0

    1

    2

    antecedent a ##1 b

    a

    b (ends here)

    |-> checks c

    here

    |=> checks c

    here

    The classic contradiction is $rose(x) |-> !x: $rose means x is high in the sampled cycle, so the consequent can never hold non-vacuously. Every pulse-width drill exists to catch exactly this slip, and the fix is |=>.

    Interview

    Likely follow-up: “when is |-> on a boolean antecedent actually a contradiction?”

    • Answer whenever the consequent denies in the same cycle what the antecedent asserts.

    • Give $rose(x) |-> !x as the canonical instance.

    • Point at the pulse drills as the family that catches it.

    Check yourself The spec says ack two cycles after the req rise. A candidate writes $rose(req) |=> ##2 ack. Which cycle is checked, and what fails?

    Rise at T, |=> moves to T+1, ##2 moves to T+3. A correct DUT with ack at T+2 fails falsely. Off-by-ones fail good designs, which is how triage spots them.

    Ranges and repetition endpoints

    A nondeterministic delay a ##[1:5] b forks one thread per offset, and b at any offset matches. In a consequent one surviving match suffices: existential. In an antecedent every match raises its own obligation: universal. Same operator, opposite quantification per side.

    offset

    +1

    +2

    +3

    +4

    +5

    ack

    0

    1

    0

    1

    0

    thread matches

    yes

    yes

    As a consequent, one match is enough and the attempt passes. As an antecedent, each match raises its own obligation, and each must pass.

    Contrast with b[->1], which matches exactly once at the first b. For consequents the verdicts usually coincide. For antecedents, composition, and cover counting, the multiplicity is visible: covers on ##[1:5] can count several hits per event.

    Consecutive, goto, nonconsecutive

    All three repeat a boolean three times and differ in gaps and endpoint. b[*3] needs three adjacent cycles. b[->3] allows non-b gaps and ends exactly on the third b. b[=3] allows gaps and may extend past the third b through trailing non-b cycles. Within any match window the count is exactly three: a fourth b kills that thread.

    Form

    On b = 0 1 0 1 1 0 0

    Ends

    b [*3]

    No match: the 1s are not adjacent

    Never

    b [->3]

    Matches

    Exactly on the third 1

    b [=3]

    Matches

    On the third 1, or any later cycle without a fourth

    One spec per operator fixes the triad. [*3]: busy stays high three cycles. [->3]: the third error interrupts now. [=3]: a frame holds exactly three parity events, then EOF whenever it comes. The endpoint distinction decides what ##1 means after the repetition, which is what the counting drills test.

    Check yourself Why does [=3] forbid a fourth b inside its window?

    Each match thread counts exactly three within its span. A fourth b inside the span matches no [=3] thread, so every candidate window holding it dies. “Exactly N” is per window, not a lower bound.

    Windows and first matches

    Three containment relations cover conditions over windows. Expr throughout seq demands expr at every cycle of the match. Seq1 within seq2 matches seq1 somewhere inside seq2's span. Seq1 intersect seq2 demands both match over exactly the same cycles: same start, same end.

    This fragment is an illustration, so the course checks skip it. Verilator 5.048 also does not support the unbounded [*1:$] repetition.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet
    p_stable_burst: assert property (@(posedge clk) disable iff (!rst_n)
      $rose(burst) |-> mode_locked throughout (burst[*1:$] ##1 !burst));
    
    p_cal_in_init: assert property (@(posedge clk) disable iff (!rst_n)
      $rose(init) |-> (cal_done[->1]) within (init[*1:$] ##1 !init));
    
    p_3_in_10: assert property (@(posedge clk) disable iff (!rst_n)
      $rose(win) |=> (ev[=3]) intersect 1[*10]);

    Intersect is the length equalizer and and is its loose cousin: and requires a common start but lets lengths differ, so ev[=3] could sprawl past the 10-cycle window and still hold. That contrast is always the follow-up.

    First_match collapses a multi-match sequence to its earliest match. A ranged antecedent without it raises one obligation per match, so a spec that says “from the first ready” fails falsely on later matches. When the spec wants every match checked, first_match would mask real bugs: the operator implements “the first”, and the spec decides.

    Common mistakes

    Common pitfall

    Quoting “same cycle” for |-> without saying which cycle for multi-cycle antecedents. Using and where intersect is needed, silently losing the window-length pin. Reflexive first_match on every ranged sequence, hiding bugs the spec wanted checked.

    Interview

    Likely probe: “consequent versus antecedent — same ##[1:5], what flips?”

    • Answer the quantifier: existential in consequents, universal in antecedents.

    • Show the two-match thread diagram and its consequence count.

    • Land on first_match as the antecedent-side remedy, with its warning.

    Check yourself The antecedent start ##[1:5] ready matches at +3 and +4, and data_ok holds only at +3. Verdict with and without first_match?

    Without: two obligations, the +4 one fails, so the assertion fails on a spec-compliant DUT. With first_match around the antecedent: only +3 survives, pass. The spec must say “first” for the fix to be right.

    Pair

    Distinction that decides

    |-> versus |=>

    Antecedent end cycle versus one later

    ##[a:b] consequent versus antecedent

    Existential match versus universal obligations

    [->N] versus [=N]

    Ends on the Nth versus may trail

    intersect versus and

    Identical spans versus common start only

    Testbench

    The bench checks four answers with real assertions and covers: both implication anchors on a two-cycle antecedent, the $rose(x) |-> !x contradiction over three pulses, and the two repetition forms on the gapped trace.

    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
    module tb_qa_operators;
      logic clk = 0, a = 0, b = 0, c = 0, x = 0, bt = 0, st = 0;
      always #5 clk = ~clk;
      int ovl_fails = 0, novl_fails = 0, contra_fails = 0, star_hits = 0, goto_hits = 0;
    
      // A two-cycle antecedent: |-> checks c on b's cycle, |=> one later.
      a_ovl:  assert property (@(posedge clk) (a ##1 b) |-> c) else ovl_fails++;
      a_novl: assert property (@(posedge clk) (a ##1 b) |=> c) else novl_fails++;
      // The classic contradiction: $rose(x) means x is 1 in this cycle.
      a_contra: assert property (@(posedge clk) $rose(x) |-> !x) else contra_fails++;
      // Endpoints on the lesson's gapped trace: from st at cycle 0, b at cycles 1, 3 and 4.
      c_star: cover property (@(posedge clk) st ##1 bt [*3]) star_hits++;
      c_goto: cover property (@(posedge clk) st ##0 bt [->3]) goto_hits++;
    
      initial begin
        repeat (2) @(negedge clk);
        a = 1; @(negedge clk) a = 0; b = 1; c = 1;            // c on b's cycle only
        @(negedge clk) b = 0; c = 0;
        repeat (3) @(negedge clk);
        $display("c on b's cycle: |-> %0d failures, |=> %0d failures", ovl_fails, novl_fails);
        repeat (3) begin x = 1; @(negedge clk) x = 0; @(negedge clk); end
        $display("$rose(x) |-> !x over 3 pulses: %0d failures", contra_fails);
        st = 1; @(negedge clk) st = 0; bt = 1; @(negedge clk) bt = 0; @(negedge clk) bt = 1; @(negedge clk); @(negedge clk) bt = 0;   // b: 0 1 0 1 1
        repeat (4) @(negedge clk);
        $display("trace b = 0 1 0 1 1: b [*3] matched %0d times, b [->3] matched %0d times", star_hits, goto_hits);
        if (ovl_fails != 0 || novl_fails != 1 || contra_fails != 3 || star_hits != 0 || goto_hits != 1)
          $fatal(1, "FAIL: %0d %0d %0d %0d %0d", ovl_fails, novl_fails, contra_fails, star_hits, goto_hits);
        $display("PASS: the anchors, the contradiction and the two repetition endpoints all behaved as the answers say");
        $finish;
      end
    endmodule
    Terminal
    c on b's cycle: |-> 0 failures, |=> 1 failures
    $rose(x) |-> !x over 3 pulses: 3 failures
    trace b = 0 1 0 1 1: b [*3] matched 0 times, b [->3] matched 1 times
    PASS: the anchors, the contradiction and the two repetition endpoints all behaved as the answers say

    Remember

    • |-> starts on the antecedent's last cycle; |=> one later.

    • [*N] adjacent, [->N] ends on the Nth, [=N] may trail.

    • Ranges are existential in consequents, universal in antecedents.

    Rule of thumb: draw the trace with cycle numbers before naming an operator.

    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.

    On Verilator 5.048 a goto started on a cycle where b was already 1 did not count that occurrence; the bench starts the count on a 0, as the lesson's trace does.

    Related topics

    • Ordering & Counting Drills

    • Q&A: Assertion Fundamentals

    Previous

    139 / 259

    Next: Q&A: Vacuity, Debug & Formal

    Up next

    Up nextQ&A: Vacuity, Debug & Formal

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Handshake Drills
    • Pulse & Stability Drills
    • Ordering & Counting Drills