AMBA AHB · Module 6
Common Wait-State Bugs
A debugging catalogue of the recurring AHB wait-state bugs — HREADY stuck low, dropped hold, sampling during a wait, glitched address, off-by-one mux select, HREADYOUT/HREADY confusion — their waveform signatures, and the single root cause behind most of them.
This chapter closes Module 6 by turning the mechanism into a debugging skill. Chapters 6.1–6.6 built up how HREADY and wait states work correctly; this one catalogues what goes wrong — the recurring bugs, their waveform signatures, and how to find them. The payoff is a diagnostic mindset: when an AHB bus hangs, returns garbage, or hits the wrong subordinate, you should be able to name the likely wait-state bug and confirm it from the waveform. There is also a unifying insight: almost every wait-state bug traces to one root cause — something is not gated on HREADY — and many are latent on zero-wait subordinates, surfacing only once a subordinate inserts a wait. Recognizing that pattern is what makes these bugs findable.
1. What Is It?
The common wait-state bugs are a small set of recurring failures around HREADY and wait states. They fall into two families:
- Hung bus — the bus stops making progress. The dominant cause is HREADY stuck low: a subordinate never asserts its HREADYOUT high, so no transfer ever completes.
- Corrupted transfer — a transfer completes but with wrong behaviour. Causes include a dropped hold (the manager changes address/control/write data during a wait), sampling during a wait (capturing read data before it is valid), a glitched held address (a spurious wrong-subordinate access), an off-by-one mux select (the interconnect pairs the wrong subordinate's HREADYOUT with a transfer), and HREADYOUT/HREADY confusion (a subordinate times its response off the wrong signal).
These are the bugs every AHB engineer eventually meets. Knowing the catalogue — the symptom, the cause, the signature — turns a mysterious failure into a quick diagnosis. The rest of this chapter works through them and, crucially, the common thread that ties most of them together.
2. Why Does It Exist? (Why these bugs recur)
These bugs recur because the wait-state contract has several distinct obligations, each on a different participant, and missing any one produces a characteristic failure — and because the obligations are only exercised when a subordinate actually inserts a wait, so violations stay hidden in the common zero-wait case.
The wait-state mechanism distributes obligations: the subordinate must eventually assert HREADYOUT high (or the bus hangs); the manager must hold its outputs while HREADY is low (or the transfer corrupts); the manager must gate its capture on HREADY (or it samples garbage); the held address must be glitch-free (or it hits the wrong subordinate); the interconnect must mux on the registered data-phase select (or it pairs the wrong readiness). Each obligation is a place a design can go wrong, and each failure has its own signature. So the bugs recur because there are several independent obligations, each easy to miss in isolation. The catalogue is really a list of "what happens when each obligation is dropped."
The reason these bugs are insidious is that most are latent on zero-wait subordinates. If every subordinate completed in one cycle (HREADY always high), the hold obligations would never be tested — there is no wait cycle in which to drop a hold, sample early, or glitch the held address. So a manager with a dropped-hold bug works perfectly against fast SRAM and only fails when it meets a slow, wait-inserting flash. This is why these bugs survive into silicon: the common case hides them, and they surface only with the (rarer) wait-inserting subordinate. The bugs exist not because the contract is hard but because its violations are invisible until a wait occurs.
This is also why the bugs are worth cataloguing explicitly: their signatures are how you find them despite the latency. A hung bus screams "HREADY stuck low"; an address changing while HREADY is low is a dropped hold; a read returning garbage with a wait present is a sample-during-wait. Knowing the signatures lets you diagnose from a waveform even when the bug only manifests under specific subordinates. So the catalogue exists to convert "mysterious intermittent failure with slow peripherals" into "check these specific signatures." That diagnostic shortcut is the chapter's value.
3. Mental Model
Model wait-state debugging as a relay race where a dropped baton, a false start, or a runner who never finishes each leaves a distinct mark on the tape — and most mistakes only show when a runner actually slows down.
In a relay (the pipelined bus), runners (participants) must hand off cleanly (honor HREADY). The failures have signatures: a runner who never finishes their leg stalls the whole race (HREADY stuck low — hung bus); a runner who drops the baton mid-handoff corrupts the exchange (dropped hold — the address changes mid-wait); a false start grabs the baton before it arrives (sampling during a wait — capturing data before it's valid). And critically, many of these mistakes only show up when a runner actually slows down on their leg (a subordinate inserts a wait) — if everyone sprints through cleanly (zero-wait), the sloppy handoff technique never gets exposed. The race tape (the waveform) records each kind of mistake with its own mark.
This captures the chapter: distinct bugs leave distinct signatures (the marks on the tape), and most are exposed only by a slow leg (a wait-inserting subordinate). Debugging is reading the tape for the characteristic mark, knowing it tends to appear only when someone slowed down.
See the dropped-hold bug's signature directly:
The model's lesson: each wait-state bug leaves a characteristic signature, and most surface only when a subordinate slows down (inserts a wait). Debug by reading the waveform for the signature — here, an address changing while HREADY is low is the unmistakable mark of a dropped hold.
4. Real Hardware Perspective
In hardware, the unifying root cause of most wait-state bugs is concrete: a register or capture that should be gated by HREADY is not — so it advances, holds, or samples on the wrong cycles.
Walk the bugs to their hardware roots:
- HREADY stuck low (hung bus). The subordinate's HREADYOUT logic never reaches its "done" condition — a missing or incorrect done-signal, a combinational loop, or a state machine stuck in a wait state. Sometimes it is a default subordinate misconfiguration: an unmapped access selects a default subordinate that does not drive HREADYOUT high. The hardware fix is ensuring HREADYOUT always reaches high within bounded time.
- Dropped hold (corrupted transfer). The manager's address/control/HWDATA output registers are not gated on HREADY — they advance every cycle regardless, so they change during a wait. The fix is gating those registers' enables on HREADY (chapter 6.3).
- Sampling during a wait (garbage read). The manager's read-data capture register is not gated on HREADY — it loads HRDATA every cycle instead of only on the completing cycle, capturing invalid data during a wait. The fix is gating the capture on HREADY (chapter 6.3).
- Glitch on the held address. The held HADDR's output path has combinational logic (e.g., a multi-master mux) that glitches, momentarily reaching the decoder. The fix is a clean, glitch-free output path on the held address (chapter 6.4).
- Off-by-one mux select. The interconnect muxes HREADY (and HRDATA/HRESP) on the address-phase decode instead of a registered data-phase decode — pairing the wrong subordinate's readiness with the transfer. The fix is registering the decode for the data-phase mux (chapter 6.6).
- HREADYOUT/HREADY confusion. A subordinate times its response off its own HREADYOUT instead of the incoming HREADY, so it samples/responds on the wrong cycles. The fix is using the incoming HREADY for pipeline qualification (chapter 6.6).
The pattern is unmistakable: five of the six are "not gated on HREADY" (or the wrong HREADY) in some register or select. The held outputs aren't gated (dropped hold), the capture isn't gated (sampling), the mux select isn't the right (registered) one (off-by-one), the subordinate uses the wrong ready signal (confusion). Only "HREADY stuck low" is different — it is a subordinate that never completes. So the hardware debugging heuristic is powerful: when a wait-state bug appears, ask what is not correctly gated on HREADY? — that question localizes most of these bugs.
5. System Architecture Perspective
At the system level, these bugs matter because they are the failure modes that appear during integration — when fast and slow subordinates are mixed — and because some (the hung bus) can be catastrophic, motivating system-level safeguards.
The integration-time emergence is the key system insight. A subordinate or manager tested in isolation against zero-wait stimulus can pass while harboring a latent wait-state bug; the bug emerges only at integration, when the manager meets a real wait-inserting subordinate (a slow flash, a busy peripheral). So these bugs are classically integration bugs — they appear when components built separately are connected and the slow paths get exercised. This is why integration testing must include wait-inserting subordinates: a test suite that only uses zero-wait models will miss every latent hold bug. The architecture-level lesson is to verify against subordinates that insert waits, deliberately, to surface these bugs before silicon.
The hung bus is the most dangerous failure because it halts all forward progress — every master, every transfer, frozen. So systems often include safeguards against a permanently-low HREADY: a bus watchdog/timeout that detects a transfer taking too long and forces an error or reset (related to the default-subordinate and error mechanisms of chapter 7). The default subordinate itself is a safeguard: it ensures unmapped accesses complete (with an error) rather than hanging on a missing subordinate. So at the system level, the hung-bus failure mode justifies architectural defenses — timeouts and well-behaved default subordinates — that bound how long HREADY can stay low. A robust system never lets a single subordinate hang the bus indefinitely.
For verification, these bugs define a core set of AHB protocol assertions (foreshadowing later modules): HREADY must eventually go high (no indefinite stall), address/control stable while HREADY low (no dropped hold), read data sampled only when HREADY high (no early sample), HSEL/response consistency across the address-data pipeline (no off-by-one). So the bug catalogue is essentially the assertion checklist inverted — each bug is a property a checker guards. A monitor that encodes these assertions catches the bugs automatically in simulation, including the latent ones (because the assertions fire the moment a wait occurs and an obligation is violated). So the system-level answer to these bugs is assert the contract — the catalogue tells you exactly which assertions to write.
6. Engineering Tradeoffs
The bug catalogue reflects the cost side of AHB's wait-state design.
- Distributed obligations vs centralized. AHB spreads wait-state obligations across manager, subordinate, and interconnect — each simple locally, but each a place to introduce a bug. A more centralized design might reduce the number of obligation-holders but would be more complex. AHB accepts the distributed obligations (and thus the distributed bugs), managed by clear contracts and assertions.
- Latent-until-wait vs always-exercised. Because the obligations are only exercised under waits, the common (zero-wait) case is simple and fast — but bugs hide until a wait occurs. An always-exercised design would surface bugs earlier but lose the zero-wait simplicity. AHB keeps the simple common case and pays with latent bugs, mitigated by deliberately testing with wait-inserting subordinates.
- Trusting well-behaved subordinates vs enforcing timeouts everywhere. AHB assumes subordinates eventually complete (no built-in per-transfer timeout), which keeps the protocol simple but allows a buggy subordinate to hang the bus. Systems add timeouts as a safeguard. The tradeoff is protocol simplicity vs built-in hang protection; AHB chooses simplicity and leaves hang protection to the system.
- Signature-based debugging vs formal-only. The bugs have clear waveform signatures, making them debuggable by inspection — but relying on inspection is error-prone at scale, so assertions formalize them. The practical approach combines both: signatures for quick diagnosis, assertions for systematic coverage.
The throughline: these bugs are the cost of AHB's distributed, latent-until-wait obligation model. The benefits (simple local contracts, fast zero-wait common case) come with the cost (several places to err, bugs hidden until a wait). The mitigations are the chapter's practical content: know the signatures, test with wait-inserting subordinates, assert the contract, and add system-level timeouts against the catastrophic hung bus.
7. Industry Example
Trace a real integration-debugging session through the catalogue.
A team integrates a custom DMA manager with a subsystem containing SRAM (zero-wait) and a slow flash (multi-wait). The DMA passed all its unit tests against zero-wait memory models.
- Symptom: corrupted flash writes. DMA writes to SRAM are perfect, but writes to flash store wrong data. SRAM is zero-wait; flash inserts waits. The "works on fast, fails on slow" pattern immediately suggests a latent wait-state bug exposed by the wait.
- Diagnosis from the waveform. The team captures a flash write and sees the signature: HWDATA changes during the wait (HREADY low). That is the dropped-hold signature (Figure 2). Root cause: the DMA's write-data register is not gated on HREADY — it advances to the next beat's data every cycle, so during flash's wait it overwrites the data flash hasn't yet accepted. The fix: gate the write-data register on HREADY (chapter 6.3). SRAM never exposed it because zero-wait flash... er, zero-wait SRAM accepted the data in the first cycle, before any change.
- A second symptom: garbage flash reads. After fixing writes, flash reads return garbage. The waveform shows the DMA capturing HRDATA during the wait (HREADY low), before flash drives valid data. That is the sample-during-wait signature. Root cause: the read-capture register isn't gated on HREADY. Fix: gate the capture on HREADY high.
- A third symptom (interconnect): wrong-subordinate pacing. On a different integration, a transfer to flash is paced by the SRAM's HREADYOUT. The waveform shows HREADY following the wrong subordinate. Root cause: the interconnect muxes HREADY on the address-phase select instead of the registered data-phase select — the off-by-one bug (chapter 6.6). Fix: register the decode for the data-phase mux.
- A hung-bus scare. A test with an unmapped address hangs the bus — HREADY stuck low forever. Root cause: the default subordinate wasn't driving HREADYOUT high on the unmapped access. Fix: ensure the default subordinate completes (with an error) so HREADY is always eventually high (chapters 3.10, 7).
- The systemic fix. The team adds wait-inserting subordinate models to the regression suite and a set of protocol assertions (HREADY stable-hold, sample-on-ready, eventual-completion). The latent bugs become caught-in-simulation bugs, and future managers are verified against waits from the start.
The session shows the catalogue in practice: each symptom mapped to a signature, each signature to a root cause (almost all "not gated on HREADY"), and the systemic fix (test with waits, assert the contract). The "works on fast, fails on slow" pattern was the tell that a latent wait-state bug was being exposed — exactly the insight this chapter builds.
8. Common Mistakes
9. Interview Insight
Wait-state debugging is a favorite interview area — it tests whether you can reason from symptom to root cause, not just recite the protocol.
The answer that lands organizes the bugs and names the root cause: "Wait-state bugs come in two families. One is a hung bus — HREADY stuck low because a subordinate never asserts HREADYOUT high, from a missing done-condition or a bad default subordinate. The other is a corrupted transfer — the manager drops its hold (changes address or data during the wait), samples read data during the wait before it's valid, or glitches the held address into the wrong subordinate; or the interconnect muxes HREADY on the wrong (address-phase, not registered) select. The unifying root cause for most is 'something isn't gated on HREADY.' And the reason they're nasty is that they're latent on zero-wait subordinates — they only show up once a subordinate inserts a wait, so they emerge at integration. The fixes are: gate everything on HREADY, test with wait-inserting models, and assert the contract." The two-families organization, the root-cause unification, and the latent-until-wait insight are the senior signals.
10. Practice Challenge
Reason from symptom to root cause.
- Name the families. State the two families of wait-state bugs and a representative bug in each.
- Read the signature. From Figure 2, identify the dropped-hold signature and explain its effect.
- Apply the heuristic. A flash read returns garbage and the waveform shows a capture during a wait. Name the bug and the fix.
- Explain the latency. Why does a dropped-hold bug pass zero-wait tests but fail against slow subordinates?
- Prescribe prevention. List three systemic practices that prevent or catch this whole class of bugs.
11. Key Takeaways
- Wait-state bugs fall into two families: a hung bus (HREADY stuck low — a subordinate never completes) and a corrupted transfer (dropped hold, sampling during a wait, glitched address, off-by-one select, HREADYOUT/HREADY confusion).
- The unifying root cause of most is "something is not gated on HREADY" — an output not held, a capture not gated, a mux select not registered to the data phase, or the wrong ready signal used.
- The one exception is the hung bus — a completion failure (no done-condition / bad default subordinate), guarded by a bus timeout.
- Most bugs are latent on zero-wait subordinates and surface only when a subordinate inserts a wait — the classic "works on fast, fails on slow" integration signature.
- Each bug has a distinctive waveform signature — diagnose by reading the waveform for it (e.g., an address changing while HREADY is low = dropped hold).
- Prevent the whole class by testing with wait-inserting subordinate models, gating everything on HREADY, asserting the contract, and adding a system-level bus timeout.
12. What Comes Next
This completes Module 6 — HREADY and Wait States. You now understand HREADY's meaning, how subordinates insert wait states, how managers hold through them, the address/control and data-phase mechanics, the HREADY/HREADYOUT sourcing, and the bugs that arise. The next module turns to what a subordinate signals besides readiness:
- Module 7 — Responses (coming next) — the HRESP response (OKAY, ERROR, and the legacy RETRY/SPLIT), the two-cycle error handshake, and how the manager reacts.
To revisit this module, see What HREADY Means, Slave-Inserted Wait States, Master Behavior During Wait, Holding Address & Control, Data Phase Extension, and HREADY vs HREADYOUT. For the default subordinate that prevents unmapped-access hangs, see HSEL. For the broader protocol map, see the AMBA family overview.