Part 4 · Assertions (SVA) · Intermediate
Advanced SVA: Local Variables, Multiclock & More
Hub — sequence local variables, multi-clock properties, expect, the checker construct, and assertion control system tasks.
Beyond single-clock pattern matching
Everything so far assumed one clock, fixed latencies, and checks that only observe signals. Real designs break all three assumptions: a pipeline returns data with a tag after a variable number of cycles, a handshake crosses from a 200 MHz domain to a 100 MHz domain, and a senior DV engineer wants one reusable checker dropped onto twenty FIFO instances. This topic covers the SVA machinery for each of those: local variables carry data through a sequence, multiclocked sequences stitch domains together, expect lets procedural testbench code block on a property, checker packages assertions plus modeling code into a reusable unit, and the assertion control system tasks silence the whole suite during reset.
These are the constructs that separate "I can write req |-> ##[1:3] ack" candidates from "I have verified a real pipeline" candidates. Interviewers reach for local variables in particular — the data-integrity question ("check that the data out of this FIFO matches what went in") is unanswerable without them.
Sub-topics
Local Variables in Sequences — per-thread data capture, the canonical tag/data integrity check, FIFO data checking.
Multi-Clock Assertions — explicit clocking events per subsequence, clock-crossing ## semantics, CDC handshake checks.
expect & Procedural Concurrent Assertions — blocking property waits in tasks, assertions inside always blocks.
The checker Construct — encapsulating assertions plus modeling code, free variables, bind-friendly reuse.
Assertion Control & System Functions — $assertoff/$asserton/$assertkill, $assertcontrol, assertion coverage, $global_clock.
Legend: [DATA] [CLOCK] [TB] [REUSE] [CTRL]
┌────────────────────────────────────────────────────────────────────────┐
│ ADVANCED SVA — topic map │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. LOCAL VARIABLES [DATA] │
│ (valid, x = tag) ##[1:20] (done && rtag == x) │
│ per-thread copies │ capture at request │ compare at response │
│ │
│ 2. MULTICLOCK [CLOCK] │
│ @(posedge clkA) req ##1 @(posedge clkB) ack │
│ clock-change ## rules │ CDC handshakes │ when NOT to cross │
│ │
│ 3. expect / PROCEDURAL [TB] │
│ task body: expect (@(posedge clk) gnt ##1 done) ... │
│ blocking wait on a property │ assert property inside always │
│ │
│ 4. checker CONSTRUCT [REUSE] │
│ checker fifo_chk(...) — assertions + aux modeling + free vars │
│ instantiate or bind │ library-of-checkers pattern │
│ │
│ 5. ASSERTION CONTROL [CTRL] │
│ $assertoff at reset │ $asserton after init │ $assertkill │
│ $assertcontrol levels │ cover-of-assertions │ $global_clock │
│ │
└────────────────────────────────────────────────────────────────────────┘Key takeaways
Local variables are the only way to check data integrity across variable latency — $past needs a fixed delay.
Multiclocked sequences glue subsequences with ##1/##0 at clock changes; most sequence operators are single-clock only.
expect is a blocking procedural wait on a property — testbench code, not a checker.
checker packages assertions, modeling code, and default clocking into one bindable, reusable unit.
$assertoff/$asserton around reset is standard methodology — assertions on X-state logic produce noise, not information.