AMBA APB · Module 6
Read Wait States
Why reads specifically stall — a register across a clock domain, a FIFO read, or a fetch from a slow backing store — and how the subordinate holds PREADY low while the source is not ready, with PRDATA don't-care until completion.
A generic wait state is a subordinate saying "not yet." A read wait state is more specific: the subordinate is saying "the answer has not arrived yet" — the value the manager asked for is still travelling out of a slow source. This chapter is about why reads stall and what PRDATA is doing while they do. The single idea to carry: a read inserts wait states because the data source is slow to produce the value, the subordinate holds PREADY low until that value is ready, and PRDATA is in-transit don't-care for every wait cycle and real on exactly one — the completion edge. The frozen-access machinery is identical to any wait; the reason and the PRDATA behaviour are what make a read wait read-specific.
1. What problem is being solved?
The problem is reading a value out of a source that cannot present it on the first access cycle — and doing so without the manager knowing or caring how slow that source is.
On a read, the data does not exist at the manager; it lives somewhere inside the subordinate, and that "somewhere" is not always a flop one combinational hop from PRDATA. It might be:
- a register in another clock domain, reachable only through a synchronizer that costs cycles before the value is safe to sample;
- a FIFO or peripheral whose addressed entry needs a cycle to be driven onto its read-data port;
- a location in a slower backing store that the read itself must fetch — the access launches the lookup, and the result returns several cycles later.
In every case the manager has already done its part — it presented PADDR with PWRITE low and asserted PENABLE. The value simply is not back yet. APB's answer is the wait state: the subordinate holds PREADY low, the access is held in place, and PRDATA stays meaningless until the source delivers. The problem being solved is matching a fixed-shape read to an any-speed data source, and the lever is exactly one bit — PREADY — held low until the answer arrives.
2. Why the previous model is not enough
Module 4's multi-cycle transfer taught the mechanism of a wait state in full: PREADY held low stretches one access, PENABLE stays high, SETUP is never re-entered, and every access-defining signal is frozen until the single completion edge. That mechanism is correct for reads and writes alike — and this chapter does not restate it. What Module 4 deliberately left general is the part that is read-specific, and that is what we drill here:
- Why a read stalls at all. A write stalls because the subordinate is slow to accept data; a read stalls because the subordinate is slow to produce it. The data source — a synchronizer, a FIFO, a triggered fetch — is the thing buying time, and that source-side latency is the read's own reason for waiting. Module 4 said "the subordinate isn't ready"; here we say why the read source isn't ready.
- What
PRDATAdoes during the wait. On a write the held signals arePADDR/PWDATA, and they are driven by the manager, so "stable" is the rule. On a read the interesting held signal isPRDATA, driven by the subordinate, and it is not meaningful during the wait — it is in-transit don't-care, because the value literally has not arrived. The wait cycles are precisely the cyclesPRDATAmust be ignored. - Where the value becomes real. The whole reason a read waits is to give the source time to deliver a value the manager can trust on the completion edge. The completion edge is not just "the write commits"; for a read it is "the answer is finally here" — the one cycle
PRDATAis valid and captured.
So the layer to add is not the wait-state machine — you have that. It is the read-specific causation (slow source → hold PREADY) and the read-specific data behaviour (PRDATA don't-care through the waits, valid once).
3. Mental model
The model: a read wait state is the courier still in transit. You posted a request (PADDR, PWRITE low), the bus is held open, and PREADY low is the courier saying "still on the road." Whatever is in your hands right now (PRDATA during the wait) is not the parcel — it is empty road. The parcel is real only at the doorstep, the instant the courier hands it over: the completion edge.
The model has a read-specific shape Module 4's "pause button" did not stress:
- The hold is a held-open question, not a held-back answer. The access is frozen —
PADDRandPWRITEstable,PENABLEhigh — because the subordinate must still be looking at the same read request when its source finally delivers. Change the address mid-wait and the courier delivers the wrong parcel. - What you see during the wait is empty road.
PRDATAduring a wait cycle is don't-care — undriven-meaningfully, or holding stale bits, orX. It is defined to be ignored. The single most common read bug is treating empty road as the parcel: samplingPRDATAon a wait cycle. - The doorstep is one instant. When the source is ready, the subordinate drives the value onto
PRDATAand raisesPREADYin the same cycle. That single cycle is the only onePRDATAis real, and the only one the manager captures.
4. Real SoC / hardware context
In silicon, a read wait state is almost always source latency surfacing on the bus. The subordinate's read mux is still combinational and fast; what is slow is the thing feeding the mux. Three patterns dominate.
Clock-domain crossing. A status register lives in a peripheral clocked separately from PCLK. Reading it cleanly means passing the value (or a "value valid" handshake) through a synchronizer — two flop stages minimum — before the APB side may sample it. Those stages are wait cycles: the subordinate holds PREADY low until the synchronized value lands, then drives PRDATA and completes.
FIFO / peripheral read port. Popping a FIFO or reading a RAM-backed register file typically registers the read address and presents data one cycle later. The first access cycle launches the read; PRDATA is not valid yet, so the subordinate inserts one wait, then completes with the entry on PRDATA.
Triggered fetch. Some reads are actions: reading a register kicks off a fetch from a slower memory or an off-bus resource. The access cannot complete until the fetch returns, so the subordinate holds PREADY low for the fetch latency, then drives the returned value and raises PREADY once.
In all three, the manager's contract is unchanged from Module 4 — hold the access frozen, sample PREADY each cycle, capture only at completion. What changes is which PRDATA the manager must distrust: every wait cycle's.
// Slow-READ subordinate: holds PREADY low while its data source is not ready,
// then drives PRDATA from the (now-ready) source and completes for one cycle.
// Teaching sketch — read path only, not a full slave.
//
// `src_valid` rises when the slow source (synchronizer / FIFO / fetch) has the
// value; `src_data` is that value. While selected in ACCESS for a read but the
// source is not yet valid, we hold PREADY low (a wait). PRDATA is don't-care on
// every wait cycle and only meaningful on the completion cycle below.
logic pready;
logic [31:0] prdata;
wire access_read = psel && penable && !pwrite; // in ACCESS, this is a read
always_ff @(posedge pclk or negedge presetn) begin
if (!presetn) begin
pready <= 1'b0;
prdata <= '0;
end else if (access_read && !pready) begin
if (src_valid) begin
prdata <= src_data; // value has arrived — present it on the bus
pready <= 1'b1; // raise PREADY for the single completion cycle
end else begin
pready <= 1'b0; // source not ready -> insert a read wait state
// (PRDATA left don't-care; manager must ignore it)
end
end else begin
pready <= 1'b0; // completion passed or bus idle -> re-arm
end
endTwo facts make this read-specific and correct. First, pready rises only in the cycle src_valid is true, and prdata is loaded in that same edge — so PRDATA and PREADY go valid together, exactly as the completion contract demands; nothing the manager reads on a wait cycle is the answer. Second, the wait length is owned entirely by the source's src_valid: a one-cycle FIFO produces one wait, a two-flop synchronizer two or three, a deep fetch many — and the manager's behaviour is identical for all of them. The subordinate's only job is to not lie: keep PREADY low and PRDATA meaningless until the value is genuinely back.
5. Engineering tradeoff table
Letting a read insert wait states driven by its source is a deliberate, minimal design. Each property trades a capability APB does not need for the simplicity it does.
| Read-wait property | What it gives up | What it buys | Why it is correct for APB |
|---|---|---|---|
Source latency surfaces as PREADY low | A fixed, known read latency | One bus serves instant and slow sources | CDC, FIFO, and fetch reads share identical timing |
PRDATA don't-care during waits | A continuously-valid read bus | The source can deliver late without lying early | The manager has one trustworthy sample point |
Value + PREADY raised together | Decoupled data/ready timing | PRDATA is real exactly when PREADY says | No race between "ready" and "data present" |
| Wait length owned by the source | Manager control over read duration | Any-speed source on one bus, no side channel | Only the source knows when its value is ready |
| Access frozen across waits | Re-issuing or pipelining the read | The same request is answered, once | The read never changed target — only its return leg |
The throughline: a read spends fixed latency and read-bus continuity to buy a flow where the slow part — the data source — is allowed to be slow without changing anything the manager does. The completion edge is the single landmark where the answer is both present and announced.
6. Common RTL / waveform mistakes
7. Interview framing
When an interviewer asks "why would an APB read need wait states?" the weak answer is "because the subordinate isn't ready" — true but generic, and it could describe a write. The strong answer is read-specific: a read waits because its data source is slow to produce the value.
Lead with the cause, with examples: the addressed value lives across a clock domain and must cross a synchronizer, or it comes from a FIFO/peripheral that needs a cycle to present data, or the read triggers a fetch from a slower store. Then state the data behaviour: while the source is not ready the subordinate holds PREADY low, and PRDATA is don't-care on every one of those wait cycles — the manager must ignore it. Close with the completion rule and the depth point: the subordinate drives PRDATA and raises PREADY in the same cycle, so the value is real on exactly the completion edge — and the access machinery (frozen PADDR/PWRITE, PENABLE high, one completion edge) is identical to a generic wait, but the reason and the PRDATA-during-wait rule are what make it a read wait. Volunteering "the wait length is owned by the source — a synchronizer gives two or three, a FIFO one, a fetch many — and the manager's behaviour is the same for all of them" shows you think about where latency actually comes from.
8. Q&A
9. Practice
- Name the source. For each of the three causes (CDC register, FIFO read, triggered fetch), state in one sentence why the value is not ready on the first access cycle and how many waits you would expect.
- Mark don't-care. On a three-wait read waveform, label
PRDATAon every cycle — say which cycles are don't-care and which one is valid, and why. - Place completion. Given
PREADYrises on the third access cycle, identify the completion edge and state what the manager captures and on which signal. - Find the bug. A subordinate raises
PREADYone cycle beforesrc_validis true. Show whatPRDATAthe manager captures and why the read returns wrong data. - Distinguish wait from error. A read holds
PREADYlow for five cycles, then completes withPSLVERRlow. State why this is a normal slow read and not an error, and where an error would actually appear.
10. Key takeaways
- A read waits because its data source is slow to produce the value — a register across a clock domain (synchronizer latency), a FIFO/peripheral that needs a cycle, or a read that triggers a fetch. This is the read-specific reason a generic wait never names.
PRDATAis don't-care on every wait cycle — the value is still in transit — and valid on exactly one cycle, the completion edge. The manager must ignorePRDATAuntil then.- The subordinate drives
PRDATAand raisesPREADYin the same cycle. The value is real precisely whenPREADYsays it is; they never rise apart. - The access machinery is identical to any wait state —
PADDR/PWRITEfrozen,PENABLEhigh, one completion edge — so this chapter adds the cause and thePRDATAbehaviour, not new wait mechanics. - The wait length is owned by the source. A FIFO gives one wait, a synchronizer two or three, a deep fetch many — and the manager's behaviour is the same for all of them.
- A long
PREADY-low stretch is backpressure, not an error. Slow reads are normal; read errors are signalled byPSLVERR, sampled only at completion — never inferred from how long the read waited.