Skip to content

AMBA APB · Module 13

Wait-State Propagation

How an APB peripheral's wait state propagates upstream through the bridge to stall the AHB or AXI master — the back-pressure chain, the latency multiplication a CPU sees, and the unbounded-wait hang hazard that makes a bridge-level timeout essential.

A peripheral wait does not stay on the peripheral. When an APB slave holds PREADY low, the bridge has no buffer to hide behind — APB does not pipeline, so the bridge holds its upstream handshake deasserted too, and the back-pressure walks all the way to the CPU. This chapter is the back-pressure chain: how one peripheral wait cycle becomes one upstream wait cycle plus the bridge's own two-phase overhead, why an unbounded wait hangs the whole system, and why a serious bridge needs a timeout. The throughline: a bridge makes APB back-pressure visible upstream — a peripheral wait becomes a CPU stall — which is correct flow control but also a system-latency and hang risk.

1. Problem statement

The problem is carrying APB's wait state across the protocol boundary so a slow peripheral correctly stalls the upstream master — without losing the access, and without letting a non-responding peripheral hang the entire bus.

An APB peripheral asserts PREADY=0 to say "I'm not done — hold the transfer." On a standalone APB bus that wait stalls only the APB manager. But behind a bridge, the APB manager is the bridge, and the bridge is servicing a live AHB (or AXI) transaction on its other face. The bridge cannot tell the upstream master "done" until APB is actually done. So the wait has to propagate:

  • The bridge has nowhere to put the wait. APB has no pipelining and no write buffer in the base bridge — there is exactly one in-flight transfer. While PREADY=0, the bridge has no result to return, so it must hold the upstream handshake deasserted (HREADY low on AHB; the response channel not VALID on AXI) and stall the master cycle-for-cycle with the peripheral.
  • The cost multiplies into a CPU-visible latency. The CPU does not just pay the peripheral's wait cycles — it pays the bridge's SETUP + ACCESS overhead on top, every access, plus the wait. One slow register read can stall the core for many cycles, and the bridge serialises, so anything queued behind it waits too.
  • An unbounded wait is a system hang. APB allows a peripheral to hold PREADY low arbitrarily long. If it never asserts PREADY — a gated clock, a wedged FSM, a bad address that hits dead space — the bridge waits forever, the upstream master never retires, and the whole CPU (and everything serialised behind the bridge) locks up. The bridge needs a timeout to convert that hang into a recoverable bus error.

So the job is not "wire PREADY to HREADY." It is to propagate the wait faithfully, account for the latency the master pays, and bound the wait so a dead peripheral cannot kill the bus.

2. Why previous knowledge is insufficient

You already know each piece in isolation — but not the chain, and not its failure mode.

  • 13.1 (the AHB-to-APB bridge) established the mapping; this chapter drills it as a chain. 13.1 said "PREADY=0 becomes HREADY=0" as one of two cross-boundary translations. That sentence hides a whole system story: how far the back-pressure travels, how much latency it costs the CPU, and what happens when the wait never ends. This chapter is that story.
  • Module 8's multi-wait / unbounded waits were a one-bus phenomenon. There, PREADY low for N cycles just extended the APB ACCESS phase — bounded or unbounded, it only stalled the APB manager. Across a bridge the same unbounded wait now stalls a CPU and freezes a system bus, so "unbounded is allowed" stops being a benign spec note and becomes a hang hazard that demands a timeout.
  • AHB wait states were the AHB slave's own choice. You learned that an AHB slave extends the data phase by holding HREADY low. Here the bridge is that AHB slave — but it does not choose its wait length; it inherits it from APB. The wait length is set on the far side of the boundary, which is new.
  • Error propagation is the sibling, owned next door. This chapter is wait propagation only — the peripheral is working, just slow. 13.5 covers what happens when the peripheral fails (PSLVERR → HRESP=ERROR). Keep them separate: a wait stalls; an error aborts.

So the model to add is the propagation chain: a single peripheral wait, traced from PREADY low, through the bridge, to the exact cycle the CPU stalls — and the bound that keeps it from becoming a hang.

3. Mental model

The model: the bridge is a single-lane checkout with no holding area. The peripheral is the customer fumbling for change at the register; the bridge is the cashier; the upstream master is the next person in line — and the whole CPU is everyone behind them. The cashier cannot wave the next person forward until the current customer pays, and there is no second lane to absorb the delay. Every second the customer fumbles, the entire line waits. And if the customer freezes and never pays, the line never moves — unless the store has a rule: after a set time, void the sale and move on.

