Skip to content

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 before PREADY, 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 PREADY was 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). Understanding PREADY deeply 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 — PREADY is the exception each slow slave invokes for itself.
  • It is level-based, not event-based. PREADY is 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 while PREADY == 0 and completes when it samples PREADY == 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; PREADY just lets the ACCESS phase last longer than one cycle when the slave says so. Minimality was a design goal, and PREADY honours it.
A four-row comparison: rows 1 to 3 in red show clocking the whole bus down, fixed worst-case padding, and forcing the slow peripheral onto AHB, each labelled with its cost; row 4 in green shows PREADY flow control letting a fast peripheral complete in two cycles while the slow peripheral self-extends only its own ACCESS phase.
Figure 1 — the design-pressure comparison: three rejected pre-PREADY approaches versus PREADY self-pacing. Rows 1–3 (red) are the bad coping strategies: clock the whole bus down to the slowest peripheral (every fast register crawls); pad every transfer with fixed worst-case dead cycles (fast peripherals waste cycles, and the worst case shifts across corners); or forbid the slow peripheral on APB and push it onto AHB (gates, verification, and design time for a peripheral that never needed that bus). Row 4 (green) is PREADY flow control: a single back-pressure bit driven per transfer lets each peripheral self-pace, so the slow peripheral extends only its own ACCESS phase while every fast one still completes in two cycles. The figure makes the throughline visible — PREADY is the minimal per-transfer fix that accommodates the slow peripheral without penalising any fast one.

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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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.
endmodule

Two 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 optionWhat it costWho paysWhy PREADY beat it
Clock the whole bus down to the slowest peripheralEvery fast register/GPIO now runs at the slow peripheral's rate — subsystem throughput is hostage to one blockAll 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 cyclesFast peripherals waste cycles they never needed; and the "worst case" shifts across PVT / clock-ratio corners, so the count is either wasteful or wrongFast 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 AHBGate count, verification effort, and design time to put a trivial peripheral on a high-performance bus it never neededThe 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 controlOne extra slave→manager bit; manager must be able to hold the ACCESS phaseAlmost 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 PREADY line (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 samples PRDATA — 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 PREADY to extend the transfer, so the manager cannot know the data isn't ready; it samples blindly. This is exactly the failure mode PREADY'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 PREADY to 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 Mentor
    Snippet
    // 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.

Two stacked APB timing cases for the same CDC peripheral: the top red case pads a fixed two cycles and samples PRDATA before the synchronized data is valid at a slow corner, capturing a stale value; the bottom green case uses PREADY held low until the data is valid then driven high, so the manager samples valid data and the transfer self-extends correctly at every corner.
Figure 2 — the fixed-padding corner bug versus PREADY self-pacing. Top (bug, red): a fixed two-cycle pad tuned at one corner. The manager samples PRDATA on schedule, but at a slower peripheral-clock corner the synchronized data isn't valid until one cycle later, so the manager captures a stale value — the padding count is correct at the tuned corner and wrong here, with no PREADY to protect it. Bottom (correct, green): with PREADY the slave holds PREADY low until its synced data is genuinely valid, then drives PREADY high; the manager only completes when it samples PREADY=1, so the transfer self-extends by exactly the right amount and captures valid data at every corner. The figure teaches that a fixed latency is a corner-specific guess, whereas a level-based ready makes the transfer corner-independent.

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 PREADY is 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, PREADY already high), a single-wait transfer, a multi-wait transfer, and back-to-back transfers where a slow PREADY-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

  1. 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).
  2. 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.
  3. Defend level over event. Argue, in two sentences each, why a level-based PREADY is safer than (a) a one-shot done strobe and (b) a fixed-latency field the slave advertises.
  4. 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.
  5. 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

  • PREADY exists 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.
  • PREADY won 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 done strobe (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.