Skip to content

AMBA AHB · Module 18

Stuck HREADY

Diagnosing a bus hung because HREADY never goes high — the symptom (HREADY low for many cycles, master frozen on the same address, no further activity), the diagnosis path (the shared HREADY is the addressed slave's HREADYOUT routed back, so decode HADDR, go to that slave, and find why its HREADYOUT is stuck low), and the common root causes (a wait-state FSM that never reaches done, a resource/downstream bus that never responds, a completion condition gated by something permanently false, or an interconnect HREADY-aggregation routing bug) with their fixes. Never force HREADY high (masks the bug with corruption); a bus watchdog catches it in the field.

Module 17 verified designs; Module 18 debugs the failures that escape — starting with the most catastrophic: a stuck HREADY. When the shared HREADY never goes high, the current transfer never completes, the master holds forever, and the entire bus hangs. It's the bus-level equivalent of a system freeze — nothing moves. This chapter teaches how to diagnose it from the waveform. The symptom is unmistakable: HREADY low for many cycles with no rising edge, the master frozen on the same HADDR/HTRANS, and no further bus activity. The diagnosis path follows the signal structure: the shared HREADY is the addressed slave's HREADYOUT routed back by the interconnect (chapter 6.6), so you (1) decode HADDR to find which slave is addressed, (2) look at that slave's HREADYOUT, and (3) find why it's stuck low. The common root causes: a wait-state FSM that never reaches done (a counter that never expires, a condition never true), a resource the slave waits on that never responds (a memory or downstream bus itself stuck), a completion condition gated by something permanently false, or an interconnect HREADY-aggregation bug routing the wrong slave's ready. The fix follows the cause. And a bus watchdog timer catches this in the field by flagging HREADY low beyond a threshold. This chapter builds the diagnosis.

1. What Is It?

Stuck HREADY is a bus hang where HREADY never rises; debugging it means tracing from the symptom to the addressed slave's HREADYOUT and its root cause. Its parts:

  • The symptomHREADY low for many cycles (no rising edge), the master frozen on the same address, no further activity → the bus hangs.
  • The diagnosis path — the shared HREADY is the addressed slave's HREADYOUT, so decode HADDR, go to that slave, find why its HREADYOUT is stuck low.
  • The root causes — wait-FSM never done, resource never responds, wrong completion condition, or interconnect aggregation routing bug.
  • The fix + guard — fix per the cause; add a bus watchdog (flag HREADY low beyond a threshold) for the field.
The stuck-HREADY symptom, the diagnosis path, and the four common root causes with their fixes.
Figure 1 — diagnosing a stuck HREADY. Symptom: HREADY low for many cycles with no rising edge, the master holding the same HADDR/HTRANS, no further activity → the bus hangs. Diagnosis path: (1) decode HADDR to find which slave is addressed; (2) look at THAT slave's HREADYOUT (the shared HREADY is it, routed back); (3) find why HREADYOUT is stuck low. Common root causes → fix: wait FSM never done (counter never expires / condition never true → fix the FSM); resource never responds (memory/downstream bus stuck → fix the resource); wrong completion condition (HREADYOUT gated by something always false → fix the condition); HREADY aggregation (interconnect routes the wrong slave's ready → fix the routing). A bus watchdog flags HREADY low beyond a threshold.

So stuck HREADY is the canonical bus hang — and debugging it is the canonical AHB debug: a systematic trace from a symptom (the bus is frozen) through the signal structure (shared HREADY → addressed slave's HREADYOUT) to a root cause (why that HREADYOUT won't rise) and a fix. The key insight — and the reason the diagnosis is directed rather than random — is the signal relationship: the shared HREADY (which everyone sees) is not an independent signal; it's the addressed slave's HREADYOUT (16.4) selected and routed back by the interconnect (6.6). So when HREADY is stuck, the culprit is almost always the addressed slave's HREADYOUT (or, less often, the interconnect's routing of it). So you don't search randomly — you decode the address, go to that slave, and examine its HREADYOUT logic. From there, the root cause is one of a small set (FSM, resource, condition, routing), each with a known fix. So stuck HREADY debugging is the systematic trace from the hang to the addressed slave's HREADYOUT. So it's the canonical AHB debug.

