Writing out a ##1 a ##1 a to say "a should be true for 3 cycles in a row" gets unwieldy fast, especially once the count grows. SystemVerilog provides three repetition operators that express this compactly — and two of them, despite looking similar, behave in a genuinely different way that's worth understanding precisely.

13 min read | Intermediate Level

What You'll Learn

  • How [*n] compactly expresses n consecutive repetitions of the same condition
  • How [->n] (goto repetition) finds the nth occurrence of a condition, with gaps allowed in between
  • How [=n] (non-consecutive repetition) differs from goto in exactly where its match is allowed to end
  • Why the exact same signal trace can pass under [=n] but fail under [->n]

Consecutive Repetition: [*n]

expr[*n] requires expr to be true for n consecutive clock cycles in a row. req2[*3] is exactly equivalent to writing req2 ##1 req2 ##1 req2 — the repetition operator just avoids spelling that out by hand:


module tb;
  bit req1, req2;
  bit clk;

  always #10 clk = ~clk;

  // Once req1 is seen, req2 must stay high for 3 consecutive
  // cycles starting the very next cycle
  sequence s_consec;
    @(posedge clk) req1 ##1 req2[*3];
  endsequence

  assert property (s_consec)
    $display ("[%0t] Match: req2 held high for 3 consecutive cycles", $time);

  initial begin
    req1 <= 1; req2 <= 0;
    @(posedge clk);                 // 10ns: req1=1

    req1 <= 0; req2 <= 1;
    @(posedge clk);                 // 30ns: req2's 1st consecutive cycle

    @(posedge clk);                 // 50ns: req2's 2nd consecutive cycle

    @(posedge clk);                 // 70ns: req2's 3rd consecutive cycle - match completes

    req2 <= 0;
    @(posedge clk);                 // 90ns

    #20 $finish;
  end
endmodule

req1 is seen at 10ns, and req2 stays high for exactly three cycles afterward — 30ns, 50ns, and 70ns — so the sequence completes and the assertion's action block fires at 70ns, printing "Match: req2 held high for 3 consecutive cycles". Every other independent attempt starting at 30ns, 50ns, 70ns, or 90ns fails immediately, since req1 is 0 at each of those points — only the attempt that started with req1=1 at 10ns has any chance of succeeding.

Range Repetition: [*n:m] and [*n:$]

A range works the same way, just with flexible bounds instead of a fixed count:


req2[*2:4]    // req2 true for anywhere between 2 and 4 consecutive cycles
req2[*2:$]    // req2 true for 2 or more consecutive cycles, with no upper limit

Quick Check

Q: In the worked example above, if req2 had only stayed high for 2 cycles (30ns and 50ns) before dropping at 70ns, would req1 ##1 req2[*3] still match?

Show Answer

No. [*3] requires exactly 3 consecutive cycles — dropping after only 2 breaks the requirement, and this specific attempt fails. A range like [*2:3] would have accepted either count, but a plain [*3] does not.

Goto Repetition: [->n]

expr[->n] waits for expr to become true a total of n times — the occurrences don't need to be consecutive, gaps are allowed in between. The important detail: the sequence's own match point is fixed exactly at the cycle of the nth occurrence, with no flexibility about where that match "ends."


sequence s_goto;
  @(posedge clk) a[->2] ##1 b;
endsequence

This says: wait for a to occur twice (with any gap in between), and b must be true in the cycle immediately following that 2nd occurrence — no later.

Non-Consecutive Repetition: [=n]

expr[=n] also waits for expr to become true n times with gaps allowed, but differs from goto in one specific way: its match point does not have to be exactly the nth occurrence — it can be any later cycle too, as long as expr stays false the whole way there:


sequence s_nonconsec;
  @(posedge clk) a[=2] ##1 b;
endsequence

Because [=n]'s match point can slide forward through any number of additional false cycles, this is effectively evaluated as several candidate possibilities in parallel — the property succeeds overall if any valid placement makes the rest of the sequence hold true.

Comparing goto and non-consecutive on the Same Trace

Here's a trace where a occurs at cycles 1 and 3 (with a gap at cycle 2), and b doesn't show up until cycle 5, staying false at cycles 2 and 4:

Cycle12345
a10100
b00001