Three refinements make it precise:

  • The wait propagates one-for-one, plus the bridge's overhead. While PREADY=0, the bridge holds the upstream handshake deasserted for exactly those cycles. But the upstream master also pays the bridge's SETUP + ACCESS framing on every access. So the CPU-visible latency is SETUP (1) + ACCESS (1) + N wait cycles — the wait is added to, not absorbed by, the bridge's own cost. This is the latency multiplication: a peripheral that waits N cycles makes the CPU wait N cycles and pay the two-phase tax.
  • AHB and AXI propagate the same wait through different signals. On AHB, the bridge holds HREADY low — it extends the data phase of the AHB transfer (data-phase extension) for the whole APB transfer. On AXI, there is no single ready line; instead the bridge simply does not assert the response — the read data on the R-channel (R-channel) or the write response on the B-channel (B-channel) does not go VALID until APB completes. Different mechanics, identical effect: the master is stalled until PREADY rises.
  • Unbounded propagation is a hang; the bridge must bound it. Because the wait length comes from the far side of the boundary, a peripheral that never finishes propagates an infinite stall upstream. The bridge cannot trust the peripheral to terminate, so a robust bridge runs a watchdog: count the wait cycles, and on reaching a limit, abort the APB transfer, release the upstream handshake, and return an error upstream — turning a fatal hang into a recoverable fault.
A waveform of one peripheral access through an AHB-to-APB bridge: the APB side shows PSEL, PENABLE, and PREADY held low for three wait cycles in amber during ACCESS; directly above, HREADY on the AHB side is held low in amber for the entire APB transfer; both PREADY and HREADY rise together on the blue completion edge, and a bracket annotates the total CPU-visible latency as one SETUP plus one ACCESS plus three wait cycles.
Figure 1 — the back-pressure chain on one peripheral access. Bottom (downstream, APB): the transfer walks SETUP then ACCESS, and the peripheral holds PREADY low for three wait cycles (amber) before asserting it. Top (upstream, AHB): the bridge holds HREADY low (amber) for the whole APB transfer — SETUP plus ACCESS plus the three waits — so the AHB master is stalled cycle-for-cycle with the peripheral. PREADY and HREADY rise together on the completion edge (blue), where the access finally retires. The bracket shows the latency the CPU pays: 1 (SETUP) + 1 (ACCESS) + 3 (waits) = 5 AHB wait states — the wait is added to the bridge's two-phase overhead, not absorbed by it. The bridge has no buffer to hide the wait because APB does not pipeline.

4. Real SoC implementation

In RTL the propagation is almost trivial — the upstream ready is just a function of the APB completion — but the robust version pairs it with a timeout watchdog so a non-responding peripheral cannot hang the bus. Below, the AHB side holds HREADY low until APB completes, and a counter aborts a stuck transfer and returns an error upstream.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Wait propagation + timeout watchdog in an AHB-to-APB bridge.
// HREADY upstream is held low until APB completes; a watchdog bounds the wait.
typedef enum logic [1:0] {IDLE, SETUP, ACCESS} state_t;
state_t state, nstate;
 
localparam int WAIT_LIMIT = 1024;            // max APB wait cycles before abort (tune per SoC)
logic [10:0] wait_cnt;                        // counts cycles spent waiting in ACCESS
logic        timeout;
assign timeout = (wait_cnt == WAIT_LIMIT);    // watchdog has expired
 
// --- count wait cycles while we sit in ACCESS with PREADY still low ---
always_ff @(posedge hclk or negedge hresetn)
  if (!hresetn)                       wait_cnt <= '0;
  else if (state == ACCESS && !pready) wait_cnt <= wait_cnt + 1'b1;  // still waiting
  else                                 wait_cnt <= '0;               // completed or left ACCESS
 
// --- the transfer ends when APB completes (pready) OR the watchdog fires (timeout) ---
wire apb_done = pready || timeout;
 
always_comb begin
  nstate = state;
  unique case (state)
    IDLE:   if (ahb_accept) nstate = SETUP;
    SETUP:                  nstate = ACCESS;
    ACCESS: if (apb_done)   nstate = IDLE;     // leave on real completion OR timeout
  endcase