2. Why Does It Exist?

Stuck HREADY exists as a failure mode because HREADY is the bus's single, shared, blocking flow-control signal — so any logic that drives it low and fails to raise it hangs the entire bus; and it's a catastrophic, high-priority bug because one slave's local failure becomes a global bus freeze.

The HREADY is shared and blocking is the root: HREADY is the one signal that paces the whole busevery transfer waits for it. It's blocking: while it's low, the master cannot proceed. And it's shared: it's the addressed slave's HREADYOUT broadcast to everyone. So if HREADY stays low, the current transfer (and thus the master, and thus everything) is blocked indefinitely. So a stuck HREADY is uniquely catastrophic — it halts the entire bus. So this failure mode exists because HREADY is the single shared blocking signal. So it's a single point of failure. So the structure makes it catastrophic.

The local failure → global hang drives the priority: the cause is usually local (one slave's HREADYOUT logic, or the interconnect's routing of it) — but the effect is global (the whole bus hangs). So a small, local bug has a huge, system-wide consequence. This asymmetry (local cause, global effect) makes stuck HREADY a high-priority bug — and makes it easy to misdiagnose (the symptom is everywhere — the bus is frozen — but the cause is in one place). So debugging it exists as a skill because you must trace the global symptom back to the local cause. So it's local-cause/global-effect tracing. So the asymmetry demands directed debug.