a[->2] ##1 b (goto): a's 2nd occurrence is at cycle 3, so the goto match ends exactly there — no flexibility. ##1 b then requires b to be true precisely at cycle 4. But b=0 at cycle 4, so this fails.

a[=2] ##1 b (non-consecutive): the match point for the [=2] portion doesn't have to be cycle 3 — it can also be cycle 4, since a stays false there. Using that later match point, ##1 b now requires b at cycle 5 instead — and b=1 there, so this passes.

This exact behavior — goto and non-consecutive repetition genuinely disagreeing on the same trace — is one of the most subtle corners of SVA syntax, and it's easy to find experienced engineers who mix the two up. If this distinction matters for a real check you're writing, it's worth verifying the exact behavior against your specific simulator rather than relying on this explanation alone, since matching semantics for repetition operators are a known area of subtlety even among production tools.

Quick Check

Q: Using the same trace, if b were high at cycle 4 instead of cycle 5, would both a[->2] ##1 b and a[=2] ##1 b pass?

Show Answer

Yes. If b is true exactly one cycle after a's 2nd occurrence (cycle 3), both the rigid goto match point and one of the non-consecutive repetition's candidate match points land on the same answer — the two operators only disagree when the correct answer specifically depends on that extra flexibility.

Where Are These Used?

[*n] is common for checking a signal holds steady for a required minimum duration — a reset pulse width, or a data-valid window. [->n] and [=n] show up in protocols where an event needs to happen a specific number of times before something else is expected, like waiting for a fixed number of acknowledge pulses across an arbitrary number of cycles before checking that a completion signal follows.

Common Beginner Mistakes

Mistake #1: Confusing [*n] with [->n]

[*n] requires consecutive cycles; [->n] allows gaps between occurrences. Why? Using [*n] when gaps are actually expected will fail the moment the condition drops even briefly — the wrong operator here doesn't just give an unexpected result, it changes what's actually being checked.

Mistake #2: Assuming goto and non-consecutive repetition are interchangeable

As the comparison trace shows, they can produce different results on the exact same signal behavior. Why? Goto rigidly anchors its match to the nth occurrence; non-consecutive repetition allows that match point to slide later through additional false cycles — picking the wrong one changes what a downstream ##1 continuation actually requires.

Mistake #3: Forgetting a range's upper bound still needs to be reachable

[*2:4] requires the repeated condition to genuinely hold for somewhere between 2 and 4 consecutive cycles. Why? If the condition drops after only 1 cycle, that's below the range's minimum, and the attempt fails — a range doesn't make the minimum optional.

Mistake #4: Overusing [=n] or [->n] when [*n] would already do the job

If the intended behavior genuinely requires consecutive cycles, reaching for the more flexible non-consecutive or goto operators adds unnecessary ambiguity. Why? The more permissive operators accept traces a consecutive check would correctly reject — use the most restrictive operator that actually matches your intended check.

Practice Exercise

Challenge: Predict a Longer Trace

Given a occurring at cycles 1, 3, and 5 (with gaps at 2 and 4), and b true only at cycle 7 (staying false at cycle 6), predict whether a[->3] ##1 b and a[=3] ##1 b each pass or fail.

Hint

a's 3rd occurrence is at cycle 5. Work out exactly which cycle each operator's match point can land on, then check what ##1 b requires from there.

Solution

a[->3] ##1 b fails. The 3rd occurrence of a is at cycle 5, so goto's match ends exactly there, requiring b at cycle 6 — but b=0 at cycle 6.

a[=3] ##1 b passes. The non-consecutive match point can slide forward to cycle 6 (since a stays false there), which then requires b at cycle 7 instead — and b=1 there. Same underlying logic as the article's own example, just one cycle further along.

Summary

In this tutorial, you learned:

  • [*n] requires a condition to hold true for n consecutive cycles, and [*n:m]/[*n:$] extend that to a range
  • [->n] (goto repetition) waits for the nth occurrence of a condition, allowing gaps, but ends its match exactly at that nth occurrence
  • [=n] (non-consecutive repetition) also allows gaps, but its match point can slide forward through additional cycles where the condition stays false
  • This flexibility means goto and non-consecutive repetition can genuinely disagree on the same trace, specifically when a later match point would succeed but the exact nth-occurrence point would not
  • Always use the most restrictive operator that matches your intended check — reaching for extra flexibility you don't actually need makes a property accept traces it should reject
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5
Cron Job Starts