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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here5
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive39
  • SystemVerilog Assertions — Interview-Critical Deep Dive41
    • 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 SystemVerilog25
  • Testbench Architecture in SystemVerilog24
  • Advanced Language & Integration26
  • Senior & Interview Preparation23
  • Principal-Level Verification34
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
On this page

    Part 4 of 9, lesson 13 of 41Part 4 of 9/Assertions (SVA)/Lesson 13 of 41Intermediate

    nexttime, always, s_eventually, until

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

    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.

    The operators on a timeline

    Each diagram shows one stimulus and the verdict of every attempt, tick by tick. A signal row is the value sampled at that edge.

    Two attempts, one pass and one fail. nexttime looks at a single future tick and nothing else.

    always has no end of its own: one low p fails the attempt, and a p that stays high is never reported as a pass during the run.

    A pending strong obligation is neither pass nor fail yet. If gnt never came, both attempts would fail at the end of the run.

    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.

    The verdict rows show it per attempt: until ends successfully at the done tick, while until_with also needs busy at that tick and fails.

    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

    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
    • 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

    115 / 275

    Next: Designing Good Antecedents

    Up next

    Up nextDesigning Good Antecedents

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • |-> vs |=>
    • Vacuous Success
    clkstartpattempt at 1PASS: p highattempt at 4FAIL: p low
    start |-> nexttime p: each attempt is judged exactly one tick after it starts.
    clkstartpattempt at 1p okp okp okp okFAIL: p low
    start |-> always p: the attempt watches every tick and fails at the first low one.
    clkreqgntattempt at 1waitwaitwaitwaitPASSattempt at 3waitwaitPASS
    req |-> s_eventually gnt: two attempts wait, and one grant satisfies both.
    clkbusydone
    Busy held until done passes, while busy held through done fails at the done tick.
    clkbusydonebusy until donebusy okbusy okPASS: donebusy until_with donebusy okbusy okFAIL: busy low
    The same stimulus judged by both operators: busy drops on the tick where done is true.