AMBA AHB · Module 16
A Simple AHB-Lite Slave
Building a minimal, correct AHB-Lite slave from scratch — the capture-then-act structure: in the address phase, capture the qualified select (HSEL && a real HTRANS), the write flag, and the address into registers; in the data phase, act on the captured context — write HWDATA to the addressed register, or mux the addressed register onto HRDATA. Zero-wait (HREADYOUT high, HRESP OKAY), no FSM needed. The foundation every richer slave extends.
This opens Module 16 — AHB RTL Design by building the most fundamental block: a minimal, correct AHB-Lite slave, from scratch, in RTL. Chapter 12.6 gave the slave-design rules (qualify with HTRANS, register address-phase, respond in data-phase, two-cycle ERROR); this chapter constructs the actual RTL, the simplest version: a small register-backed slave with zero wait states. The structure is a two-stage pipeline matching the AHB protocol. In the address phase, when HREADY is high, the slave registers the address-phase context: the qualified select sel_q = HSEL && (HTRANS is NONSEQ or SEQ), the write flag write_q = HWRITE, and the relevant address bits addr_q. In the data phase, it acts: for a write, if (sel_q && write_q) the register <= HWDATA; for a read, HRDATA = mux of the registers by addr_q. Because it's zero-wait, it drives HREADYOUT = 1 and HRESP = OKAY always — so it needs no real state machine, just the captured context. This chapter builds that slave step by step — the RTL realization of the slave-design rules, and the foundation for the richer slaves in the rest of the module.
1. What Is It?
A simple AHB-Lite slave is the minimal RTL that correctly implements the AHB-Lite slave protocol for a small register. Its parts:
- Address-phase capture — register
sel_q(the qualified select),write_q(=HWRITE), andaddr_q(the address bits) at the clock edge, whenHREADYis high. - Write path — in the data phase,
if (sel_q && write_q) the register <= HWDATA. - Read path —
HRDATA = mux(addr_q, registers)(combinational read of the addressed register). - Response —
HREADYOUT = 1,HRESP = OKAY(always — zero-wait, no error).
So a simple AHB-Lite slave is the direct RTL realization of the slave-design rules (chapter 12.6): the qualify rule becomes the sel_q computation; the register-address-phase rule becomes the sel_q/write_q/addr_q flops; the act-in-data-phase rule becomes the write/read logic using the registered context; and the respond rule becomes the HREADYOUT/HRESP drivers (here trivially constant for a zero-wait slave). The whole slave is two stages — capture (address phase), act (data phase) — with the only registered state being the captured context. So building it is: write the capture flops, the write/read paths off the captured context, and the (constant) response. So a simple AHB-Lite slave is this minimal, correct, two-stage RTL — the building block of the module.
2. Why Does It Exist?
This minimal slave exists as the starting point for RTL slave design — it captures the essential structure (the two-stage capture-then-act pipeline) in the simplest form, so you understand the core before adding complications (wait states, errors, banks, memory), which the later chapters layer on.
The essential structure is what it teaches: every AHB slave — no matter how complex — has the same core: capture the address-phase context, then act and respond in the data phase. The simple slave shows this cleanly, without distractions. So building it first establishes the fundamental pattern that all the richer slaves (register bank, memory, with wait states/errors) extend. So the simple slave exists to teach the core pattern. So it's the foundation.
The zero-wait simplification keeps it minimal: by making the slave always complete in one cycle (zero-wait), the response is trivial (HREADYOUT = 1, HRESP = OKAY always) — no wait-state logic, no error logic, no multi-state FSM. So the slave reduces to just the capture flops + the write/read paths — the bare minimum. This lets you focus on the essential (capture and act) without the additional (wait states, errors). So the zero-wait simplification exists to isolate the core. So it's deliberately minimal.
The reason it's the basis for extension is that the later chapters add to it: the register bank (16.2) parameterizes the registers; the memory slave (16.3) adds wait states (and bigger storage); HREADYOUT generation (16.4) makes the response non-trivial (wait states); HRDATA muxing (16.5) details the read path; HRESP generation (16.6) adds errors (the two-cycle ERROR); the write/read FSMs (16.8/16.9) formalize the paths. All of these build on the simple slave's structure (capture + act). So the simple slave exists as the base that the module extends. So this minimal slave exists because: it captures the essential capture-then-act structure that all slaves share (the core); the zero-wait simplification reduces it to the bare minimum (capture + act, trivial response — isolating the essential); and it's the basis the rest of the module extends (register bank, memory, wait states, errors, FSMs). So building it first teaches the fundamental slave pattern cleanly, before the complications. So it's the RTL foundation of the module.
3. Mental Model
Model the simple slave as a single-window service counter with a "take a ticket, then come back" flow — when you arrive (address phase), the clerk writes your request on a ticket (captures sel_q, write_q, addr_q) — which service, deposit or withdrawal, and your account number — but doesn't serve you yet; on your next step (data phase), the clerk reads the ticket and does the service (writes your deposit, or hands you your balance from the file) and says "done" immediately (HREADYOUT = 1, HRESP = OKAY) — because this simple counter always serves in one step.
A single-window service counter (the slave) has a disciplined two-step flow. When you arrive (the address phase), the clerk doesn't serve you immediately — they write your request on a ticket (capture the context): which service you want (write_q — deposit or withdrawal), your account number (addr_q — which register), and that you're a real customer with a real request (sel_q — selected and a real transfer, not just browsing). On your next step (the data phase), the clerk reads the ticket and does the service from the captured details: if it's a deposit, they file your money (write HWDATA to the register); if it's a withdrawal, they hand you your balance from the file (read the register onto HRDATA). And because this is a simple, fast counter that always serves in one step, the clerk says "done" (HREADYOUT = 1) and "all good" (HRESP = OKAY) immediately — there's no "please wait" (no wait states) and no "sorry, can't" (no errors). So the flow is: write the ticket (capture the request), serve from the ticket (act on the captured context), say done (constant response). A busier counter might say "please wait" (wait states) or "can't do that" (errors), but this simple one always serves immediately.
This captures the simple slave: arriving and the clerk writing the ticket = the address-phase capture (sel_q, write_q, addr_q); which service + account number = the write flag and address; a real customer with a real request = sel_q = HSEL && real HTRANS; serving from the ticket on the next step = the data-phase action from the captured context; filing the deposit = the write; handing the balance = the read mux; saying done immediately = HREADYOUT = 1, HRESP = OKAY. Write the ticket (capture), serve from it (act), say done (respond) — always in one step.
Watch a write then a read through the slave:
Simple AHB-Lite slave: write then read
4 cyclesThe model's lesson: write the ticket (capture), serve from it (act), say done (respond) — always in one step. In the waveform, the write captures then writes HWDATA, the read captures then muxes the register onto HRDATA, and HREADYOUT is high throughout (zero-wait).
4. Real Hardware Perspective
In hardware, the simple slave is a couple of small always-blocks: a sequential block capturing the address-phase context and (on a write) updating the register, and a combinational block muxing the read data — plus the constant response drivers.
The address-phase capture block is sequential (clocked): on each rising HCLK edge, when HREADY is high (the bus is advancing), it registers sel_q <= HSEL && (HTRANS == NONSEQ || HTRANS == SEQ), write_q <= HWRITE, and addr_q <= HADDR[relevant bits]. (Gating the capture on HREADY high ensures it samples the correct address phase even if a previous transfer inserted wait states — chapter 13.7.) So in hardware, this is a clocked block latching the qualified context. So it's a few flops.
The write and read paths: the write is part of the sequential block — if (sel_q && write_q) the register <= HWDATA (in the data phase, HWDATA is valid, and the registered sel_q/write_q tell it this is a real write to this slave). The read is combinational — HRDATA = (addr_q selects register X) ? register_X : ... (a mux of the registers selected by the registered address). So the read returns the addressed register's current value in the data phase. (For a write-then-read of the same register in back-to-back cycles, the read sees the just-written value if the write updated the register on the same edge — Figure 2 shows this.) So in hardware, the write updates the register (sequential), and the read muxes it out (combinational). So those are the two paths.
The constant response: because the slave is zero-wait, HREADYOUT is tied high (always ready — it completes every transfer in one cycle) and HRESP is tied to OKAY (no errors). So there's no response logic — just constants. So in hardware, the response is trivial. So the whole slave is: a sequential capture+write block, a combinational read mux, and constant HREADYOUT/HRESP. No multi-state FSM is needed (the "state" is just the one-cycle pipeline register). So in hardware, the simple slave is minimal — a handful of flops and a mux. The richer slaves (later chapters) add to this: wait-state logic makes HREADYOUT non-constant (16.4); error logic makes HRESP non-constant (16.6); bigger storage (memory, 16.3); explicit FSMs (16.8/16.9). So the hardware reality: a tiny two-block slave (capture+write, read-mux) with constant response — the minimal correct AHB-Lite slave. So building it is straightforward once you have the structure.
5. System Architecture Perspective
At the system level, the simple slave is the template every AHB slave follows — so getting its structure right (capture-then-act, qualified select, registered context) establishes the correct pattern that scales to all slaves, and it's where the slave-design rules become concrete, reusable RTL.
The template for all slaves: every AHB slave in a system — memories, peripherals, control blocks — follows the same core structure as the simple slave: capture the address-phase context, act in the data phase, drive the response. So the simple slave is the template. Establishing it correctly (the qualified select, the registered context, the data-phase action) means every slave built from it inherits the correct protocol behavior. So at the system level, the simple slave is the pattern that ensures slave correctness across the system. So get the template right.
The rules-to-RTL realization: the simple slave is where the abstract slave-design rules (chapter 12.6 — qualify with HTRANS, register address-phase, etc.) become concrete RTL (sel_q, the capture flops, the data-phase logic). This concretization is what makes the rules implementable and verifiable: you can see the sel_q = HSEL && real HTRANS (the qualify rule), the capture flops (the register rule), the data-phase action (the act rule). So the simple slave bridges the design rules and the implementation. So at the system level, it's the concrete embodiment of the slave contract — the RTL that every compliant slave realizes. So it makes the rules real.
The reusability: because the structure is standard, the simple slave (and its richer descendants) can be reused — as a template (copy and customize the registers/behavior) or parameterized (a generic slave with configurable registers/storage, chapter 16.2/16.11). So slave RTL is not written from scratch each time; it's instantiated from the standard pattern. So at the system level, the simple slave establishes a reusable slave template — the basis for a library of AHB slave IP. So at the system level, the simple slave is the template every AHB slave follows (ensuring correct protocol behavior across the system), the concrete RTL realization of the slave-design rules (bridging design and implementation, making the rules verifiable), and the basis for reusable slave IP (template/parameterized, chapter 16.2/16.11). So building the simple slave correctly is foundational — it's the pattern from which all the system's slaves derive, and the RTL module builds out from it. So it's the cornerstone of AHB slave design in RTL. So the rest of the module extends this template.
6. Engineering Tradeoffs
The simple slave embodies the capture-then-act, minimal-RTL design.
- Register the address-phase context vs use live signals. Registering
sel_q/write_q/addr_qaligns the data-phase action to the right transfer (correct — the pipeline) at the cost of the flops; using liveHADDR/HWRITEin the data phase is wrong (they've moved to the next transfer). Always register. - Qualify the select vs use HSEL alone. Computing
sel_q = HSEL && real HTRANSprevents spurious IDLE/BUSY accesses (correct) at the cost of the HTRANS check; usingHSELalone is buggy. Always qualify. - Zero-wait (simple) vs wait states. A zero-wait slave is minimal (constant response, no FSM) at the cost of requiring single-cycle access; wait states (chapter 16.4) handle slow storage at more logic. Zero-wait for the simple case.
- Combinational read vs registered read. A combinational read mux returns data in the data phase (lowest latency) at the cost of the mux delay; a registered read would add a cycle. Combinational for a simple/fast slave.
The throughline: a simple AHB-Lite slave is a two-stage pipeline — in the address phase (when HREADY is high), register the qualified select sel_q = HSEL && (real HTRANS), the write flag write_q, and the address addr_q; in the data phase, act on the captured context — write HWDATA to the register if (sel_q && write_q), and mux the addressed register onto HRDATA for reads. Being zero-wait, it drives HREADYOUT = 1 and HRESP = OKAY always — no real FSM, just the captured context. It's the direct RTL realization of the slave-design rules (qualify, register, act, respond), the template every AHB slave follows, and the basis the rest of the module extends (register bank, memory, wait states, errors, FSMs).
7. Industry Example
Build a 4-register AHB-Lite peripheral slave.
A peripheral has 4 registers (CTRL, STATUS, DATA, CONFIG); build its AHB-Lite slave RTL.
- Address-phase capture. On each clock edge, when
HREADYis high, the slave registers:sel_q = HSEL && (HTRANS == NONSEQ || HTRANS == SEQ),write_q = HWRITE, andaddr_q = HADDR[3:2](the 2 bits selecting among the 4 registers). So it captures whether it's accessed by a real transfer, read or write, and which register. - Write path. In the data phase,
if (sel_q && write_q), the slave decodesaddr_qand writesHWDATAto the selected register:addr_q == 0 → CTRL <= HWDATA,== 2 → DATA <= HWDATA, etc. (STATUS might be read-only — see below.) - Read path. Combinationally,
HRDATA = (addr_q == 0) ? CTRL : (addr_q == 1) ? STATUS : (addr_q == 2) ? DATA : CONFIG— a mux of the 4 registers selected byaddr_q. - Response.
HREADYOUT = 1andHRESP = OKAYalways — the registers are accessed in one cycle (zero-wait). - The IDLE-qualification in action. Between accesses, the master drives
HTRANS = IDLEwhileHADDRmight still point to this peripheral. Becausesel_qrequires a realHTRANS, the slave does not write or read on the IDLE — no spurious access (e.g. it doesn't clear a read-to-clear STATUS bit on an idle cycle). The qualification protects it. - The registered-context in action. When the master does a write to CTRL then a read of DATA back-to-back, the data-phase write uses
addr_q/write_qfrom the write's address phase (captured), and the next data-phase read usesaddr_qfrom the read's address phase — each acts on its own registered context, not the live (next)HADDR. So the right registers are accessed. - A read-only register (preview of errors). If STATUS is read-only and the master writes it, this simple slave would just ignore the write (or write a no-op). A robust slave would return an ERROR (the two-cycle ERROR, chapter 16.6) — but that's an extension; the simple zero-wait slave here just OKAYs everything.
The example shows the simple slave built: address-phase capture (sel_q, write_q, addr_q), a write path (decode addr_q, write HWDATA), a read mux (HRDATA from the registers), and constant HREADYOUT/HRESP — with the qualification protecting against IDLE accesses and the registered context ensuring the right register is accessed. This is the minimal correct AHB-Lite slave in RTL. This is the build.
8. Common Mistakes
9. Interview Insight
Building a simple slave is a hands-on RTL interview topic — the capture-then-act structure, the registered context, and the qualified select are the signals.
The answer that lands describes the build concretely: "A minimal AHB-Lite slave is a two-stage pipeline matching the protocol. In the address phase, on each clock edge when HREADY is high, I register the address-phase context: the qualified select — sel_q = HSEL && (HTRANS is NONSEQ or SEQ) — the write flag write_q = HWRITE, and the address bits addr_q. I gate the capture on HREADY high so it samples the correct address phase even if a previous transfer inserted wait states. Then in the data phase, I act on the captured context: for a write, if (sel_q && write_q) I decode addr_q and write HWDATA into the selected register; for a read, I combinationally mux the addressed register onto HRDATA, selected by addr_q. Because this simple slave is zero-wait — it completes every access in one cycle — I drive HREADYOUT high and HRESP to OKAY always, so it needs no real state machine; the only registered state is the captured context. The two things I'm careful about: I register the address-phase signals and use the registered values in the data phase, never the live HADDR — which has advanced to the next transfer — and I qualify the select with HTRANS so I don't perform spurious accesses during IDLE or BUSY cycles when HSEL might be high. Wait states and errors would add an FSM and non-constant HREADYOUT/HRESP, but that's the next chapters." The capture-then-act structure, the registered context, and the qualified select are the senior signals.
10. Practice Challenge
Build and reason from the simple slave.
- The structure. Describe the two-stage structure: address-phase capture and data-phase act.
- Register the context. Explain why you register the address-phase signals and use the registered values.
- Read the waveform. From Figure 2, trace the write's capture-then-write and the read's capture-then-mux.
- Qualify the select. Explain the
sel_q = HSEL && real HTRANScomputation and what it prevents. - No FSM. Explain why a zero-wait slave needs no real state machine.
11. Key Takeaways
- A simple AHB-Lite slave is a two-stage pipeline — address phase: register
sel_q(= HSEL && a real HTRANS),write_q(= HWRITE),addr_q(gated onHREADYhigh); data phase: act on the captured context. - Act on the registered context — write
HWDATAto the registerif (sel_q && write_q); read combinationally mux the addressed register ontoHRDATA(byaddr_q). - Register the context, don't use live signals — the live
HADDR/HWRITEhave advanced to the next transfer in the data phase (off-by-one-phase bug otherwise). - Qualify the select —
sel_q = HSEL && (NONSEQ or SEQ), notHSELalone (which triggers spurious IDLE/BUSY accesses). - Zero-wait → no real FSM —
HREADYOUT = 1andHRESP = OKAYalways; the only registered state is the captured context. Wait states/errors add an FSM (later chapters). - It's the template every AHB slave follows — the direct RTL realization of the slave-design rules, and the basis the rest of the module extends (register bank, memory, wait states, errors, FSMs).
12. What Comes Next
You now can build a minimal correct AHB-Lite slave in RTL. The next chapters extend it:
- Register Bank Slave (next) — parameterize the slave into a configurable register bank.
- Memory Slave — an SRAM/memory slave with wait states.
- HREADYOUT Generation, HRDATA Muxing, HRESP Generation, Address/Control Capture, Write FSM, Read FSM, Bridge FSM, Reusable Templates — the richer slave RTL.
To revisit the slave-design rules this realizes, see AHB-Lite Slave Design; for HSEL qualification, see HSEL Generation.