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

  • SystemVerilog Language Foundations34
  • Object-Oriented SystemVerilog for Verification33
  • Constraint Randomization — Interview-Critical Deep Dive56
  • SystemVerilog Assertions — Interview-Critical Deep Dive54
    • SVA Fundamentals
    • Sequences: Temporal Building Blocks
      • Cycle Delays: ##N and ##[M:N]
      • Consecutive Repetition: [*N], [*M:N], [*0]
      • goto [->N] & Non-Consecutive [=N] Repetition
      • and, or, intersect
      • throughout & within
      • first_match() & Sequence Endpoints
    • Implication & Property Operators
    • Sampled Value Functions
    • Protocol Assertion Cookbook
    • Advanced SVA: Local Variables, Multiclock & More
    • Assertion Debug & Methodology
    • Interview SVA Patterns — Write-the-Assertion Drills
    • SVA Interview Q&A — Deep Answers
  • Functional Coverage in SystemVerilog28
  • Testbench Architecture in SystemVerilog32
  • Advanced Language & Integration30
  • Senior & Interview Preparation29
Loading lesson

‹Sequences: Temporal Building Blocks
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Assertions — Interview-Critical Deep Dive
  4. Sequences: Temporal Building Blocks
  5. first_match() & Sequence Endpoints

Part 4 · Assertions (SVA) · Intermediate

first_match() & Sequence Endpoints

Thread explosion from multiple matches, first_match pruning, and .triggered/.matched endpoint detection.

