AMBA AHB · Module 16
Read FSM
Designing the slave read-side FSM — IDLE to READ-DATA (the data phase, driving source[addr_q] onto HRDATA) to optional READ-WAIT (HREADYOUT low until a multi-cycle source is ready) to PRESENT or ERROR. Simpler than the write side because there's no incoming data to wait for — the slave produces the data from the captured address. HRDATA need only be valid in the cycle HREADYOUT is high, and any read side effect (read-to-clear, FIFO pop) must fire only on the completing legal cycle — never during a wait and never on an illegal access.
The write FSM (16.8) had to wait for its data (HWDATA arrives in the data phase). The read FSM is the mirror — and simpler in one way, subtler in another. Simpler: a read has no incoming data to wait for; the slave produces the data, driving the captured source onto HRDATA. Subtler: a read can have side effects — read-to-clear registers, FIFO pops (16.2) — and those side effects, like the write commit, must fire only on the completing cycle. The read-side logic, as an FSM: IDLE → on a captured real read, READ-DATA (the data phase, drive source[addr_q] onto HRDATA) → if the source is multi-cycle, READ-WAIT (HREADYOUT low until ready) → PRESENT (valid HRDATA, HREADYOUT high) or, if illegal, ERROR (two-cycle ERROR — 16.6 — no meaningful data). The two rules: HRDATA need only be valid in the cycle HREADYOUT is high (during waits it can be anything — the master ignores it — 16.5); and any read side effect must be qualified by HREADYOUT high — fire only on the completing legal cycle, never during a wait or on an illegal access. This chapter builds the read FSM and that side-effect discipline.
1. What Is It?
The read FSM is the slave's read-side control — the states that produce read data on HRDATA and gate read side effects. Its parts:
- IDLE — waiting; no read in progress (
HREADYOUThigh). - READ-DATA — entered on a captured real read; the data phase where the slave drives
source[addr_q]ontoHRDATA. - READ-WAIT (optional) — if the source is multi-cycle (a slow memory), hold with
HREADYOUTlow until the data is ready. - PRESENT / ERROR — present valid
HRDATAon the completing cycle (HREADYOUThigh) and fire any side effect; or, if illegal, run the two-cycle ERROR (no real data); then back to IDLE.
So the read FSM is the slave's read producer. Unlike the write (which consumes incoming HWDATA), the read produces outgoing HRDATA — driving the captured source (source[addr_q] — a register, SRAM read data, status; 16.5) onto the read-data bus. Because the slave produces the data, there's no incoming data to wait for — making the read simpler than the write. The FSM waits only if the source itself is multi-cycle (READ-WAIT for a slow memory). The two rules that matter: (1) HRDATA need only be valid in the cycle HREADYOUT is high (the master samples only then — 16.5; during waits, HRDATA is don't-care); and (2) any read side effect (read-to-clear, FIFO pop) must be qualified by HREADYOUT high — firing only on the completing legal cycle, exactly once, never during a wait (which would fire it repeatedly) or on an illegal access. For a simple zero-wait read, the FSM collapses to "drive source[addr_q] onto HRDATA". So the read FSM is the read-produce-and-side-effect discipline. So it's how a read returns correctly.
2. Why Does It Exist?
The read FSM exists because the slave must produce read data aligned to the data phase (from the captured address), handle multi-cycle sources (wait states), and gate read side effects to the completing cycle — the read-side mirror of the write's concerns, minus the incoming-data wait.
The produce data from the captured address is the root: a read's job is to return the data at the addressed location. The address was captured in the address phase (addr_q — 16.7); the data must be driven on HRDATA in the data phase (16.5). So the read logic produces HRDATA = source[addr_q] — selecting the captured source onto the bus. Unlike a write, there's nothing to wait for from the master (no incoming data) — the slave has everything it needs (the captured address) and just produces the output. So the read FSM exists to produce the read data from the captured address, aligned to the data phase. So it's data production. So the read produces, not consumes.
The multi-cycle sources need waits drives READ-WAIT: while the slave doesn't wait for master data, it may wait for its own source — a multi-cycle memory (16.3) takes cycles to return rdata. So if the source isn't immediately available, the slave must insert wait states (HREADYOUT low — READ-WAIT) until the source produces the data, then present it. So the FSM needs a READ-WAIT state for slow sources. So it handles source latency. So multi-cycle sources need the state.
The read side effects need gating is the subtlety: some reads have side effects — read-to-clear registers (clear on read), FIFO pops (consume an entry — 16.2). These side effects are state changes — and like the write commit, they must fire exactly once, on the completing cycle. If a side effect fired during a wait (HREADYOUT low), it would fire every wait cycle — multiple clears/pops — corrupting state. And it must not fire on an illegal read (suppress). So the FSM must gate read side effects by HREADYOUT high (and !error) — the same discipline as the write commit. So the FSM exists to gate read side effects correctly. So it's side-effect control. So side effects need the completion gate. So the read FSM exists because: the slave must produce read data from the captured address, aligned to the data phase (the why — production, no incoming wait); multi-cycle sources need wait states (a READ-WAIT state — the source latency); and read side effects (RC, FIFO pop) must fire only on the completing legal cycle (gated by HREADYOUT high and !error — the side-effect discipline, mirroring the write commit). So the read FSM is the slave's read-produce-and-side-effect sequencer — producing valid HRDATA aligned to the data phase, handling slow sources with waits, and gating side effects to the completing cycle. So this chapter builds the read side's control. So reads return correctly.
3. Mental Model
Model the read FSM as a museum's "view the artifact" request. You hand in a request slip with the case number (the address phase). A moment later, at the viewing window, the curator shows you the artifact from that case (the data phase — HRDATA from source[addr_q]). If the artifact is in deep storage (a multi-cycle source), they put up a "retrieving" sign and make you wait, then show it. And for special artifacts — a "one-time-view" exhibit that gets sealed away after viewing (a read-to-clear), or a "take-a-sample" dispenser (a FIFO pop) — the sealing/dispensing happens only at the actual viewing, not while you're still waiting in line — and exactly once.
A museum with a request desk and a viewing window, a step apart (the pipeline). You hand in a request slip naming a case number at the request desk (the address phase — addr_q captured). A step later, at the viewing window, the curator shows you the artifact from that case (the data phase — the slave drives source[addr_q] onto HRDATA). Notice the difference from a delivery (a write): here nothing arrives from you — the curator produces the artifact from the museum's own collection. So there's nothing to wait for from your side — the curator has the case number and just fetches and shows. If the artifact is in deep storage (a multi-cycle source), the curator puts up a "retrieving — please wait" sign (READ-WAIT, HREADYOUT low) and makes you wait at the window until it's fetched, then shows it. You only trust what's at the window when the "now viewing" light is on (you only sample HRDATA when HREADYOUT is high) — while the "retrieving" sign is up, whatever's at the window doesn't count (during waits, HRDATA is don't-care). Now the special cases. Some artifacts are "one-time-view" exhibits — once shown, they're sealed away (a read-to-clear register — reading it clears it). Others are a "take-a-sample" dispenser — showing it dispenses one sample (a FIFO pop — reading consumes an entry). The crucial rule: the sealing or dispensing happens at the actual viewing — when the "now viewing" light comes on — not while you're still waiting in line (HREADYOUT low), and exactly once. If the curator sealed the exhibit every cycle you waited, they'd seal it before you even saw it, or multiple times — ruining it. So the side effect fires once, at the viewing. And if your slip names a forbidden case (an illegal read), the curator refuses — and doesn't seal or dispense anything.
This captures the read FSM: the request desk = the address phase (addr_q captured); the viewing window a step later = the data phase (HRDATA driven); the curator producing the artifact = the slave producing source[addr_q] (no incoming data); the "retrieving" sign = READ-WAIT (HREADYOUT low for a multi-cycle source); trusting only when the "now viewing" light is on = sampling HRDATA only when HREADYOUT high; the one-time-view exhibit sealed at viewing = a read-to-clear firing on the completing cycle; the dispenser giving one sample at viewing = a FIFO pop on completion; not sealing while you wait in line = gating the side effect by HREADYOUT high (not during waits); refusing a forbidden case = the ERROR path suppressing the side effect. Produce from the case number, wait if it's in deep storage, and seal/dispense only at the actual viewing, once.
Watch a read with a side effect fire only on the completing cycle, after a wait:
A read-to-clear fires the clear on the completing cycle
4 cyclesThe model's lesson: produce from the case number, wait if needed, and seal/dispense only at the actual viewing, once. In the waveform, HRDATA = V is driven from cycle 1, but the master samples it (and the clear fires) only on the completing cycle (cycle 2, HREADYOUT high) — the clear fires exactly once, not during the wait.
4. Real Hardware Perspective
In hardware, the read side is often combinational (drive HRDATA = source[addr_q]) with no explicit FSM; the READ-WAIT is the source's HREADYOUT logic (16.4); and read side effects are gated by HREADYOUT (and !error) — the mirror of the write-enable.
The combinational HRDATA drive: for a zero-wait read, there's no FSM — HRDATA = source[addr_q] is a combinational mux (16.5) selecting the captured source. The slave always drives HRDATA with the captured source's value; the master samples it only when HREADYOUT (via HREADY) is high. So the slave doesn't need to gate HRDATA itself (it's don't-care when not sampled) — it just drives the mux. So in hardware, a zero-wait read is a combinational mux. So it's simple. So no FSM for the common case.
The READ-WAIT is the source's HREADYOUT logic: for a multi-cycle source (a slow memory — 16.3), the "READ-WAIT state" is the HREADYOUT-low logic — the slave drives HREADYOUT low while the source produces the data, then high when ready (and HRDATA is valid). The master only samples HRDATA when HREADYOUT is high — so the intermediate (invalid) HRDATA during the wait is ignored. So in hardware, the read wait falls out of the HREADYOUT logic — no separate gating of HRDATA needed. So it's the HREADYOUT term again. So waits are integrated.
The read side effects gated like a write-enable: for a read with a side effect (read-to-clear, FIFO pop), the side effect is gated exactly like the write commit: clear_en = sel_q && !write_q && is_RC(addr_q) && HREADYOUT && !error; (and likewise pop_en for a FIFO). The HREADYOUT term ensures the side effect fires only on the completing cycle (not during a wait — which would fire it repeatedly); the !error term suppresses it on an illegal read; the !write_q ensures it's a read. So in hardware, a read side effect is a gated enable — the mirror of the write-enable (16.8). So in hardware, the read side is a combinational HRDATA mux (drive source[addr_q]), the READ-WAIT integrated into the HREADYOUT logic, and read side effects gated by HREADYOUT && !error (mirroring the write-enable). The side-effect gating is the piece to get right (the rest is combinational). So in hardware, gate read side effects to completion. So that's the read-side crux.
5. System Architecture Perspective
At the system level, the read FSM is the data-source side of the slave — where the system's state is observed — and its correctness (valid data on completion, side effects gated to completion) is what makes reads reliable and side-effecting reads safe.
The observation point: the read is how the system observes a slave's state — reading a register's value, a memory location, a status. So the read FSM is the observation mechanism — the counterpart to the write's mutation. So at the system level, the read produces the observed value. So it's the system's read-out. So reads are observation.
The read reliability: the read's validity rule (HRDATA valid when HREADYOUT high) ensures the observed value is correct — the master samples the right data, when it's ready. A read that presented stale/garbage data when HREADYOUT is high (e.g. a slow source whose data isn't actually ready) would corrupt the observation. So the read FSM's valid-on-completion property is what makes reads reliable. So it's read correctness. So validity-on-completion matters.
The side-effecting read safety: the side-effect gating is a safety property for side-effecting reads. A read-to-clear register, a FIFO pop — these change state on read, and the change must be exactly once, on a real completing read. If a side effect fired multiple times (during waits) or spuriously (on an idle/illegal access), the system would lose data (extra pops), clear flags prematurely, corrupt the FIFO. So the completion-gating (and the qualified select from 16.7) makes side-effecting reads safe — the consume-on-read semantics are honored exactly. So at the system level, the read FSM is the observation side of the slave (where state is read out), its valid-on-completion property makes reads reliable (the master observes the right value when ready), and its side-effect gating makes side-effecting reads safe (read-to-clear/FIFO-pop fire exactly once, on a real completing read — honoring consume-on-read semantics, no lost data). So the read FSM is where the system's state is observed correctly and side-effecting reads behave exactly — the read-out counterpart to the write's commit. So produce valid data on completion, and gate side effects to the completing legal cycle.
6. Engineering Tradeoffs
The read FSM embodies the produce-from-capture, valid-on-completion, gate-side-effects design.
- Combinational read vs registered (pipelined) read. A combinational
HRDATAmux has low latency but a long path (16.5); a registered read closes timing at the cost of +1 latency (a wait). Trade frequency for latency. - Drive HRDATA always vs gate it. Driving
HRDATA = source[addr_q]always (don't-care when not sampled) is simplest; gating it (e.g. to 0 when not selected) helps the interconnect's AND-OR (16.5). Drive 0 when unselected (for the interconnect), else drive the source. - Side effect on completion vs eagerly. Gating side effects by
HREADYOUT && !error(completion, legal) is correct (exactly once); firing them eagerly (during a wait) fires them repeatedly (corrupts). Always gate to completion. - Qualified side effect vs unqualified. Qualifying side effects by the qualified select (16.7) prevents idle-cycle firing; unqualified spuriously clears/pops. Always qualify (and gate to completion).
The throughline: the read FSM is the slave's read-producer — IDLE → READ-DATA (the data phase, drive source[addr_q] onto HRDATA) → optional READ-WAIT (HREADYOUT low for a multi-cycle source) → PRESENT or ERROR. Unlike the write, the read produces outgoing data (no incoming wait), making it simpler. Two rules: HRDATA need only be valid when HREADYOUT is high (the master samples only then — 16.5); and any read side effect (read-to-clear, FIFO pop) must fire only on the completing legal cycle (gated by HREADYOUT && !error, qualified by sel_q — exactly once, never during a wait or illegally). The side-effect rule is the exact mirror of the write's commit condition (16.8). It's the observation side of the slave — making reads reliable and side-effecting reads safe.
7. Industry Example
Build the read side for a peripheral with a plain register, a status register, a read-to-clear register, and a RX-FIFO — across wait states.
A peripheral has CTRL (plain RW, readable), STATUS (RO), IRQ (read-to-clear), and RXDATA (read pops the RX-FIFO), with the FIFO sometimes needing a wait.
- CTRL / STATUS (plain reads), zero-wait. Reading CTRL or STATUS: the slave drives
HRDATA = source[addr_q](the register/status value) combinationally;HREADYOUThigh; the master samples it. No side effect. Simple. - IRQ (read-to-clear). Reading IRQ returns its value and clears it (read-to-clear). The clear is gated:
clear = sel_q && !write_q && (addr_q == IRQ) && HREADYOUT && !error— fires exactly once, on the completing cycle. So the master gets IRQ's value, and IRQ clears — once. - RXDATA (FIFO pop), with a wait. Reading RXDATA returns the next RX byte and pops the FIFO. If the FIFO read path is multi-cycle, the slave inserts a wait (
HREADYOUTlow). Critically, the pop is gated:pop = sel_q && !write_q && (addr_q == RXDATA) && HREADYOUT && !error— so the FIFO pops only on the completing cycle, once. If the pop omittedHREADYOUT, the FIFO would pop every wait cycle — losing received bytes. The gate ensures one pop per read. - Illegal read (write-only or reserved). A read of a write-only location or a reserved address sets the error condition → the slave runs the two-cycle ERROR, drives no meaningful data, and suppresses any side effect (
!error). - The validity rule. For all reads,
HRDATAis valid in the cycleHREADYOUTis high (the master samples only then); during any waits,HRDATAis don't-care.
The example shows the read FSM across plain and side-effecting reads: plain reads (CTRL/STATUS) just drive the source; the read-to-clear (IRQ) and FIFO-pop (RXDATA) side effects are gated by HREADYOUT && !error (fire exactly once, on completion — the FIFO pop especially depends on this across its wait); illegal reads error and suppress. The completion-gating makes every read — plain, clear-on-read, pop-on-read — behave correctly. This is the read side done right. This is reliable, safe reads.
8. Common Mistakes
9. Interview Insight
The read FSM is a practical RTL interview topic — the produce-not-consume simplicity, the validity rule, and especially the side-effect-on-completion gating are the signals.
The answer that lands gives the produce-not-consume simplicity and the side-effect gating: "The slave's read side is the mirror of the write side, but simpler in one way and subtler in another. Simpler: a read has no incoming data to wait for — the slave produces the data, driving the captured source onto HRDATA. So from idle, a captured real read moves to a read-data state in the data phase, where it drives source-at-addr_q onto HRDATA; if the source is multi-cycle, like a slow memory, it waits with HREADYOUT low; then it presents the data. For a simple zero-wait read it's just a combinational mux. The first rule is that HRDATA only needs to be valid in the cycle HREADYOUT is high — that's when the master samples it; during waits it's don't-care. The subtler part is read side effects. Some reads change state: a read-to-clear register clears on read, a FIFO read pops the FIFO. Those side effects, exactly like the write commit, must fire only on the completing cycle — gated by HREADYOUT high, and qualified by a real select and no error. If you fired a FIFO pop on just select-and-read, without the HREADYOUT term, a waited read would pop the FIFO every wait cycle, losing entries. And if you gated on raw HSEL instead of the qualified select, an idle cycle would spuriously pop. So the side effect must be select-qualified, read, side-effecting, HREADYOUT high, and not-error — fire exactly once, on a real completing legal read. That's the exact mirror of the write's commit condition. Plain reads are unaffected by all this — re-driving HRDATA during waits is harmless — so the bug only bites side-effecting reads when they wait, which is why it hides in simple tests." The produce-not-consume simplicity, the validity rule, and the side-effect-on-completion gating are the senior signals.
10. Practice Challenge
Build and reason from the read FSM.
- The states. Describe the read FSM states (IDLE, READ-DATA, READ-WAIT, PRESENT/ERROR) and why it's simpler than the write FSM.
- Produce not consume. Explain how the read produces
HRDATAfrom the captured source (no incoming data wait). - Read the waveform. From Figure 2, explain how the read-to-clear fires only on the completing cycle (after a wait), and when
HRDATAis valid. - Side-effect gating. State the gated side-effect condition (
sel_q && !write_q && <side-effecting> && HREADYOUT && !error) and explain each term; relate it to the write commit. - Why it hides. Explain why the side-effect bug is invisible for plain reads and only bites side-effecting reads that wait.
11. Key Takeaways
- The read FSM produces read data: IDLE → READ-DATA (drive
source[addr_q]ontoHRDATA) → optional READ-WAIT (HREADYOUTlow for a multi-cycle source) → PRESENT or ERROR. - It's simpler than the write — no incoming data to wait for (the slave produces the data); it only waits for its own multi-cycle source.
HRDATAneed only be valid whenHREADYOUTis high — the master samples only then; during waits it's don't-care (no special handling needed).- Read side effects (read-to-clear, FIFO pop) fire only on the completing legal cycle — gated by
HREADYOUT && !error, qualified bysel_q: exactly once, never during a wait (repeated) or illegally/idle (spurious). - It's the exact mirror of the write commit — state changes happen once, on completion. Plain reads are unaffected (re-driving
HRDATAin waits is harmless), so the side-effect bug hides until a side-effecting read waits. - It's the observation side of the slave — making reads reliable (valid-on-completion) and side-effecting reads safe (consume-on-read honored exactly).
12. What Comes Next
You now can build both the write and read sides. The next chapter assembles a full control FSM for the most complex slave — the bridge:
- Bridge FSM (RTL) (next) — implement the AHB-to-APB bridge control FSM in RTL (combining capture, write/read, wait, and response across two protocols).
- Reusable AHB RTL Templates — drop-in, parameterised AHB RTL building blocks.
To revisit the write side this mirrors, see Write FSM; for the read-to-clear/FIFO side effects, see Register Bank Slave; for the read-data path, see HRDATA Muxing.