AMBA AXI · Module 16
AXI Assertions (SVA)
Write the core AXI protocol assertions in SystemVerilog Assertions — the handshake rules (VALID stable until READY, VALID not waiting on READY), payload stability ($stable while VALID held), and structural/encoding checks — with the concurrent-assertion idioms, disable-on-reset, and binding pattern that make them reusable across any AXI interface.
The protocol-checker mindset (16.1) produced a categorised rule catalog; this chapter turns the per-cycle rules into executable SystemVerilog Assertions (SVA). We'll write the core checks for the two categories that are naturally cycle-by-cycle — handshake and payload stability — plus the structural/encoding checks, using the concurrent-assertion idioms (property, |->, $stable, $rose, disable iff) that every AXI assertion set relies on. (The ordering/transaction rules that span beats are better handled by monitors and scoreboards in 16.3–16.4.) The goal is a set of assertions you can bind onto any AXI interface and trust — the executable form of the rule catalog, and the engine inside every protocol VIP.
1. The Anatomy of an AXI Assertion
A concurrent SVA assertion has a fixed shape: a clocking event, a disable condition (almost always reset), an antecedent (the trigger), and a consequent (what must then hold). Nearly every AXI rule fits the pattern "when this is observed, that must be true on the relevant cycles."
// Template: every AXI assertion shares this skeleton
property p_example;
@(posedge aclk) disable iff (!aresetn)
antecedent |-> consequent; // |-> same cycle, |=> next cycle
endproperty
a_example: assert property (p_example)
else $error("AXI violation: <rule> at %0t", $time);The disable iff (!aresetn) is essential: during reset the bus is not expected to be compliant, so every AXI assertion is disabled while reset is asserted. The |-> (overlapping) and |=> (non-overlapping) implication operators express "in the same cycle" vs. "starting next cycle" — the choice matters for getting the timing of a rule exactly right.
2. Handshake Assertions
The handshake category produces three canonical checks per channel. Shown for the write-address (AW) channel; the same three apply to W, B, AR, R by signal substitution.
// (H1) VALID must remain asserted, with no change, until READY accepts it.
property p_awvalid_stable;
@(posedge aclk) disable iff (!aresetn)
(awvalid && !awready) |=> awvalid;
endproperty
a_awvalid_stable: assert property (p_awvalid_stable)
else $error("AWVALID deasserted before AWREADY");
// (H2) Once asserted, VALID is held — equivalently it may only fall the cycle
// after a completed handshake (this is the dual of H1).
// (H3) VALID must not depend combinationally on READY (no deadlock).
// Checked structurally / formally: prove awvalid has no comb. path from
// awready. In simulation, a liveness check guards against the hang:
property p_aw_progress; // an asserted request is eventually accepted
@(posedge aclk) disable iff (!aresetn)
awvalid |-> s_eventually awready;
endproperty
a_aw_progress: assert property (p_aw_progress)
else $error("AWVALID never accepted — possible deadlock");H1 (stability of VALID) is the everyday workhorse; the liveness check (H3) guards the deadlock rule that 16.1 flagged as fatal. Note H3's combinational-independence is ideally proven structurally/formally (no logic path awready → awvalid), with the liveness assertion as the simulation backstop.
3. Payload-Stability Assertions
The stability category checks that every payload field stays constant while its VALID is held and not yet accepted — the $stable built-in is exactly the tool. One assertion per payload field per channel (often grouped):
// (S1) All AW payload fields stable while AWVALID held and not yet accepted.
property p_aw_payload_stable;
@(posedge aclk) disable iff (!aresetn)
(awvalid && !awready) |=>
$stable(awaddr) && $stable(awlen) && $stable(awsize) &&
$stable(awburst)&& $stable(awid) && $stable(awprot) &&
$stable(awcache)&& $stable(awlock);
endproperty
a_aw_payload_stable: assert property (p_aw_payload_stable)
else $error("AW payload changed while AWVALID held");
// (S2) W payload stable while WVALID held.
property p_w_payload_stable;
@(posedge aclk) disable iff (!aresetn)
(wvalid && !wready) |=> $stable(wdata) && $stable(wstrb) && $stable(wlast);
endproperty
a_w_payload_stable: assert property (p_w_payload_stable)
else $error("W payload changed while WVALID held");
// (S3) Read-data payload stable while RVALID held.
property p_r_payload_stable;
@(posedge aclk) disable iff (!aresetn)
(rvalid && !rready) |=> $stable(rdata) && $stable(rresp) &&
$stable(rlast) && $stable(rid);
endproperty
a_r_payload_stable: assert property (p_r_payload_stable)
else $error("R payload changed while RVALID held");$stable(x) is true when x has the same value as the previous clock — so "VALID held last cycle ⇒ payload unchanged this cycle" exactly encodes the stability rule. The |=> (next-cycle) form is correct here because $stable compares against the prior sample.
4. Structural Assertions and Reset Behavior
The structural/encoding category checks legal field values and combinations — immediate, single-cycle conditions on the payload (only meaningful when VALID). Plus a few reset-behavior checks that do apply during/after reset:
// (E1) Burst type is never the reserved encoding (2'b11).
property p_awburst_legal;
@(posedge aclk) disable iff (!aresetn)
awvalid |-> (awburst != 2'b11);
endproperty
a_awburst_legal: assert property (p_awburst_legal)
else $error("Reserved AWBURST encoding");
// (E2) Transfer size never exceeds the data bus width.
property p_awsize_legal;
@(posedge aclk) disable iff (!aresetn)
awvalid |-> ((1 << awsize) <= (DATA_W/8));
endproperty
a_awsize_legal: assert property (p_awsize_legal)
else $error("AWSIZE exceeds bus width");
// (E3) WRAP bursts have a legal length (2, 4, 8, or 16 beats).
property p_wrap_len_legal;
@(posedge aclk) disable iff (!aresetn)
(awvalid && awburst == 2'b10) |-> (awlen inside {1, 3, 7, 15});
endproperty
a_wrap_len_legal: assert property (p_wrap_len_legal)
else $error("Illegal WRAP burst length");
// (R1) RESET behavior: all VALIDs must be low during reset.
property p_reset_valids_low;
@(posedge aclk) (!aresetn) |-> (!awvalid && !wvalid && !arvalid
&& !bvalid && !rvalid);
endproperty
a_reset_valids_low: assert property (p_reset_valids_low)
else $error("A VALID was high during reset");Note R1 is not disabled on reset — it specifically checks the reset behavior (all VALIDs low), so it must be active exactly when reset is asserted.
When a structural or handshake assertion fires, the failure is pinned to a cycle and a signal, as the waveform shows:
Payload-stability assertion firing
7 cyclesThe whole set is deployed and trusted via one pattern: write the assertions once in a checker module, bind it to every AXI interface, then prove the set is sound (inject violations → each fires) and non-vacuous (antecedent coverage):
5. Common Misconceptions
6. Debugging Insight
7. Verification Insight
8. Interview Questions
9. Summary
AXI assertions encode the per-cycle rules of the catalog as SystemVerilog Assertions. Every concurrent assertion shares one skeleton — @(posedge aclk) disable iff (!aresetn) antecedent |-> / |=> consequent — where disable iff (!aresetn) suppresses checks during reset and the implication operator selects same-cycle (|->) vs. next-cycle (|=>) timing. The handshake category yields, per channel, the VALID-held-until-READY stability check (the workhorse), its deassert-timing dual, and a liveness backstop for the deadlock rule (with combinational independence proven formally). The payload-stability category uses (valid && !ready) |=> $stable(field) for every payload field — $stable comparing to the prior sample is the exact tool. The structural/encoding category uses same-cycle |-> checks gated on VALID for legal BURST/SIZE/WRAP-length and reserved encodings, plus a reset-behavior check (all VALIDs low) that deliberately stays active during reset.
The cross-beat ordering/transaction rules are not forced into per-cycle SVA — they're delegated to monitors and scoreboards (16.3–16.4) that hold transaction state. Assertions are deployed as a bound checker module (bind) so they attach to any interface without editing RTL and are reusable as VIP. Crucially, the assertion set must itself be verified — injected violations confirm each fires (soundness), antecedent coverage confirms each is exercised (non-vacuity), the catalog mapping confirms completeness, and formal proves the structural/liveness subset. Next, we build the passive monitor that reconstructs transactions so the stateful ordering rules can be checked.
10. What Comes Next
You can now check the signal-local rules; next we reconstruct transactions to check the stateful ones:
- 16.3 — AXI Monitors (coming next) — a passive monitor that observes the bus and reconstructs complete transactions, the bridge from per-cycle assertions to transaction-level checking.
Previous: 16.1 — The Protocol-Checker Mindset. Related: 3.5 — Handshake Dependency & Deadlock Rules for the handshake rules these assertions encode, 7.4 — WRAP Bursts for the legal WRAP lengths, and 6.8 — RRESP, BRESP & RLAST for the response/last rules.