AMBA APB · Module 10
PREADY Introduction Rationale
Why APB3 added PREADY — the engineering pressure of peripherals that genuinely could not answer in one cycle, the bad pre-PREADY workarounds (slow the whole bus, pad worst-case dead cycles, force slow IP onto AHB), and why a single level-based back-pressure bit that lets each peripheral self-pace was the minimal right fix.
APB3 did not add PREADY to make the bus faster — it added it to make a whole class of peripherals legal at all. The single idea to carry: a fixed two-cycle bus could not accommodate the real diversity of peripheral latencies — clock-domain crossings, multi-cycle flash, synchronized register banks, shared resources — without either crippling the fast peripherals or forbidding the slow ones. Flow control was the minimal, per-transfer fix: one back-pressure bit that lets each peripheral self-pace, so a slow slave extends only its own transfer while every fast one stays fast. This chapter is the why behind that decision — the pressure that forced it and the alternatives that lost.
1. Problem statement
APB2 gave you the cheapest correct way to read and write a register, on one strict condition: the slave must answer within the single ACCESS cycle, every time. That condition was fine when every peripheral really was a near-combinational register file. It stopped being fine the moment SoCs filled up with peripherals that genuinely could not answer in one cycle.
The pressure came from four directions at once, and all of them are real silicon, not edge cases:
- Clock-domain crossings to a slower peripheral clock. A peripheral living on its own slow clock (a low-power UART, an audio block, a sensor interface) must synchronize the access across the boundary. A two-flop synchronizer alone is two of that peripheral's clocks — which can be many APB clocks. There is no honest way to make that single-cycle.
- Register banks behind synchronizers or handshakes. Even a "register" read can take multiple cycles when the register physically lives in another domain and the value must be safely brought across. The decode is instant; the data is not.
- Flash / EEPROM / multi-cycle memory. A flash read is inherently multi-cycle — sense, settle, return. No clever RTL collapses a flash access into one ACCESS cycle.
- Peripherals sharing a resource. A peripheral arbitrating for a shared SRAM, a shared crypto engine, or a shared bus downstream cannot promise a fixed response time — sometimes it has the resource this cycle, sometimes it must wait.
So the problem APB3 had to solve was sharp: how do you let peripherals with genuinely variable, multi-cycle latency live on the same simple bus as trivial single-cycle registers, without making the simple ones pay for the slow ones? That tension — diversity of latency on one shared bus — is the entire reason PREADY exists.
2. Why previous knowledge is insufficient
You already know the mechanics of PREADY cold. Module 8 taught the handshake and timing — how the slave holds the ACCESS phase by keeping PREADY low and releases it by driving PREADY high — and the multi-wait chapter showed it extending across many cycles. And Chapter 10.2 named the fix: APB3 = APB2 + PREADY + PSLVERR. That knowledge tells you what the signal does and what the version added. It does not tell you why the committee chose this fix over the others, and that "why" is what an interviewer and a protocol architect actually probe.
To get there you have to subtract two things you have been assuming:
- You have been assuming the slow peripheral is a given on the bus. Chapter 10.1 (
apb2-baseline) was explicit: a peripheral that cannot answer in one ACCESS cycle is not a legal APB2 slave at all. So beforePREADY, the slow peripheral was not "a slow transfer" — it was an illegal transfer. The pressure was not "make slow transfers efficient"; it was "make slow peripherals possible." That reframing is the whole chapter. - You have been assuming
PREADYwas the obvious answer. It was not. There were at least three other ways to cope — slow the whole bus, pad fixed dead cycles, or banish slow peripherals to AHB — and a fourth design choice inside the flow-control idea (a fixed-latency field, or a one-shot done strobe, instead of a level-based ready). UnderstandingPREADYdeeply means understanding what each rejected option cost and why a level-based back-pressure bit beat all of them.
So the model to build here is not mechanical, it is comparative: hold the four coping strategies side by side and see why exactly one of them — per-transfer, level-based flow control — accommodates latency diversity without penalising the fast majority. The next chapter, 10.5 (pslverr-introduction-rationale), does the identical "why" treatment for the other APB3 signal, the error channel.
3. Mental model
The model: PREADY turns a fixed-length conveyor belt into a belt where each item can ask for a pause — but only the items that need it. Before PREADY, every parcel rode the belt for exactly two segments; if a parcel needed longer to process, your only options were to slow the whole belt (everyone suffers), build in a fixed extra-long segment for everyone (everyone wastes time, and it's still wrong if one parcel needs even longer), or refuse the slow parcels entirely (send them to a different, expensive belt). PREADY is the pause button on each station: the slow station holds the parcel as long as it truly needs, and the moment it's done, the parcel moves on — while every fast station never touches the button at all.
Three refinements make it precise:
- It is per-transfer, not per-bus. The wait belongs to one transfer to one peripheral. A read of a slow flash extends; the very next read of a fast GPIO does not. The bus's default is still the fast two-cycle transfer —
PREADYis the exception each slow slave invokes for itself. - It is level-based, not event-based.
PREADYis a state ("am I ready now?") sampled every ACCESS cycle, not a one-shot pulse the manager must catch. The manager simply holds the phase whilePREADY == 0and completes when it samplesPREADY == 1. There is no edge to miss, no count to maintain — which is exactly why it composes cleanly with synchronizers, arbiters, and variable-latency logic that cannot promise when they'll be done, only whether they are. - It is the minimal addition. One bit, one direction (slave → manager), no new phase, no change to SETUP/ACCESS sequencing. The two-phase core of APB2 is untouched;
PREADYjust lets the ACCESS phase last longer than one cycle when the slave says so. Minimality was a design goal, andPREADYhonours it.
4. Real SoC implementation
Here is the concrete case that motivated the whole feature: a peripheral whose data lives in a slower clock domain. Without PREADY, this block could not be a legal APB slave — you would have to clock the bus down or pad dead cycles (and pray the count is right at every corner). With PREADY, the elegant fix is a single bit: hold PREADY low until the synchronized data is genuinely valid, then assert it. The slow peripheral self-paces; the rest of the bus is untouched.
// APB3 slave for a register that physically lives in a SLOWER clock domain.
// The access must cross a CDC boundary, so the data is NOT available in the
// single ACCESS cycle. PREADY is the one bit that makes this legal on APB.
module apb3_cdc_slave (
input pclk, presetn, // APB (fast) domain
input psel, penable, pwrite,
input [31:0] pwdata,
output reg [31:0] prdata,
output pready, // <-- the entire fix lives here
// far-side handshake to the slow peripheral domain (synchronized elsewhere)
output reg req_sync, // request pulse, synced into slow domain
input ack_sync, // ack, synced back into PCLK domain
input [31:0] far_rdata // far-side data, stable when ack_sync=1
);
// Drive the cross-domain request while the access is selected and enabled,
// and DEASSERT it once we've been acked (so we issue exactly one request).
always_ff @(posedge pclk or negedge presetn) begin
if (!presetn) req_sync <= 1'b0;
else if (psel && !req_sync && !ack_sync) req_sync <= 1'b1; // launch on access
else if (ack_sync) req_sync <= 1'b0; // done, drop it
end
// THE ELEGANT FIX: PREADY is simply "is the synced data valid yet?".
// Level-based, not a pulse — the manager holds ACCESS while this is 0 and
// completes the cycle it reads 1. No cycle count, no corner-tuned padding:
// the transfer self-paces to however long the CDC actually took THIS time.
assign pready = ack_sync; // ready exactly when far data is valid
always_ff @(posedge pclk) begin
if (psel && penable && pready) // capture/commit only on the ready edge
prdata <= far_rdata; // far_rdata is guaranteed stable here
end
// WHY this beats the pre-PREADY workarounds:
// - Slowing the whole bus to the CDC rate would punish every fast register.
// - Fixed dead-cycle padding guesses a worst-case count that breaks at a
// different PVT / clock-ratio corner (see the debugging scenario).
// - Forcing this onto AHB costs gates + verification for a trivial register.
// PREADY accommodates the variable CDC latency with ONE bit, per transfer,
// and the fast peripherals on the same bus never pay for it.
endmoduleTwo facts make this the canonical motivating example. First, the latency is genuinely variable — the CDC round-trip is not a fixed number of PCLK cycles; it depends on the clock ratio and on metastability resolution, so no fixed count is correct at every corner. A level-based PREADY doesn't need a count; it tracks the actual ack. Second, the fix is local and cheap — one output, asserted only by this slave, on transfers only to this slave. The GPIO and timer two doors down still complete in two cycles. That locality is the property every rejected alternative lacked.
5. Engineering tradeoffs
The committee was not choosing between "flow control" and "no flow control" — the slow peripherals had to work somehow. The real choice was which mechanism, and each option carried a different cost. This table is the heart of the chapter.
| Pre-PREADY option | What it cost | Who pays | Why PREADY beat it |
|---|---|---|---|
| Clock the whole bus down to the slowest peripheral | Every fast register/GPIO now runs at the slow peripheral's rate — subsystem throughput is hostage to one block | All peripherals (the fast majority) | PREADY localises the slowdown to the one slow transfer; fast peripherals keep their full clock |
| Pad every transfer with fixed worst-case dead cycles | Fast peripherals waste cycles they never needed; and the "worst case" shifts across PVT / clock-ratio corners, so the count is either wasteful or wrong | Fast peripherals (waste) + correctness (corner-dependent) | PREADY is data-driven, not count-driven — it extends by exactly the cycles actually needed, at every corner |
| Forbid slow peripherals on APB — force them onto AHB | Gate count, verification effort, and design time to put a trivial peripheral on a high-performance bus it never needed | The design/verification team (cost + schedule) | PREADY lets the slow peripheral stay on the cheap simple bus — no AHB tax |
PREADY — per-transfer, level-based flow control | One extra slave→manager bit; manager must be able to hold the ACCESS phase | Almost nobody — fast transfers are unchanged | (the chosen fix) — minimal, local, corner-independent, composes with CDC/arbiters |
And there was a second trade hidden inside choosing PREADY itself — the form the signal took:
- A fixed-latency field (slave advertises "I take N cycles") was implicitly rejected because the whole problem is that real latency is not a fixed N — a CDC or an arbiter cannot honestly name a number that holds at every corner and every contention case. A field that lies is worse than no field.
- A separate one-shot "done" strobe was implicitly rejected because an event pulse must be caught — it introduces a "what if the manager misses the edge?" failure mode and forces both sides to agree on timing. A level ("am I ready now?") sampled every cycle has no edge to miss and composes cleanly with variable-latency logic. Level-based ready is the safer, more composable primitive, and that is why it won.
The throughline: PREADY is not the only way to let slow peripherals onto APB — it is the minimal way that doesn't penalise the fast majority, doesn't depend on guessing a latency, and doesn't have an edge to miss.
6. Common RTL mistakes
7. Debugging scenario
The signature "this is why PREADY exists" bug is a team that thought they'd solved a slow CDC peripheral on a fixed-latency, APB2-style bus by padding dead cycles tuned for one corner — and shipped it because it passed at that corner.
-
Observed symptom: a CDC peripheral reads correctly all through bring-up and in the lab at room temperature, but a customer reports intermittent stale/garbage reads from that one peripheral on slow-corner silicon — and only when the peripheral clock is configured to its lowest divider. Faster peripherals on the same bus are flawless.
-
Waveform clue: the bus has no
PREADYline (it's a fixed-latency segment). The ACCESS phase is padded by a hard-coded two dead cycles. On the failing trace, the peripheral's synchronized data does not go valid until one cycle after the manager samplesPRDATA— at the slow clock-ratio corner the two-flop synchronizer plus handshake needs three effective cycles, not the two the padding assumed. The manager samples on schedule and latches the previous value. -
Root cause: the design encoded a fixed peripheral latency (two pad cycles) into a bus that has no flow control, then tuned that constant at one PVT/clock-ratio corner. The real CDC latency is variable and grows at slow corners — so the constant that was correct in the lab is one cycle short on slow-corner silicon. There is no
PREADYto extend the transfer, so the manager cannot know the data isn't ready; it samples blindly. This is exactly the failure modePREADY's self-pacing was created to prevent — the design depends on a latency assumption that the silicon violates. -
Correct RTL: stop encoding a fixed latency and let the peripheral self-pace. Move the segment to APB3 and tie
PREADYto the actual data-valid handshake —assign pready = ack_sync;— so the ACCESS phase extends by exactly the cycles the CDC actually took this time, at every corner. Delete the corner-tuned pad counter entirely; it is a latent bug at any corner you didn't characterize. -
Verification assertion: prove the manager never samples read data before the slave says it's ready — i.e. the design must not rely on a fixed latency:
Azvya Education Pvt. Ltd.VLSI MentorSnippet// On any completed ACCESS, the slave's data-valid handshake MUST be true the // cycle the manager captures. If this ever fires, the design is sampling on a // latency ASSUMPTION rather than on actual readiness — the pre-PREADY bug. property no_sample_before_ready; @(posedge pclk) disable iff (!presetn) (psel && penable && pready) |-> ack_sync; endproperty assert property (no_sample_before_ready); -
Debug habit: when an integrated peripheral reads stale data only at certain corners or clock ratios, suspect a fixed-latency assumption, not a one-off timing glitch. Ask immediately: does the bus segment have
PREADY, and is it wired to the peripheral's real data-valid signal — or did someone bake in a dead-cycle count? A corner-dependent stale read is the fingerprint of a hard-coded latency that the slow corner outgrew. The fix is always to make the transfer self-pace, never to retune the constant.
8. Verification perspective
Because PREADY exists precisely to absorb variable latency, the verification job is the inverse of the bug above: prove the design works across the full range of latencies and never secretly depends on a fixed one. A testbench that only ever exercises one clock ratio will pass a design that has the corner bug baked in.
- Sweep clock-ratio and latency corners, not one nominal. The whole point of
PREADYis that the CDC/arbiter latency changes with PVT and clock divider. So the testbench must vary the number of wait cycles the slave inserts — minimum, typical, and the worst case plus margin — and confirm the manager captures correct data in every case. A design that passes at two wait cycles but fails at three is the padding bug; only a corner sweep exposes it. Reuse the multi-wait randomization patterns from the multiple-wait chapter. - Prove the design does not assume a fixed peripheral latency. Bind an assertion (as in beat 7) that read data is only ever sampled when the slave's real data-valid handshake is true — so any reliance on a hard-coded count fires rather than passing silently. Equivalently, randomize the wait-state count per transfer and check that correctness is independent of it; if any specific count is required for a pass, the design has an illegal latency assumption.
- Cover the self-pacing path explicitly. Functional coverage must hit: a zero-wait transfer (fast peripheral,
PREADYalready high), a single-wait transfer, a multi-wait transfer, and back-to-back transfers where a slowPREADY-extended access is immediately followed by a fast one (proving the wait was local and did not stick to the bus). Cross the wait-length bin with the peripheral-clock-ratio bin — that cross is where the corner bugs live.
The point: verifying PREADY is not about checking one handshake — it is about proving the design is latency-agnostic, which means deliberately exercising the diversity of latencies the feature was invented to handle, and asserting that no fixed assumption survives.
9. Interview discussion
"Why does APB3 have PREADY — what problem did it actually solve?" is a favourite because it separates engineers who memorized the handshake from those who understand protocol design pressure. The weak answer is "it adds wait states for slow peripherals." The strong answer reframes it.
Lead with legality, not speed: before PREADY, a peripheral that couldn't answer in one ACCESS cycle — a CDC register, a flash, an arbitrated resource — was not a slow APB slave, it was an illegal one (cite 10.1). The pressure was the diversity of peripheral latencies on one shared simple bus. Then walk the rejected alternatives and their costs, because that's the depth signal: you could clock the whole bus down (punishes every fast peripheral), pad fixed dead cycles (wasteful, and corner-fragile — the constant breaks at a different PVT/clock ratio), or banish slow peripherals to AHB (gate/verification/schedule cost). PREADY beat all three by being per-transfer and local — the slow slave extends only its own access, the fast majority is untouched. Finish with the form argument: it's level-based, not a one-shot done strobe (no edge to miss, composes with logic that can only say whether it's ready, not when) and not a fixed-latency field (real latency isn't a fixed N). The one-line close: PREADY is the minimal per-transfer flow-control bit that made latency-diverse peripherals legal on APB without making the fast ones pay. That answer shows you reason about trade-offs, which is exactly what protocol-integration work needs — and it sets up the same reasoning for PSLVERR in 10.5.
10. Practice
- Name the pressure. List four distinct kinds of real peripheral that genuinely cannot answer in one ACCESS cycle, and for each say why the latency is variable (not just long).
- Cost out the alternatives. For a bus with ten fast registers and one slow CDC peripheral, explain concretely what "clock the whole bus down" costs versus "fixed worst-case padding" versus "force the slow one onto AHB" — and which peripherals pay in each case.
- Defend level over event. Argue, in two sentences each, why a level-based
PREADYis safer than (a) a one-shotdonestrobe and (b) a fixed-latency field the slave advertises. - Spot the corner bug. Given a CDC slave on a fixed-2-pad-cycle bus that passes at a 2:1 clock ratio, explain why it can fail at a 4:1 ratio and what single RTL change makes it corner-independent.
- Write the latency-agnostic check. Sketch the assertion and the coverage cross that together prove a design does not depend on a fixed peripheral latency.
11. Q&A
12. Key takeaways
PREADYexists to make latency-diverse peripherals legal, not merely fast. Before it, a peripheral that couldn't answer in one ACCESS cycle (CDC, flash, arbitrated resource) was an illegal APB2 slave — the pressure was correctness and reach, not throughput.- The pressure was real silicon: clock-domain crossings to slower peripheral clocks, register banks behind synchronizers, multi-cycle flash/EEPROM, and peripherals sharing a downstream resource — all have genuinely variable latency that no fixed two-cycle bus can absorb.
- Every pre-PREADY workaround penalised someone: slowing the whole bus punished the fast majority, fixed dead-cycle padding was wasteful and corner-fragile, and forcing slow peripherals onto AHB cost gates, verification, and schedule.
PREADYwon by being per-transfer and local — the slow slave extends only its own ACCESS phase while every fast peripheral still completes in two cycles, and it adds just one slave→manager bit to the untouched two-phase core.- It is level-based by design — sampled every cycle, no edge to miss, no count to maintain — which beats both a fixed-latency field (real latency isn't a fixed N) and a one-shot
donestrobe (an event you can miss). A sampled level is the more composable primitive. - The signature bug it prevents is a corner-dependent stale read from a design that baked in a fixed peripheral latency. Verify by sweeping latencies and asserting the design never samples before the real data-valid handshake — prove it is latency-agnostic.