end
always_ff @(posedge hclk or negedge hresetn)
  if (!hresetn) state <= IDLE; else state <= nstate;
 
// --- WAIT PROPAGATION: hold HREADY low for the whole APB transfer ---------
// HREADY is high only when idle, or when the APB transfer actually finishes.
// While PREADY is low, this stays low -> the AHB master is stalled.
assign hready_out = (state == IDLE) || (state == ACCESS && apb_done);
 
// --- the wait becomes an ERROR only if the watchdog aborted it -------------
// A clean completion is OKAY; a timeout returns ERROR so the CPU faults
// instead of hanging. (PSLVERR-driven errors are covered in 13.5.)
assign hresp = (state == ACCESS && timeout) ? HRESP_ERROR : HRESP_OKAY;
assign hrdata = prdata;                        // read data, valid on real completion

Two facts make this the canonical pattern. First, wait propagation is a pure consequence of having no buffer: hready_out is low for the entire ACCESS phase until apb_done, so every cycle the peripheral waits is a cycle the AHB master is stalled — the bridge does not, and cannot, absorb it. Second, the timeout is not optional in a hardened bridge: without it, pready low forever means hready_out low forever, and the CPU never retires the instruction — a permanent hang. The watchdog converts an unbounded APB wait (the multi-wait/unbounded concept) into a bounded upstream stall that ends in a clean bus error. On the AXI side the same logic appears differently: there is no hready_out to hold low — instead the bridge withholds RVALID/BVALID (and may withhold AWREADY/WREADY/ARREADY so the master cannot even launch the next access) until apb_done, then drives RRESP/BRESP = SLVERR on a timeout. The forward question — what HRESP=ERROR (or SLVERR) means and how the master reacts — is error propagation, the sibling chapter.

5. Engineering tradeoffs

The wait propagates the same way everywhere, but the signal it travels on, the latency it costs, and the hang risk differ by upstream protocol — and the timeout policy is a real design knob.

UpstreamHow the wait propagates (PREADY=0 →)Upstream stall mechanismLatency the master paysHang risk / mitigation
AHBBridge holds HREADY lowData-phase extension: the AHB transfer's data phase is stretched, master frozenSETUP + ACCESS + N cycles, surfaced as AHB wait statesHREADY low forever ⇒ master never retires; bound with a watchdog, return HRESP=ERROR on timeout
AXI (read)Bridge withholds RVALIDR-channel handshake never completes; master's read outstanding stays openSETUP + ACCESS + N cycles before RVALID; may also stall ARREADY so no new reads launchRVALID never asserted ⇒ read hangs; timeout drives RRESP=SLVERR to release it
AXI (write)Bridge withholds BVALID (and may hold WREADY low)B-channel response never returned; write transaction never acknowledgedSETUP + ACCESS + N cycles before BVALIDBVALID never returned ⇒ write hangs; timeout drives BRESP=SLVERR
Timeout limitWAIT_LIMIT smallFails fast — quick recovery, but may abort a legitimately slow peripheraln/aToo small ⇒ false aborts; too large ⇒ long visible stall before recovery

