Skip to content

AMBA AXI · Module 3

Common Handshake Bugs

A field guide to the recurring AXI handshake bugs — dropped VALID, mutated payload, VALID-waits-for-READY deadlock, ordering violations, and undrained responses — and how to spot each.

Every rule in this module has a failure mode, and the same handful of bugs show up on real AXI bring-ups again and again. This chapter is the field guide: the recurring handshake mistakes, what each looks like on a waveform, and the one-line guardrail that prevents it. They cluster into four families — stability, dependency, ordering, and drain — and once you can name the family from the signature on a capture, diagnosis is fast. This closes Module 3 by turning the contract (3.1), the transfer event (3.2), backpressure (3.3), throughput (3.4), and the dependency rules (3.5) into a debugging checklist you'll use on every AXI block.

1. Four Families of Handshake Bug

Almost every handshake bug is a violation of one rule from this module, so they sort into four families:

Four families of AXI handshake bug: stability (payload or VALID changes before transfer), dependency (VALID waits for READY, deadlock), ordering (response before data), and drain (READY never asserted to consume responses).StabilityVALID dropped or payload mutatedbefore transferDependencyVALID waits for READY → deadlockOrderingB before W, or R before ARDrainREADY never asserted — responsespile up12
Figure 1 — the four families of handshake bug, each breaking one rule from this module. Stability bugs break 'hold VALID + payload until transfer'. Dependency bugs break 'VALID never waits for READY' (deadlock). Ordering bugs break 'B after W / R after AR'. Drain bugs break 'eventually assert READY' (responses never consumed). Name the family from the signature, and the fix follows.

2. Stability Bugs

These break the stability rule (3.1): once VALID is asserted, the source must hold VALID and the payload stable until the transfer completes. Two variants dominate.

Dropped VALID — the source asserts VALID with a beat, the destination isn't ready yet (READY low), and the source illegally withdraws VALID before the transfer. The beat is silently lost.

Dropped VALID — a lost beat

8 cycles
The source asserts VALID with D0 while READY is low, then illegally drops VALID at cycle 3 just as READY rises, so no transfer occurs and D0 is lost.should have HELD VALID hereVALID + D0 asserted, READY lowVALID + D0 asserted, R…BUG: VALID dropped as READY rises → D0 lostBUG: VALID dropped as …aclkvalidreadydataXD0D0XXXXXt0t1t2t3t4t5t6t7
Figure 2 — the dropped-VALID bug. The source asserts VALID with D0 at cycle 1, but READY is low. Instead of holding (stability rule), the source drops VALID at cycle 3 — exactly when READY finally rises. At the cycle-3 edge VALID is low, so no transfer happens and D0 is lost forever. The source should have held VALID and D0 until the transfer.

Mutated payload — the source holds VALID but changes the data while waiting. The destination captures whatever is present at the transfer edge, so it gets the wrong beat.

Mutated payload under held VALID — a corrupt beat

8 cycles
VALID is held high from cycle 1 to 3, but the data changes from D0 to D7 at cycle 2; the transfer at cycle 3 captures D7 instead of the originally-offered D0.payload must stay stable hereVALID + D0VALID + D0BUG: data changed to D7 while VALID heldBUG: data changed to D…transfer captures D7 — corrupt beattransfer captures D7 —…aclkvalidreadydataXD0D7D7XXXXt0t1t2t3t4t5t6t7
Figure 3 — the mutated-payload bug. VALID is correctly held high cycles 1–3, but the data illegally changes from D0 to D7 at cycle 2 while VALID is asserted. The transfer happens at cycle 3 (READY high), capturing D7 — not the D0 the source originally offered. Payload must stay stable from VALID-assert until transfer.

3. Dependency Bugs

These break the cardinal rule (3.1/3.5): a VALID must never wait for its READY. The canonical mistake is coding valid = f(ready):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ❌ Dependency bug — VALID waits for READY. If the slave (legally) waits for
//    VALID before asserting READY, both stay low forever → DEADLOCK.
assign awvalid = awready;          // never rises
 
// ✅ Fix — assert VALID from your own data; hold until the transfer.
assign awvalid = have_addr;        // independent of READY

A related, subtler form is a combinational loop: VALID is wired combinationally from READY and the destination wires READY combinationally from VALID, creating a zero-delay cycle (a simulation X/oscillation or an unsynthesizable loop). The signature on a capture is the deadlock from 3.5 — both control signals flat-low forever, no forward progress. The fix is always the same: drive VALID from internal state, and if a registered path is needed for timing, use a skid buffer (3.4), not a combinational dependency.

4. Ordering & Drain Bugs

Ordering bugs break the mandatory orderings (3.5). The dangerous one is B before W: a slave asserts BVALID before it has accepted the write data (WLAST). The master sees OKAY and believes the write landed — but the data was never taken, so memory is silently wrong. The read analogue is RVALID before the AR handshake. These don't hang the bus; they corrupt correctness while looking healthy.

Drain bugs break liveness on the response/data side: a master that never asserts BREADY (or RREADY). The slave correctly holds BVALID high until accepted, but it's never accepted, so the response channel jams and subsequent transactions back up behind it. The signature is a VALID (often bvalid/rvalid) high for a long time with its READY low — and unlike legitimate backpressure, it never clears because the master simply isn't draining. Many simple masters tie BREADY/RREADY high precisely to avoid this.

