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
    • Sampled Value Functions
      • $rose, $fell, $stable, $changed
      • $past: Looking Backward
      • $countones, $onehot, $onehot0, $isunknown
      • Building Data-Integrity Checks
    • 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

←Sampled Value Functions
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Assertions — Interview-Critical Deep Dive
  4. Sampled Value Functions
  5. $past: Looking Backward
On this page

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

    $past: Looking Backward

    Past reaches N samples back, with gating for valid-qualified pipelines and guards for early cycles.

    A latency check on an elastic pipe failed at the first bubble. It used $past(in, 2), which counts clock ticks, while the data moved only on valid beats.

    After this lesson you can: reach back N samples with $past, keep history aligned to valid beats, and guard the cycles before history exists.

    N samples back

    $past(expr) returns the value sampled on the previous clocking event. $past(expr, N) reaches N events back, as if each call owned a shift register of snapshots.

    That shape suits backward-looking checks. Instead of opening a thread that waits N cycles for an output, trigger on the output and reach back to the input.

    The full signature is $past(expr, N, gate, clock). The gate is third and the clock is fourth: $past(din, 2, vld) advances only on ticks where vld holds.

    Standardize on the three-argument gated form. The double-comma form $past(e, 1, , @(posedge clk)) exists only to pass an explicit clock with no gate.

    Call

    History advances

    Meaning

    $past(din)

    Every tick

    Value at the previous tick.

    $past(din, 2)

    Every tick

    Value two ticks back.

    $past(din, 2, vld)

    Only when vld holds

    Value two qualifying beats back.

    On a gated tick, $past(e, 1, gate) still returns the previous gated sample. The current tick enters the history only for future ticks.

    Check yourself A pipeline moves only on vld. Which call reads the input two data beats back?

    $past(in_data, 2, vld). The ungated $past(in_data, 2) counts clock ticks and misaligns on the first bubble.

    Read down any column: at edge 4, $past(data, 2) is d2, the sample from edge 2. The x slots at the start are why a $past check needs a guard for its first N cycles.

    Guard the first N cycles

    Before the history fills, $past returns the default sampled value: the declaration initializer if there is one, otherwise the type default (X for 4-state logic, 0 for 2-state types such as int or bit).

    The 2-state case is the silent one. A legal-looking 0 can satisfy a check for the wrong reason on cycle 1, so guard the start-up window explicitly.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet
    bit [3:0] cycles_since_rst;
    always_ff @(posedge clk or negedge rst_n)
      if (!rst_n) cycles_since_rst <= '0;
      else if (!(&cycles_since_rst)) cycles_since_rst <= cycles_since_rst + 1;
    assert property (@(posedge clk) disable iff (!rst_n)
      (cycles_since_rst >= LAT) |-> (out_data == $past(in_data, LAT)));

    A cleaner guard comes free when the consequent cannot fire early. If out_valid cannot rise until LAT beats after the first input, the history is always filled when the check runs.

    Place $past in the consequent and trigger on the effect. A $past inside a delayed consequent is relative to the consequent tick, not the antecedent tick.

    Trusting $past of a 2-state expression during start-up returns a clean 0 that proves nothing. Count warm-up cycles or key the check off a valid that cannot fire early.

    Check yourself count is an int that steps by one each cycle. What does count == $past(count) + 1 report on cycle 1?

    Whatever the reset value implies, not a real step: $past returns 0 before any history exists, so the comparison is against a default rather than a sample.

    Pipelines that move on valid

    An elastic pipeline advances only on qualifying beats. The gated form keeps the comparison aligned through any stall pattern, where the ungated form fails on the first bubble.

    The listing below is not run in this course's checks because Verilator 5.048 does not support $past with a gate argument. The bench at the end of this lesson keeps the same history with two flops that advance only on valid, which every simulator runs.

    pipe_check.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // not-run: Verilator 5.048 does not support $past with a gate argument
    module pipe_check #(int DEPTH = 3) (
      input logic clk, rst_n,
      input logic in_vld,  input logic [31:0] in_data,
      input logic out_vld, input logic [31:0] out_data
    );
      int unsigned vld_beats;
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) vld_beats <= 0;
        else if (in_vld) vld_beats <= vld_beats + 1;
      assert property (@(posedge clk) disable iff (!rst_n)
        (out_vld && vld_beats >= DEPTH - 1) |->
          (out_data == $past(in_data, DEPTH - 1, in_vld)));
    endmodule

    A three-stage pipe that moves on valid shows, on each valid beat, the input from two gated beats before the current one. That is $past(in_data, DEPTH - 1, in_vld), because the current gated tick is not yet in the history. Idle slots never enter it.

    • Slot 0: valid beat with a; out_data still 0, history holds one beat.

    • Slot 1: valid beat with b; out_data still 0, history holds two beats.

    • Slot 2: bubble; out_data still 0, history frozen.

    • Slot 3: valid beat with c; out_data a, two gated beats before c.

    • Slot 4: bubble; out_data back to 0, output lane idle.

    • Slot 5: valid beat with d; out_data b.

    • Slot 6: valid beat with e; out_data c.

    An explicit clock argument such as $past(sig_a, 1, , @(posedge clk_b)) samples an asynchronous signal with no synchronization. Assert within each domain and leave crossing proofs to CDC tools.

    Likely question: “How do you check N-cycle latency when only some cycles carry data?”

    • Gated form $past(in, N, valid) plus a guard for the first N valid beats.

    • Off-by-one stated plainly: on a gated tick the call returns the previous gated sample.

    • Bubble trace drawn from memory, with the ungated failure named.

    Testbench

    The bench checks the elastic pipe from the diagram twice: against a valid-gated history kept by two flops, and against the ungated $past(in_data, 2). Then it plants a pipe that shifts on bubbles too.

    Predict first The pipe is correct and the trace has three bubbles. Which check fails, the gated one or the ungated one?

    The ungated one. It counts clock ticks, so every bubble misaligns it, while the gated history stays aligned and reports nothing.

    vpipe3.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // A three-stage pipe that moves only on valid: out_data is the input from
    // two valid beats before the current one, as in the diagram.
    module vpipe3 (
      input  logic clk, rst_n, in_vld, bug,
      input  logic [7:0] in_data,
      output logic [7:0] out_data
    );
      logic [7:0] h0, h1;
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin h0 <= 0; h1 <= 0; end
        else if (in_vld || bug) begin h0 <= in_data; h1 <= h0; end   // bug: shifts on a bubble too
      assign out_data = h1;
    endmodule
    
    Terminal
    correct pipe, 6 valid beats and 3 bubbles: gated check 0 failures, ungated $past 3
    planted bug, the pipe shifts on bubbles: gated check 3 failures
    PASS: the valid-gated history stayed aligned through bubbles and caught the pipe that shifted on them
    • $past(e, N) counts clock ticks; gated history counts qualifying beats.

    • Before history fills, $past returns the default value; guard it.

    • Trigger on the effect and look back to the cause.

    Rule of thumb: if data moves on valid, the history moves on valid too.

    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 $past with a gate argument; two flops that advance on valid kept the same history, stayed aligned through 3 bubbles and caught the planted pipe, while ungated $past(in_data, 2) failed 3 times on the correct pipe.

    Related topics

    • Edges between snapshots

    • Assembling real checks

    Previous

    118 / 275

    Next: $countones, $onehot, $onehot0, $isunknown

    Up next

    Up next$countones, $onehot, $onehot0, $isunknown

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Building Data-Integrity Checks
    clkdatad0d1d2d3d4d5d6$past(data)d0d1d2d3d4d5$past(data, 2)d0d1d2d3d4
    $past(data) and $past(data, 2) are the sampled values one and two edges back; the first edges have no history.
    clkin_vldin_dataab0c0deout_vldout_data0a0bc
    Gated history stays aligned through bubbles