On this page

    The multiple-match problem

    Every ranged operator multiplies match threads: ##[1:8] forks 8, [*2:5] forks 4, and nesting multiplies them. Multiple matches are not an error — but they bite in two places. First, performance: each live thread is simulator state, and a level antecedent retriggering into a wide range can hold thousands of threads alive. Second, semantics: when a sequence's endpoint feeds something else (a consequent suffix, a .triggered test, a not), every match thread propagates, and the property must hold for the threads you did not intend.

    Diagram
    THREAD EXPLOSION — req ##[1:4] ack ##[1:4] done (one attempt)
    
    attempt tick T
       ├─ ack@T+1 ─┬─ done@T+2
       │           ├─ done@T+3
       │           ├─ done@T+4
       │           └─ done@T+5
       ├─ ack@T+2 ─┬─ done@T+3 ... (4 threads)
       ├─ ack@T+3 ─┬─ ...          (4 threads)
       └─ ack@T+4 ─┬─ ...          (4 threads)
                             = 16 live threads PER ATTEMPT
    A level antecedent attempting every cycle for 100 cycles
    → up to 1600 concurrent threads from one assertion.

    first_match: pruning to the earliest completion

    first_match(s) matches only the earliest-ending match of s from a given attempt; the moment any thread of s completes, all other threads of that attempt are killed. If several threads end on the same earliest tick, those ties all survive — first_match prunes by end time, not to a single thread. Use it when you mean "the first time this pattern completes" and when a multi-match sequence's endpoint must be unique for downstream logic.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Without first_match: every ack in the window spawns a thread
    sequence s_any_ack;   req ##[1:8] ack;              endsequence
    // With first_match: only the EARLIEST ack ends the sequence
    sequence s_first_ack; first_match(req ##[1:8] ack); endsequence
    
    // Endpoint-sensitive use: 'done' must follow the FIRST ack
    property p_done_after_first_ack;
      @(posedge clk) $rose(req) |->
        first_match(req ##[1:8] ack) ##1 done;
    endproperty
    a_fa: assert property (p_done_after_first_ack);
    // Without first_match, done after ANY ack-thread endpoint passes —
    // a later spurious ack could legitimize a late done.
    Diagram
    WAVEFORM — first_match(req ##[1:8] ack)  (attempt @2)
    
    clk  :  1    2    3    4    5    6    7    8
    req  :  0    1    0    0    0    0    0    0
    ack  :  0    0    0    1    0    1    0    0
                 A         M1        m2
    plain sequence : matches at tick 4 AND tick 6 (two endpoints)
    first_match    : tick 4 thread completes first → MATCH @4,
                     all sibling threads killed → m2 never reported
    
    Effect on '##1 done' suffix with done only at tick 7:
      plain:        endpoint @6 exists → done@7 → PASS (maybe unintended)
      first_match:  endpoint @4 only   → done@5? no → FAIL (strict)

    Sequence endpoints: .triggered and .matched

    A named sequence exposes its endpoint as a boolean: s.triggered is true in any time step where some evaluation of s reaches its endpoint in that step (sampled, usable in expressions, including disable iff conditions and other sequences). s.matched serves the same endpoint-detection purpose for multi-clocked contexts: it persists the endpoint across to the next tick of a destination clock, making it the clock-domain-crossing flavor. Endpoint methods let one sequence act as an event for another property without re-describing the pattern.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    sequence s_wr_burst;            // endpoint = last beat of a write burst
      wr_start ##1 wr_beat [->4];
    endsequence
    
    // Use the endpoint inside another property (same clock): .triggered
    property p_resp_after_burst;
      @(posedge clk) s_wr_burst.triggered |-> ##2 wr_resp;
    endproperty
    a_resp: assert property (p_resp_after_burst);
    
    // Cross-clock endpoint hand-off: .matched
    sequence s_src;   @(posedge clk_a) val_a ##1 done_a;          endsequence
    sequence s_pair;  @(posedge clk_b) s_src.matched ##1 done_b;  endsequence
    a_cdc: assert property (@(posedge clk_b) start_b |-> s_pair);
    Diagram
    WAVEFORM — s_wr_burst.triggered as an event
    
    clk      :  1    2    3    4    5    6    7    8
    wr_start :  0    1    0    0    0    0    0    0
    wr_beat  :  0    0    1    1    0    1    0    0
                     A    b1   b2        b4*
    .triggered: 0    0    0    0    0    1    0    0
                                         E
    E = endpoint tick: the [->4] completes at tick 6 → s.triggered
        is 1 exactly there, 0 everywhere else.
    The downstream property treats E as a one-tick event:
      s.triggered |-> ##2 wr_resp   → wr_resp required @8.

    Interview angle and review

    Interview angle

    • "What does first_match actually kill?" — sibling threads of the SAME attempt, at the first completed end tick; ties at that tick all survive. "It picks one random match" is the wrong answer.

    • "When is first_match required, not just an optimization?" — when the sequence endpoint feeds downstream timing (suffix elements, .triggered consumers) and only the earliest completion is meaningful.

    • "triggered vs matched?" — both detect endpoints; .triggered is the general same-timestep form, .matched persists across clocks for multi-clock sequence pairing.

    • "Estimate the threads of req ##[1:4] ack ##[1:4] done." — 16 per attempt; multiply the ranges. Interviewers use this to test whether you think in threads at all.

    Key takeaways

    • Ranged operators multiply match threads; nested ranges multiply multiplicatively — count them.

    • first_match prunes an attempt to its earliest-ending match(es) and kills the sibling threads.

    • first_match changes meaning, not just speed, whenever the sequence endpoint drives downstream timing.

    • s.triggered turns a sequence endpoint into a boolean event; s.matched is the multi-clock variant.

    Common pitfalls

    • Wrapping first_match around a consequent just to silence multiple-match warnings — verify the earliest endpoint is really the spec's intent.

    • Believing first_match always yields exactly one thread — same-tick ties all survive the pruning.

    • Using .triggered on a sequence with ranged operators and assuming a unique fire tick — it pulses at EVERY endpoint unless first_match'd.

    • Using .triggered across clock domains where .matched is required — endpoint sampled in the wrong domain's time step.

    • Ignoring thread cost of level-retriggered wide ranges — assertion-dominated simulation slowdowns that profile as "mysterious".

    Previous

    135 / 296

    Next: Implication & Property Operators

    Up next

    Up nextImplication & Property Operators

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Cycle Delays: ##N and ##[M:N]
    • Consecutive Repetition: [*N], [*M:N], [*0]
    • goto [->N] & Non-Consecutive [=N] Repetition