The the trace is directed by the signal structure is the method's basis: the reason you can debug this systematically (not randomly) is the signal structure — the shared HREADY is definitionally the addressed slave's HREADYOUT. So the global symptom (HREADY stuck) has a defined trace to a local cause (that slave's HREADYOUT). So the debug method exists because the structure provides a directed path. So it's structure-guided. So stuck HREADY (and its debug) exists because: HREADY is the single, shared, blocking flow-control signal (so a stuck one halts the entire bus — the catastrophe); the cause is local but the effect is global (a high-priority, easy-to-misdiagnose asymmetry — the priority); and the signal structure (shared HREADY = the addressed slave's HREADYOUT) provides a directed trace from the global symptom to the local cause (the method). So stuck-HREADY debugging is the directed trace from the catastrophic global bus hang back to the local HREADYOUT (or routing) cause — the canonical, highest-stakes AHB debug. So this chapter teaches the directed trace. So trace the hang to the addressed slave's HREADYOUT.

3. Mental Model

Model debugging a stuck HREADY as finding why a whole assembly line has stopped — and tracing it to the one station that raised the "stop" signal and never cleared it. The line halts entirely (the symptom), but the line stops because one station pulled the shared stop-cord (HREADY low) and never released it. You don't inspect every station randomly — you check which order is at the front of the line (decode the address) to find which station is currently working it (the addressed slave), then ask that station why it's still holding the cord: is it stuck waiting on a part that never arrived, did its timer never go off, is its "done" button wired to something that's never pressed? The shared cord traces to one station's hand.

An assembly line (the bus) where every station works in lockstepnothing advances until a shared "go" signal (HREADY high) says so. There's a shared stop-cord (HREADY low) that any station can pull to halt the whole line while it finishes its work, and releases when done. Now the whole line has stoppedeverything is frozen (the symptom: the bus hangs). The naive debugger runs around inspecting every stationwasteful, because most are fine (they're just waiting for the line to move). The systematic debugger uses the structure: the line stopped because one station pulled the shared cord and never released it — and which station? The one currently working the order at the front of the line. So you (1) check which order is at the front (decode the address — which slave is addressed), (2) go to that station (the addressed slave), and (3) ask why it's still holding the cord (why is its HREADYOUT stuck low?). Then the root cause is one of a few: the station is waiting on a part that never arrived (a resource — memory/downstream — that never responds); its work-timer never went off (a wait-FSM counter that never expires); its "done" button is wired to something never pressed (a completion condition permanently false). Or — a subtler case — the station did release the cord (its HREADYOUT is high), but the line's control panel (the interconnect) is reading the wrong station's cord (an aggregation/routing bug) — so the line thinks it's still pulled. You distinguish these by checking the station's own cord directly: is the station still pulling (cause in the station), or has it released but the panel still shows stopped (cause in the panel)?

This captures stuck-HREADY debug: the whole assembly line stopped = the bus hangs (HREADY stuck low); the shared stop-cord = the shared HREADY; one station pulling it and not releasing = the addressed slave's HREADYOUT stuck low; inspecting every station randomly = un-directed debug; checking the order at the front = decoding the address; going to that station = the addressed slave; asking why it holds the cord = examining its HREADYOUT logic; waiting on a part / timer never off / done-button mis-wired = resource/FSM-counter/completion-condition causes; the station released but the panel reads wrong = the interconnect aggregation/routing bug; checking the station's own cord directly = comparing the slave's HREADYOUT to the shared HREADY. Trace the shared cord to the one station holding it, then ask why.

Watch a stuck HREADY on the waveform — and contrast with a normal wait:

Stuck HREADY vs a normal (bounded) wait

6 cycles
Cycles 0-2: a normal wait — HREADY low for 2 cycles then rises, the transfer completes. Cycles 3 onward: a transfer to A2 where HREADY goes low and never rises, the master holds A2 frozen, the addressed slave drives its HREADYOUT low forever. A normal wait is bounded and ends; a stuck HREADY is unbounded — the symptom. Decode A2, go to that slave, find why its HREADYOUT never rises.Normal wait: HREADY low but BOUNDED — it will riseNormal wait: HREADY lo…Wait ends — HREADY rises, transfer completes (healthy)Wait ends — HREADY ris…A2's slave HREADYOUT stuck low → HREADY never rises → bus HANGSA2's slave HREADYOUT s…HCLKHADDRA1A1A2A2A2A2HREADYaddr slave HREADYOUTstatewaitwaitdonestuckstuckHANGt0t1t2t3t4t5
Figure 2 — a stuck HREADY versus a normal wait state. Cycles 0-2 (normal wait): HREADY is low for 2 cycles (the slave inserting wait states) then RISES in cycle 2 — the transfer completes, the master proceeds. This is healthy: a wait that ENDS. Cycles 3+ (stuck): a new transfer to address A2; HREADY goes low and NEVER rises — cycle after cycle, HREADY stays 0, the master holds HADDR=A2 frozen, no completion. The 'addressed slave HREADYOUT' row shows A2's slave driving its HREADYOUT low forever (the bug is inside that slave). The distinction: a normal wait HREADY low is bounded and ends; a stuck HREADY low is unbounded — that's the symptom. Decode A2 → that slave → why its HREADYOUT never rises.

The model's lesson: trace the shared cord to the one station holding it, then ask why. In the waveform, the normal wait (cycles 0–2) is boundedHREADY rises and the transfer completes; the stuck case (cycles 3+) is unbounded — A2's slave drives its HREADYOUT low forever, so HREADY never rises and the bus hangs. The trace: decode A2 → that slave → why its HREADYOUT never rises.

4. Real Hardware Perspective

In debug, you use the waveform and the signal hierarchy: confirm the symptom (HREADY stuck), decode HADDR to the slave, compare that slave's HREADYOUT to the shared HREADY (to split interconnect vs slave), then inspect the slave's wait-FSM/completion logic — and add a watchdog assertion to catch it automatically.

The confirm and locate: in the waveform, confirm the symptom — HREADY low for many cycles with no rising edge (vs a bounded wait), the master's HADDR/HTRANS frozen. Then decode the frozen HADDR against the address map (11.4) to find which slave is addressed. So in debug, confirm the symptom and identify the slave. So it's locate-the-slave. So that's step one.

A flowchart from the stuck-HREADY symptom through address decode and the HREADYOUT-vs-HREADY split to the in-slave root causes.
Figure 3 — the stuck-HREADY diagnosis flowchart. From the symptom (HREADY stuck low): decode HADDR (which slave?), then compare HREADYOUT vs HREADY. If the slave drives its HREADYOUT high but the shared HREADY is still low → the bug is in the interconnect's HREADY aggregation/routing. If the slave drives its own HREADYOUT low → the bug is inside that slave: inspect its wait FSM / completion — FSM stuck in wait? counter not expiring? completion condition permanently false? resource never responds? — each leads to a specific fix. The flow turns a vague bus hang into a directed search ending at a concrete root cause and fix.

The split interconnect vs slave: the key diagnostic fork — compare the addressed slave's HREADYOUT to the shared HREADY. If the slave drives its HREADYOUT high but the shared HREADY is still low → the bug is in the interconnect's HREADY aggregation/routing (13.7 — it's selecting the wrong slave's ready, or its select logic is wrong). If the slave drives its own HREADYOUT low → the bug is inside that slave. So in debug, this one comparison splits the search. So it's the fork. So that localizes it.

The inspect the slave logic + watchdog: if it's inside the slave, inspect its wait-FSM/completion logic (16.4): is the FSM stuck in a wait state? Is the counter not expiring? Is the completion condition (the term that raises HREADYOUT) permanently false (ANDed with a signal that's always 0, or waiting on a valid/ack that never comes)? Is it waiting on a resource (memory/downstream bus) that's itself stuck? Each → a specific fix. And to catch this automatically (in sim and the field), add a watchdog: a liveness assertion (17.2 — !HREADY |-> ##[1:MAX] HREADY) in sim, or a bus watchdog timer in hardware that flags HREADY low beyond a threshold. So in debug, inspect the slave's wait/completion logic and add a watchdog. So in debug, stuck HREADY is a directed waveform trace (confirm symptom → decode address → split interconnect/slave via the HREADYOUT-vs-HREADY comparison → inspect the slave's wait-FSM/completion → fix) plus a watchdog to catch recurrence. So in debug, trace by the structure and guard with a watchdog. So that's the method.

5. System Architecture Perspective

At the system level, a stuck HREADY is a total system failure (the bus is the backbone, so its hang freezes everything) — making it the highest-severity bus bug — and its debuggability and containability (watchdogs, timeouts) are essential system properties.

The total system failure: the bus is the backbone — the CPU reaches memory and peripherals through it. So a bus hang (stuck HREADY) freezes the entire system — the CPU can't fetch, can't access anything, halts. There's no graceful degradation; it's a total freeze. So at the system level, a stuck HREADY is the highest-severity failure — everything stops. So it's catastrophic. So it's top-priority.

The containment via watchdogs: because a stuck HREADY is catastrophic and can be caused by any slave (or a transient fault, or a downstream hang), robust systems contain it: a bus watchdog timer monitors HREADY and, if it's low beyond a threshold, triggers recovery (an error response to unstick the master, an interrupt, a reset of the offending slave, a system reset). So the catastrophe is contained — a hang becomes a recoverable fault rather than a permanent freeze. So at the system level, watchdogs are essential to contain the failure. So it's containment. So robust systems guard against it.

The debuggability discipline: because stuck HREADYs will happen (in bringup, in the field), the system must be debuggableobservable signals (the ability to see HREADY, HREADYOUT, the address — via debug ports, trace, or simulation), and a known method (the directed trace). So at the system level, a stuck HREADY is a total system failure (the bus backbone hangs → everything freezes — highest severity), contained by watchdogs/timeouts (turning a permanent freeze into a recoverable fault — essential robustness), and debugged by a directed, structure-guided trace (observable signals + the known method). So stuck HREADY is the most severe bus failure, and a mature system both guards against it (watchdogs) and is built to debug it (observability + method) — because the bus backbone hanging is the one failure you cannot ship. So guard, observe, and trace. So treat the bus hang as the top-severity, must-contain failure.

6. Engineering Tradeoffs

Debugging a stuck HREADY embodies the directed-trace, structure-guided, watchdog-guarded approach.

  • Directed trace vs random inspection. A directed trace (decode address → that slave's HREADYOUT → cause) is fast (the structure guides it); random inspection is slow (most signals are red herrings). Trace by the structure.
  • HREADYOUT-vs-HREADY split vs assume slave. Comparing the slave's HREADYOUT to the shared HREADY splits interconnect-routing bugs from slave bugs; assuming the slave misses the routing case. Always do the comparison.
  • Watchdog vs no guard. A watchdog (assertion in sim, timer in hardware) catches the hang automatically and contains it; no guard means a silent permanent freeze. Add a watchdog.
  • Fix the cause vs mask the symptom. Fixing the root cause (FSM/resource/condition/routing) is correct; masking (e.g. forcing HREADY high) hides the bug and corrupts the transfer. Fix the cause.

The throughline: a stuck HREADY hangs the entire bus (HREADY never rises → the current transfer never completes → the master holds forever). Debug it by a directed, structure-guided trace: the shared HREADY is the addressed slave's HREADYOUT routed back (6.6), so decode HADDR, go to that slave, and compare its HREADYOUT to the shared HREADY — if the slave drives HREADYOUT high but HREADY is still low, it's an interconnect aggregation/routing bug; if the slave drives its own HREADYOUT low, it's inside the slave (wait-FSM never done, resource never responds, completion condition permanently false). The fix follows the cause. The field guard is a bus watchdog (flag HREADY low beyond a threshold). It's the highest-severity bus failure (the backbone hangs → total system freeze), contained by watchdogs and debugged by the directed trace.

7. Industry Example

Debug a real stuck HREADY in a system with a CPU, a memory, and a bridge to peripherals.

The system hangs at bringup — the CPU is frozen. You suspect a bus hang.

  • Confirm the symptom. On the waveform, HREADY is low for thousands of cycles with no rising edge; the CPU master's HADDR/HTRANS are frozen (held). This is a stuck HREADY (not a bounded wait — it never rises).
  • Decode the address. The frozen HADDR decodes (via the address map) to the peripheral region — so the bridge (to the peripheral bus) is the addressed slave.
  • Split interconnect vs slave. You compare the bridge's HREADYOUT to the shared HREADY: the bridge is driving its own HREADYOUT low (and the shared HREADY follows it) — so the bug is inside the bridge, not the interconnect routing.
  • Inspect the bridge's wait logic. The bridge (a control FSM — 16.10) is stuck in its ACCESS state, waiting for the downstream (peripheral bus) to complete. So you trace further — to the downstream side: the downstream PREADY (or equivalent) is never asserting. The downstream peripheral is itself stuck (a clock not running, a reset not released, or its own hang).
  • Root cause + fix. The root cause: the peripheral bus's clock was gated off (a clock-enable bug) — so the peripheral never responds, the bridge waits forever, and the bridge's HREADYOUT stays low, hanging the whole AHB. The fix: correct the clock-enable so the peripheral runs. (The bug chained: peripheral stuck → bridge stuck → bus stuck.)
  • Add a watchdog. To catch this in the field (and future bringups), you add a bus watchdog timer: if HREADY is low beyond, say, 1024 cycles, it triggers a recovery (an interrupt + a forced ERROR to unstick the CPU) — turning a silent permanent freeze into a reported, recoverable fault.

The example shows the directed trace in action: confirm (truly stuck), decode (the bridge), split (inside the slave, not interconnect), inspect (the bridge's wait — chained to a downstream hang), root cause (a clock-enable bug), fix, and guard (a watchdog). The hang chained across the bridge — but the structure-guided trace followed it to the real cause. This is how you debug the most catastrophic bus bug. This is the canonical AHB debug.

8. Common Mistakes

9. Interview Insight

Stuck HREADY is a classic debug interview topic — the directed-trace method, the HREADYOUT-vs-HREADY split, and the never-force-HREADY discipline are the signals.

A summary card on debugging stuck HREADY: symptom, the directed trace, the causes, and the watchdog guard.
Figure 4 — a strong answer in one card: symptom (HREADY low for many cycles, master frozen → bus hangs); trace (shared HREADY = the addressed slave's HREADYOUT routed back → decode HADDR, go to that slave); causes (wait-FSM never done, resource never responds, wrong completion condition, aggregation routing bug); field guard (a bus watchdog flags HREADY low beyond a threshold). The senior point: trace the hang through shared HREADY → the addressed slave's HREADYOUT, and find why it never rises.

The answer that lands gives the directed trace and the split: "A stuck HREADY hangs the whole bus — the shared HREADY never goes high, so the current transfer never completes, the master holds forever, and everything freezes. The symptom on the waveform is HREADY low for many cycles with no rising edge, and the master frozen on the same address. The key to debugging it efficiently is the signal structure: the shared HREADY is the addressed slave's HREADYOUT, selected and routed back by the interconnect. So I don't inspect randomly — I decode the frozen address to find which slave is addressed, then go to that slave's HREADYOUT. The crucial diagnostic fork is comparing that slave's HREADYOUT to the shared HREADY: if the slave is driving its HREADYOUT high but the shared HREADY is still low, the bug is in the interconnect's HREADY aggregation — it's routing the wrong slave's ready; if the slave is driving its own HREADYOUT low, the bug is inside that slave. Inside the slave, the causes are a wait-state FSM that never reaches its done state — a counter that never expires or a condition never true — a resource the slave is waiting on that never responds, like a memory or a downstream bus that's itself stuck, or a completion condition gated by something permanently false. Each leads to a specific fix. Two things I'm careful about. First, I never 'fix' it by forcing HREADY high, because that completes the transfer with invalid data and corrupts the access — masking the hang with silent corruption, which is worse. I fix the root cause. Second, I distinguish a stuck HREADY from a long but bounded wait — a slow slave also holds HREADY low for many cycles but eventually rises; the test is whether HREADY ever rises. And the master being frozen is correct behavior, address stability while HREADY is low — it's the victim, not the cause. For the field, I'd add a bus watchdog that flags HREADY low beyond a threshold, turning a silent freeze into a recoverable fault." The directed-trace method, the HREADYOUT-vs-HREADY split, and the never-force-HREADY discipline are the senior signals.

10. Practice Challenge

Build and reason from debugging a stuck HREADY.

  1. The symptom. Describe the waveform symptom of a stuck HREADY and how it differs from a normal wait.
  2. The directed trace. Explain how the signal structure (shared HREADY = the addressed slave's HREADYOUT) directs the trace (decode address → that slave → cause).
  3. The split. Explain the HREADYOUT-vs-HREADY comparison and how it splits interconnect-routing bugs from in-slave bugs.
  4. The causes. List the in-slave causes (wait-FSM never done, resource never responds, completion condition false) and how to spot each.
  5. The traps. Explain why you never force HREADY high, how to distinguish a hang from a slow slave, and why the master is the victim.

11. Key Takeaways

  • A stuck HREADY hangs the entire busHREADY never rises → the current transfer never completes → the master holds forever. The highest-severity bus failure (the backbone freezes).
  • Symptom: HREADY low for many cycles (no rising edge), the master frozen on the same address, no activity — vs a bounded wait (which rises).
  • Trace by the signal structure — the shared HREADY is the addressed slave's HREADYOUT routed back (6.6): decode HADDR → that slave → why its HREADYOUT is stuck low. Directed, not random.
  • Split interconnect vs slave — compare the slave's HREADYOUT to the shared HREADY: slave high but shared lowinterconnect aggregation/routing bug; slave's own lowinside the slave (wait-FSM never done / resource never responds / completion condition permanently false).
  • Never force HREADY high — it completes the transfer with invalid data/state (masking with silent corruption). Fix the root cause. Distinguish stuck (never rises) from slow (eventually rises). The master is the victim, not the cause.
  • Guard with a watchdog — a bus watchdog timer (or sim liveness assertion — 17.2) flags HREADY low beyond a threshold, containing the catastrophe (silent freeze → recoverable fault).

12. What Comes Next

You now can debug the most catastrophic bus failure. The next chapters cover the other common AHB failures:

  • Wrong HTRANS (next) — catch incorrect IDLE/BUSY/NONSEQ/SEQ sequencing.
  • Address/Data Misalignment, Incorrect HSEL, and the rest — diagnose the other failure modes, building to a general waveform-debug methodology.

To revisit the signal this hinges on, see HREADYOUT Generation, HREADY vs HREADYOUT, and What HREADY Means; for the watchdog assertion, see AHB Assertions (SVA).