5. The Handshake Bug Catalogue

AXI handshake bugs — signature, cause, fix, and guardrail

1. Dropped VALID — a lost beat

Symptom: A beat goes missing; on the capture, VALID was high while READY was low, then went low before a transfer. The destination never saw it.

Cause: The source withdrew VALID (or moved on) during backpressure, violating the stability rule.

Fix: Once asserted, hold VALID and the payload until VALID && READY. Drive VALID from a registered "I have a beat" state, cleared only on transfer.

Guardrail: An assertion — while VALID && !READY, VALID must stay high next cycle.

2. Mutated payload — a corrupt beat

Symptom: Data is wrong despite a clean-looking transfer; the payload changed while VALID was held.

Cause: The source updated the payload mid-offer; the destination captured the value present at the transfer edge.

Fix: Latch the payload when you assert VALID and hold it stable until transfer.

Guardrail: An assertion — while VALID && !READY, the payload must not change.

3. VALID waits for READY — deadlock

Symptom: The channel is hung; VALID and READY are both low forever; no transaction completes.

Cause: VALID was coded as a function of READY (e.g. awvalid = awready); the slave waits for VALID, so the cycle never breaks.

Fix: Assert VALID from internal data readiness only — never reference READY in the VALID equation.

Guardrail: Review every VALID assignment; if its right-hand side mentions the matching READY, it's wrong (AXI4 §A3.3).

4. B before W (or R before AR) — silent corruption

Symptom: A write reports OKAY but memory is wrong; intermittent, surfaces far downstream. BVALID rose before WLAST was accepted.

Cause: The slave asserted the response before taking the write data, violating the B-after-W ordering.

Fix: Gate BVALID behind both address and last-data acceptance (aw_done && w_done); gate RVALID behind the AR handshake.

Guardrail: Assertions — BVALID must not assert before WVALID && WREADY && WLAST; RVALID not before ARVALID && ARREADY.

5. Master never drains B/R — channel jams

Symptom: BVALID (or RVALID) high for a very long time with READY low; transactions back up; throughput collapses then stalls.

Cause: The master never asserts BREADY/RREADY, so responses are never consumed and the channel can't make progress.

Fix: Ensure the master always (eventually) asserts BREADY/RREADY; simple masters tie them high.

Guardrail: A liveness/timeout assertion — an asserted BVALID/RVALID must be accepted within N cycles.

6. Naive registered handshake — half throughput

Symptom: Correct data, but the channel runs at ~50% — a beat every other cycle, a clean bubble pattern.

Cause: A single-register slice drops READY for a cycle to drain after each transfer (Chapter 3.4).

Fix: Replace with a skid buffer (2-deep register slice) that registers the path for timing without inserting bubbles.

Guardrail: A performance check on sustained utilization; investigate any channel stuck near 0.5.

6. Triage by Signature

The fastest way to use this guide is backwards — from the signature on a capture to the bug:

Triage: both signals low forever maps to deadlock; VALID dropped before transfer maps to lost beat; payload changing under VALID maps to corrupt beat; response before data maps to ordering bug; VALID high with READY never asserted maps to a drain bug.Both VALID &READY lowforeverDependency /deadlock (VALIDwaits for READY)VALID droppedbefore transferStability: lostbeatPayload changesunder held VALIDStability:corrupt beatResponse beforeits dataOrderingviolation
Figure 4 — triage by waveform signature. Both control signals low forever → a dependency/deadlock bug. VALID high then dropped before transfer → dropped-VALID. Payload changing under held VALID → mutated-payload. Response asserted before its data → ordering. VALID high with READY low and never clearing → an undrained channel. Read the signature, name the family, apply the fix.

7. Debugging Insight

8. Verification Insight

9. Interview Questions

10. Summary

The recurring AXI handshake bugs sort into four families, each breaking one rule from this module. Stability bugs — dropped VALID (a lost beat) and mutated payload (a corrupt beat) — break "hold VALID and payload until transfer." Dependency bugs — VALID waits for READY, including combinational loops — break "VALID never waits for READY," with the signature of both signals flat-low forever (deadlock). Ordering bugs — B before W, R before AR — break the mandatory orderings and silently corrupt data while looking healthy. Drain bugs — a master that never asserts BREADY/RREADY — jam the response channel and back up transactions. (The ~50% naive registered handshake from 3.4 rounds out the catalogue.)

Diagnose by signature: both low forever → dependency; VALID dropped → lost beat; payload changes under VALID → corrupt beat; response before data → ordering; VALID high with READY never asserted → drain. Each family maps to a reusable assertion (stability, structural-no-VALID-from-READY plus timeout, ordering, liveness) and is only exposed by stressful stimulus — random per-channel backpressure with many outstanding transactions. Symptom → family → rule → fix is the whole method. That completes Module 3: you now own the handshake end-to-end — contract, timing, flow control, throughput, deadlock rules, and the bugs. Module 4 returns to the write path to detail its channels signal by signal.

11. What Comes Next

That closes Module 3 — the VALID/READY Handshake. Module 4 details the write transaction channel by channel, now that the handshake underneath them is fully understood:

Previous: 3.5 — Handshake Dependency & Deadlock Rules. For the broader protocol catalog, see the AMBA family overview doc.