Skip to content

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 PRDATA does during the wait. On a write the held signals are PADDR/PWDATA, and they are driven by the manager, so "stable" is the rule. On a read the interesting held signal is PRDATA, 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 cycles PRDATA must 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 PRDATA is 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 — PADDR and PWRITE stable, PENABLE high — 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. PRDATA during a wait cycle is don't-care — undriven-meaningfully, or holding stale bits, or X. It is defined to be ignored. The single most common read bug is treating empty road as the parcel: sampling PRDATA on a wait cycle.
  • The doorstep is one instant. When the source is ready, the subordinate drives the value onto PRDATA and raises PREADY in the same cycle. That single cycle is the only one PRDATA is real, and the only one the manager captures.
A structural diagram with three source boxes on the left — a clock-domain-crossing register through a synchronizer, a FIFO or peripheral read, and a read that triggers a fetch from a backing store — with arrows converging on a central rule block 'hold PREADY low until the value is ready', which flows down to a completion block where PRDATA is driven and PREADY is raised for one cycle.
Figure 1 — why an APB read source is slow, and the single rule all three causes feed. On the left, three distinct sources cannot present read data immediately: a register in another clock domain reached through a synchronizer's flip-flop stages, a FIFO or peripheral whose addressed entry needs a cycle to reach its read-data port, and a control read that triggers a fetch from a slower backing store. Three arrows converge on one rule — hold PREADY low while the value is not ready, during which PRDATA is don't-care. When the value finally arrives the subordinate drives it onto PRDATA and raises PREADY for the single completion cycle, where the manager captures it. The figure stresses that the cause differs but the mechanism is identical: the source sets the wait length, and completion is always one edge.

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.

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

Two 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.

An APB read timing diagram with rows PCLK, PSEL, PENABLE, PWRITE, PADDR, PRDATA, PREADY: IDLE, SETUP with PSEL high and PADDR valid and PWRITE low, then four ACCESS cycles with PENABLE high — three WAIT cycles with PREADY low and PRDATA don't-care, then one COMPLETE cycle with PREADY high where PRDATA becomes valid and is captured on the dashed completion edge.
Figure 2 — a multi-wait APB read walked against PCLK, stressing PRDATA's behaviour. PWRITE is held low across the transfer to mark a read. After one SETUP cycle that presents the read address on PADDR with PSEL high, the access phase spans four cycles: three WAIT cycles where PREADY is low because the read source is not ready, and a final COMPLETE cycle where PREADY rises. PADDR is held stable across every wait, and PENABLE stays high across all four access cycles. PRDATA is shown don't-care across IDLE, SETUP, and all three wait cycles — the value is still in transit — and becomes valid only on the single completion cycle, marked with a dashed line, where PSEL, PENABLE, and PREADY are all high and the manager captures it. The waveform makes the read-specific point: the access machinery is a generic wait, but PRDATA is meaningless until the one edge the slow source delivers.

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 propertyWhat it gives upWhat it buysWhy it is correct for APB
Source latency surfaces as PREADY lowA fixed, known read latencyOne bus serves instant and slow sourcesCDC, FIFO, and fetch reads share identical timing
PRDATA don't-care during waitsA continuously-valid read busThe source can deliver late without lying earlyThe manager has one trustworthy sample point
Value + PREADY raised togetherDecoupled data/ready timingPRDATA is real exactly when PREADY saysNo race between "ready" and "data present"
Wait length owned by the sourceManager control over read durationAny-speed source on one bus, no side channelOnly the source knows when its value is ready
Access frozen across waitsRe-issuing or pipelining the readThe same request is answered, onceThe 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

  1. 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.
  2. Mark don't-care. On a three-wait read waveform, label PRDATA on every cycle — say which cycles are don't-care and which one is valid, and why.
  3. Place completion. Given PREADY rises on the third access cycle, identify the completion edge and state what the manager captures and on which signal.
  4. Find the bug. A subordinate raises PREADY one cycle before src_valid is true. Show what PRDATA the manager captures and why the read returns wrong data.
  5. Distinguish wait from error. A read holds PREADY low for five cycles, then completes with PSLVERR low. 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.
  • PRDATA is 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 ignore PRDATA until then.
  • The subordinate drives PRDATA and raises PREADY in the same cycle. The value is real precisely when PREADY says it is; they never rise apart.
  • The access machinery is identical to any wait statePADDR/PWRITE frozen, PENABLE high, one completion edge — so this chapter adds the cause and the PRDATA behaviour, 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 by PSLVERR, sampled only at completion — never inferred from how long the read waited.