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
      • |-> vs |=>
      • Vacuous Success
      • Property Operators: not, and, or, if/else, implies
      • nexttime, always, s_eventually, until
      • Designing Good Antecedents
    • 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

‹Implication and Property Operators
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Assertions — Interview-Critical Deep Dive
  4. Implication and Property Operators
  5. nexttime, always, s_eventually, until

Part 4 · Assertions (SVA) · Intermediate

nexttime, always, s_eventually, until

Strong and weak temporal operators, until-family timing, and safety versus liveness in simulation.

On this page

    Why this matters

    A 'request eventually granted' check passed a regression in which one request was never granted. It was weak, and the run ended with the obligation still pending.

    After this lesson you can: tell weak from strong operators, pick until or until_with by the overlap tick, and bound liveness so simulation can check it.

    Pending at the end

    Temporal operators come in weak and strong pairs. A weak operator lets a still-pending obligation pass at end of evaluation, while a strong one fails it.

    Only pure s_eventually defers purely to the end. s_until, s_until_with, and s_nexttime can all fail mid-trace, before the end.

    Weak

    Strong

    Question answered

    Simulation note

    always p

    s_always [m:n] p

    Does p hold at every tick

    Only the weak form is unbounded; s_always needs a range

    eventually [m:n] p

    s_eventually p

    Does p arrive

    Only the strong form is unbounded (s_eventually [m:n] p also exists); weak eventually needs a range. A checkable bound is ##[1:MAX] p

    nexttime p

    s_nexttime p

    Does p hold next tick

    Strong form needs the next tick to exist

    p until q

    p s_until q

    Must q arrive

    Weak forgives a q that never comes

    p until_with q

    p s_until_with q

    Must q arrive, with p held through it

    Same, plus the overlap tick

    Check yourself A weak obligation is still pending when simulation ends. What is reported?

    Nothing: weak operators forgive pending obligations at end of evaluation. The strong flavor would fail instead.

    One tick of overlap

    The listing below is not run in this course's checks because Verilator 5.048 does not support s_until_with, s_eventually or property always.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // not-run: Verilator 5.048 does not support s_until_with, s_eventually or property always
    // Assumes busy, handoff, start, done, both_high are design signals.
    property p_hold;
      @(posedge clk) $rose(busy) |-> busy until handoff;
    endproperty
    
    property p_hold_strong;
      @(posedge clk) $rose(busy) |-> busy s_until_with handoff;
    endproperty
    
    property p_live;
      @(posedge clk) start |-> s_eventually done;
    endproperty
    
    property p_inv;
      @(posedge clk) always (!both_high);
    endproperty

    p until q needs p at every tick before the first q, excluding the q tick. until_with includes that tick, so one overlapping cycle decides between them.

    • Slot 0: busy 0, done 0. Idle.

    • Slot 1: busy rises. Both obligations start watching.

    • Slot 2: busy 1. Holding.

    • Slot 3: busy 1. Holding.

    • Slot 4: done 1, busy 0. until passes since the q tick is excluded, while until_with fails here.

    • Slot 5: both 0. Idle.

    Common pitfall

    until where the spec keeps the condition through the terminating event is off by one at the q tick. That overlap is exactly what until_with adds, so pick by the q-tick requirement.

    Safety, liveness, and simulation

    Safety says nothing bad ever happens, and any violation shows in a finite prefix. Liveness says something good eventually happens, and its violation is an infinite behavior.

    Finite traces cannot conclude liveness. For simulation sign-off, bound it: s_eventually x becomes ##[1:MAX] x, which turns the check into safety.

    Property

    Kind

    Simulation

    Formal

    Never two grants

    Safety

    Catches violations

    Proves

    Every request granted

    Liveness

    Pending-at-end heuristic only

    Finds starvation loops

    Grant within MAX cycles

    Bounded liveness

    Catches violations

    Proves

    Interview

    Likely tasks: classify never-overflow vs eventually-delivered as safety vs liveness, then give the bounded simulation rewrite of the second. Answers: safety and liveness; req |-> ##[1:MAX] gnt.

    Check yourself Why can simulation catch a safety violation but not conclude a liveness failure?

    A safety violation has a finite witness: the bad thing happened at some tick. A liveness violation is an infinite behavior, which no finite trace exhibits.

    Testbench

    The bench runs both until flavors as real assertions on the slot-4 stimulus from the diagram, then on two more episodes: busy held through the done tick, and a planted early drop. A bounded check stands in for s_eventually, which Verilator 5.048 does not support.

    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
    module tb_impl_until;
      logic clk = 0;
      always #5 clk = ~clk;
      logic busy = 0, done = 0, start = 0, fin = 0;
      int until_fails = 0, with_fails = 0, bounded_fails = 0;
    
      ap_until: assert property (@(posedge clk) $rose(busy) |-> busy until done)      else until_fails++;
      ap_with:  assert property (@(posedge clk) $rose(busy) |-> busy until_with done) else with_fails++;
      // Bounded liveness: the simulation form of "start |-> s_eventually fin".
      localparam int MAX = 6;
      ap_bounded: assert property (@(posedge clk) $rose(start) |-> ##[1:MAX] fin)   else bounded_fails++;
    
      // busy for n ticks, then done; overlap keeps busy high on the done tick.
      task automatic episode(int n, bit overlap, int gap = 0);
        @(negedge clk) busy = 1;
        repeat (n - 1) @(negedge clk);
        if (gap) begin busy = 0; repeat (gap) @(negedge clk); end
        busy = overlap; done = 1;
        @(negedge clk) busy = 0; done = 0;
        repeat (3) @(negedge clk);
      endtask
    
      initial begin
        repeat (2) @(negedge clk);
        episode(3, 0);              // the diagram: busy drops as done rises
        $display("busy drops on the done tick: until %0d failures, until_with %0d", until_fails, with_fails);
        episode(3, 1);              // busy held through the done tick
        $display("busy held through done:      until %0d failures, until_with %0d", until_fails, with_fails);
        episode(3, 0, 1);           // planted: busy drops one tick before done
        $display("busy drops before done:      until %0d failures, until_with %0d", until_fails, with_fails);
        @(negedge clk) start = 1; @(negedge clk) start = 0; repeat (3) @(negedge clk); fin = 1; @(negedge clk) fin = 0;
        @(negedge clk) start = 1; @(negedge clk) start = 0;            // planted: fin never comes
        repeat (MAX + 3) @(negedge clk);
        $display("bounded ##[1:%0d] fin: %0d failure (the start that never finished)", MAX, bounded_fails);
        if (until_fails != 1 || with_fails != 2 || bounded_fails != 1) $fatal(1, "FAIL: %0d %0d %0d", until_fails, with_fails, bounded_fails);
        $display("PASS: until forgave the done tick, until_with demanded it, both caught the early drop, and the bounded form caught the missing fin");
        $finish;
      end
    endmodule
    Terminal
    busy drops on the done tick: until 0 failures, until_with 1
    busy held through done:      until 0 failures, until_with 1
    busy drops before done:      until 1 failures, until_with 2
    bounded ##[1:6] fin: 1 failure (the start that never finished)
    PASS: until forgave the done tick, until_with demanded it, both caught the early drop, and the bounded form caught the missing fin

    Remember

    • Weak operators forgive obligations pending at the end.

    • until excludes the q tick; until_with includes it.

    • Bound liveness with ##[1:MAX] to make it checkable.

    Rule of thumb: every 'eventually' in a simulation check gets a bound.

    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.

    until and until_with gave right verdicts on all three episodes; nexttime, always [m:n], s_until, s_until_with and s_eventually were rejected as unsupported.

    Related topics

    • Protocol Assertions

    • Assertion Debug

    Previous

    113 / 259

    Next: Designing Good Antecedents

    Up next

    Up nextDesigning Good Antecedents

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • |-> vs |=>
    • Vacuous Success
    clkbusydone
    Busy held until done passes, while busy held through done fails at the done tick.