Part 4 · Assertions (SVA) · Intermediate

goto [->N] & Non-Consecutive [=N] Repetition

The [->N] vs [=N] discriminator on identical waveforms, endpoint alignment, and practical Nth-occurrence patterns.

Two operators for scattered occurrences

Consecutive repetition requires back-to-back ticks. Real traffic is rarely back-to-back: you want "the 3rd ack, whenever the acks happen to arrive". Both [->N] (goto) and [=N] (non-consecutive) count N possibly-scattered occurrences of a boolean, allowing any number of non-occurrence ticks between them. They differ in exactly one thing — where the sequence is allowed to end . Goto must end ON the tick of the Nth occurrence. Non-consecutive may extend past it through additional non-occurrence ticks before ending.

systemverilog
// goto: endpoint IS the Nth occurrence tick
sequence s_third_ack_goto;
  start ##1 ack [->3];        // ends exactly on the 3rd ack
endsequence

// non-consecutive: endpoint may drift past the Nth occurrence
sequence s_third_ack_eq;
  start ##1 ack [=3];         // 3 acks total, trailing gap allowed
endsequence

// Formal definitions from the LRM:
//   b [->N]  ===  (!b [*0:$] ##1 b) [*N]
//   b [=N]   ===  b [->N] ##1 !b [*0:$]

Side by side on the identical waveform

This is the #1 interview discriminator question for repetition. Same stimulus, both operators, followed by the same next element — watch where each one permits done to appear.

diagram
STIMULUS (identical for both operators)

clk   :  1    2    3    4    5    6    7    8    9
start :  0    1    0    0    0    0    0    0    0
ack   :  0    0    1    0    1    0    0    1    0
              A    a1        a2             a3
3rd ack occurs at tick 8.

CASE 1 — start ##1 ack[->3] ##1 done
clk   :  1    2    3    4    5    6    7    8    9
done  :  0    0    0    0    0    0    0    0    1
                                            E    M
E = ack[->3] endpoint LOCKED to tick 8 (the 3rd ack itself)
M = done required at exactly tick 9  MATCH
If done instead came at tick 10 or 11: FAIL — no flexibility.

CASE 2 — start ##1 ack[=3] ##1 done
clk   :  1    2    3    4    5    6    7    8    9   10   11
ack   :  0    0    1    0    1    0    0    1    0    0    0
done  :  0    0    0    0    0    0    0    0    0    0    1
                                            e8   e9   e10  M
[=3] can end at tick 8, 9, 10, ... (trailing !ack gap allowed)
 threads exist expecting done at 9, 10, 11, ...
done at tick 11  the thread that ended [=3] at tick 10 MATCHES.
Same done at tick 11 under [->3]: FAIL. That is the whole difference.

One caveat hides in the trailing gap: the [=3] extension threads consume !ack ticks. If a 4th ack fires before done, threads that extended past it die — the gap must be occurrence-free, not merely idle time.


Endpoint alignment: why goto is the default

Because [->N] pins its endpoint to the Nth occurrence, whatever follows it is timed relative to that occurrence — which is almost always what a protocol means: "one cycle after the 4th beat, the burst response appears". [=N] deliberately un-anchors the endpoint, which is only what you want when the follow-on event has no timing relationship to the last occurrence — "3 retries happen, and sometime later the link gives up". When in doubt, write goto; reach for [=N] only when you can articulate why the trailing gap is part of the spec.

systemverilog
// PROTOCOL TIMING: response 1 cycle after the 4th beat → goto
property p_burst_resp;
  @(posedge clk) burst_start |-> beat_valid [->4] ##1 resp_valid;
endproperty
a_burst: assert property (p_burst_resp);

// LOOSE COUPLING: after 3 nacks, link_down eventually (window) → [=N]
property p_retry_giveup;
  @(posedge clk) $rose(txn_start) |-> nack [=3] ##1 link_down;
endproperty
a_retry: assert property (p_retry_giveup);

// Nth-occurrence grab with goto + sampled value:
property p_fourth_beat_last;
  @(posedge clk) burst_start |-> beat_valid [->4] ##0 beat_last;
endproperty  // the 4th beat itself must carry beat_last (##0 fusion)
diagram
ENDPOINT ALIGNMENT — beat_valid[->4] ##0 beat_last

clk        :  1    2    3    4    5    6    7
beat_valid :  0    1    1    0    1    1    0
beat_last  :  0    0    0    0    0    1    0
                   b1   b2        b3   b4
                                       EM
[->4] endpoint = tick 6 (4th beat). ##0 fuses beat_last check
onto that very tick  beat_last=1 @6  MATCH.
With [=4] the endpoint could drift to tick 7+ where beat_valid=0,
and ##0 beat_last would test the WRONG cycle — a silent mis-check.

Interview angle and review

Interview angle

  • "Draw a waveform where [->3] ##1 done fails but [=3] ##1 done passes." — done two or more cycles after the 3rd occurrence. If you can produce this on a whiteboard you have answered the real question.

  • "Define [->N] in terms of primitive operators." — (!b[*0:$] ##1 b)[*N]; and [=N] is [->N] ##1 !b[*0:$]. Quoting the LRM expansions is a strong senior signal.

  • "When would you actually use [=N]?" — counting events whose follow-up is not cycle-anchored to the last event (retry exhaustion, error thresholds). Default to goto otherwise.

  • "What kills the trailing gap of [=N]?" — another occurrence of the counted boolean; the gap must be occurrence-free.

Key takeaways

  • [->N] and [=N] both count N scattered occurrences; they differ only in endpoint freedom.

  • [->N] ends exactly on the Nth occurrence — follow-on elements are timed off that event.

  • [=N] permits a trailing occurrence-free gap — the follow-on is decoupled from the Nth event's cycle.

  • Default to goto for protocol checks; use [=N] only when the trailing gap is genuinely part of the spec.

Common pitfalls

  • Using [=N] where the spec times the next event off the Nth occurrence — the checker silently accepts late responses.

  • Using [->N] for "N errors then eventually shutdown" — false failures when shutdown is not cycle-adjacent.

  • Forgetting [=N] threads die if the counted event fires again during the trailing gap.

  • Assuming [->N] requires consecutive occurrences — gaps between occurrences are fine; only the endpoint is pinned.

  • Standalone b[=N] as a full property — its drifting endpoint makes pass times meaningless; anchor it to something.