The throughline: the wait is correct flow control on every upstream — a slow peripheral must stall its master — but on every upstream it is also a latency cost (the master pays the bridge's two-phase overhead plus the wait) and a hang risk (an unterminated wait propagates an infinite stall). AHB carries it on one HREADY line by stretching the data phase; AXI carries it by simply not asserting the response VALID. The single defence against the hang is a bridge-level timeout, and its limit trades recovery speed against false aborts.

6. Common RTL mistakes

7. Debugging scenario

The signature wait-propagation bug is a whole-SoC hard hang caused by an unbounded peripheral wait with no bridge timeout — a peripheral never asserts PREADY, so HREADY stays low forever and the CPU never retires the instruction.

  • Observed symptom: the entire SoC freezes. The CPU stops executing — no further instructions retire, no interrupts service, the watchdog (if any) eventually resets the chip. It is reproducible: it happens right after software accesses one specific peripheral, often one that was just clock-gated or power-gated for low-power, then poked without re-enabling its clock.
  • Waveform clue: on the APB face, PSEL and PENABLE are high (the bridge is in ACCESS) but PREADY is stuck low forever — and the peripheral's PCLK is flatlined (gated off), so its PREADY logic never even runs. On the AHB face, HREADY is low and never rises; HTRANS is frozen and the AHB master's address phase is stalled indefinitely. There is no error, no completion — just a flat stall on both faces.
  • Root cause: the bridge propagated the wait correctly (HREADY low while PREADY low) but had no upper bound on the wait. The peripheral's clock was gated, so it could never assert PREADY; with no timeout, the bridge sat in ACCESS forever, holding HREADY low forever, so the AHB master never completed its data phase and the CPU never retired the access — a permanent, system-wide hang.
  • Correct RTL: add a watchdog that bounds the wait and aborts a stuck transfer — if (state==ACCESS && !pready) wait_cnt <= wait_cnt + 1; ... wire apb_done = pready || (wait_cnt==WAIT_LIMIT); — release HREADY on apb_done and drive HRESP=ERROR when the limit is hit, so a non-responding peripheral becomes a recoverable bus fault instead of a hang. (Belt-and-braces: also keep peripheral clocks enabled while their address space is accessible.)
  • Verification assertion: assert that the bridge always completes the upstream handshake within a bounded number of cycles once it starts a transfer — assert property (@(posedge hclk) disable iff(!hresetn) (state==ACCESS) |-> ##[1:WAIT_LIMIT+1] (hready_out)); — i.e. once in ACCESS, HREADY must rise within WAIT_LIMIT+1 cycles (by real completion or by timeout). A liveness/s_eventually form catches the "never rises" hang directly.
  • Debug habit: when an SoC hard-hangs on a peripheral access, look at the bridge's downstream handshake first. If PSEL/PENABLE are asserted and PREADY is stuck low — especially with a dead PCLK — you have an unbounded wait propagating upstream. Then ask the structural question every bridge must answer: is there a timeout? A bridge that propagates waits but cannot bound them turns any wedged peripheral into a chip-killer; the fix is always a watchdog plus an error-return path.
Two stacked timing diagrams. Top, drawn red: PREADY stays low forever because the peripheral clock is gated, and with no timeout HREADY stays low forever, hanging the CPU. Bottom, drawn green: a wait_cnt counter increments to a limit N, at which point the bridge releases HREADY, ends the transfer, and drives HRESP to ERROR, turning the hang into a recoverable bus fault.
Figure 2 — unbounded wait, two cases. Top (bug, red): the peripheral never asserts PREADY (its clock is gated off), and with no timeout the bridge holds HREADY low forever — the AHB master never retires and the whole CPU, plus everything serialised behind the bridge, hard-hangs. Bottom (correct, green): a watchdog counts the wait cycles (wait_cnt 1, 2, …, N); when it reaches the limit N the bridge aborts the stuck APB transfer, releases HREADY, and drives HRESP=ERROR upstream, so the CPU takes a recoverable bus fault instead of locking up. The figure shows that unbounded wait propagation is a system hang hazard and a bridge timeout converts a non-responding peripheral into a recoverable error.

8. Verification perspective

Wait propagation is verified as a liveness property plus latency accounting — the classic per-protocol monitors will happily pass a bridge that hangs on an unterminated wait, so the boundary checks must be explicit.

  • Assert the back-pressure mapping holds every cycle. While the APB transfer is incomplete, the upstream master must be stalled: assert that PREADY=0 during the bridge's ACCESS keeps HREADY low ((state==ACCESS && !pready) |-> !hready_out), and on AXI that the corresponding response stays non-VALID. The dual is just as important — HREADY must not rise before apb_done, or the master samples stale data. This pins the wait to the boundary so a coverage hole can't let it leak early.
  • Assert bounded upstream completion (the anti-hang check). This is the property a pure protocol monitor misses: once the bridge starts a transfer it must finish within a bound — (state==ACCESS) |-> ##[1:WAIT_LIMIT+1] hready_out, or as liveness, s_eventually hready_out. Pair it with a check that a timeout produces HRESP=ERROR (not a silent completion) so the abort is observable to the master.
  • Cover the latency distribution: 0, 1, and N waits — and the timeout firing. Functional coverage must include an APB transfer with zero wait states (the fast path), one wait (the minimal stall), and N waits up to the timeout limit (the slow path), so the propagation is exercised across its whole range. Crucially, add a bin for the timeout actually firing — drive a peripheral that never asserts PREADY and confirm the watchdog aborts and the master recovers. A bridge tested only with fast, always-PREADY peripherals has its biggest hazard — the hang — completely uncovered.
  • Add hang detection to the test environment itself. Beyond formal assertions, the simulation should have a global activity watchdog: if no upstream transaction retires for far longer than WAIT_LIMIT, fail the test with a hang report rather than letting it time out silently. This catches not just the modelled timeout path but any unmodelled deadlock in the propagation logic.

The point: verify the wait as a bounded liveness property — it must propagate fully (master stalled while the peripheral waits) and terminate (within WAIT_LIMIT, by completion or by a visible error) — and cover the 0/1/N wait distribution plus the timeout-fires case. The per-protocol monitors check the handshake shape; only these boundary checks catch the hang.

9. Interview discussion

"What happens when an APB peripheral is slow — and what if it never responds?" is a favourite SoC-integration question, and the strong answer traces the chain and names the hang hazard, not just "PREADY becomes HREADY."

Lead with the mechanism: the bridge has no buffer, so a peripheral wait propagates straight upstream. While PREADY=0, the bridge holds the upstream handshake deasserted — HREADY low on AHB (stretching the data phase), or the R/B-channel response simply not VALID on AXI — so the master is stalled cycle-for-cycle with the peripheral. Then deliver the depth that separates senior answers: the latency multiplication — the CPU pays the peripheral's N wait cycles plus the bridge's SETUP+ACCESS overhead on every access, and because the bridge serialises, anything behind that access waits too (head-of-line blocking). Then raise the hang hazard yourself: an unbounded wait — a gated clock, a wedged peripheral — propagates an infinite stall, hard-hanging the CPU and everything behind the bridge, so a hardened bridge needs a timeout watchdog that aborts the stuck transfer and returns a bus error. Closing notes that mark breadth: "AHB and AXI propagate the same wait — AHB on HREADY, AXI by withholding the response VALID," and "this is wait propagation; the error response the timeout returns is the sibling problem." That arc — propagate, multiply, bound — is exactly what an interviewer is listening for.

10. Practice

  1. Trace the chain. For one AHB read to a peripheral that inserts three APB wait states, mark on a timeline when PREADY goes low, when HREADY goes low, and the exact cycle both rise — and count the AHB wait states the CPU sees.
  2. Cost the access. With a two-cycle (SETUP+ACCESS) bridge and N peripheral wait cycles, write the CPU-visible latency as a formula, then evaluate it for N = 0, 1, and 4.
  3. Map AHB vs AXI. State, for the same PREADY=0, what signal the bridge manipulates on an AHB upstream versus an AXI read upstream, and confirm the master-visible effect is identical.
  4. Design the bound. Sketch the watchdog: which state it counts in, what it counts, what it does at the limit, and what response it returns upstream — and explain why the limit trades recovery speed against false aborts.
  5. Reason about head-of-line blocking. Explain why a single slow peripheral stalls not just its own access but everything queued behind the bridge, and why that follows from APB being unpipelined.

11. Q&A

12. Key takeaways

  • A peripheral wait propagates straight upstream. The bridge has no buffer — APB doesn't pipeline — so while PREADY=0 the bridge holds its upstream handshake deasserted and the master is stalled cycle-for-cycle with the peripheral.
  • The latency multiplies. The CPU pays the N peripheral wait cycles plus the bridge's SETUP+ACCESS overhead on every access, and because the bridge serialises, everything queued behind that access waits too (head-of-line blocking).
  • AHB and AXI carry the same wait differently. AHB holds HREADY low (data-phase extension); AXI withholds the R/B-channel response VALID. Identical effect — the master stalls — different signal.
  • An unbounded wait is a hang hazard. A peripheral that never asserts PREADY propagates an infinite stall, hard-hanging the CPU and everything behind the bridge. The spec allows unbounded waits; a system cannot.
  • A timeout makes propagation safe. A watchdog that bounds the wait, aborts a stuck transfer, and returns a bus error converts a non-responding peripheral from a fatal lock-up into a recoverable fault.
  • Verify it as bounded liveness. Assert the wait propagates fully (master stalled while the peripheral waits) and terminates within WAIT_LIMIT (by completion or visible error), and cover the 0/1/N wait distribution plus the timeout-fires case — the hang is the corner per-protocol monitors miss.