Skip to content

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), and addr_q (the address bits) at the clock edge, when HREADY is high.
  • Write path — in the data phase, if (sel_q && write_q) the register <= HWDATA.
  • Read pathHRDATA = mux(addr_q, registers) (combinational read of the addressed register).
  • ResponseHREADYOUT = 1, HRESP = OKAY (always — zero-wait, no error).
An AHB-Lite slave's blocks: address-phase capture (sel_q, write_q, addr_q), data-phase write/read paths, and HREADYOUT/HRESP response.
Figure 1 — the RTL building blocks. Address phase (sample & capture): sel_q = HSEL && a real HTRANS; register sel_q, write_q = HWRITE, and addr_q (at the clock edge, when HREADY high). Data phase (act & respond): write — if (sel_q && write_q) register <= HWDATA; read — HRDATA = mux(addr_q, registers); respond — HREADYOUT = 1, HRESP = OKAY (zero-wait). So the slave captures in the address phase and acts/responds in the data phase.

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 cycles
Cycle 0: write address phase (HWRITE=1, HADDR=A), captured. Cycle 1: write data phase (HWDATA=D captured); read address phase captured. Cycle 2: read data phase (HRDATA = D). HREADYOUT high throughout.Write addr phase → capture sel_q/write_q/addr_qWrite addr phase → cap…Write data: HWDATA→reg; read addr phase capturedWrite data: HWDATA→reg…HCLKHWRITE1000HADDRAAAAHWDATA0DDDHRDATA00DDHREADYOUTt0t1t2t3
Figure 2 — a write then a read. Cycle 0: a write's address phase (HSEL=1, HTRANS=NONSEQ, HWRITE=1, HADDR=A) — the slave captures sel_q=1, write_q=1, addr_q=A. Cycle 1: the write's data phase — HWDATA=D is captured into the register (sel_q && write_q); meanwhile a read's address phase (HWRITE=0, HADDR=A) captures sel_q=1, write_q=0, addr_q=A. Cycle 2: the read's data phase — HRDATA = the register (= D, just written) is muxed out. HREADYOUT is high throughout (zero-wait).

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

A two-stage pipeline (address phase capture → data phase act) with constant HREADYOUT=1/HRESP=OKAY, noting no multi-state FSM is needed.
Figure 3 — a zero-wait slave is a 2-stage pipeline, not a multi-state FSM. The only registered state is the captured address-phase context (sel_q, write_q, addr_q), latched each cycle when HREADY is high. The read/write actions and the response are combinational or single-cycle from that captured context. Because the slave always completes in one cycle, it drives HREADYOUT = 1 and HRESP = OKAY always — no waiting or error state. A slave needing wait states/errors adds states (later chapters).

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 combinationalHRDATA = (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_q aligns the data-phase action to the right transfer (correct — the pipeline) at the cost of the flops; using live HADDR/HWRITE in 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 HTRANS prevents spurious IDLE/BUSY accesses (correct) at the cost of the HTRANS check; using HSEL alone 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 alwaysno 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 HREADY is high, the slave registers: sel_q = HSEL && (HTRANS == NONSEQ || HTRANS == SEQ), write_q = HWRITE, and addr_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 decodes addr_q and writes HWDATA to 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 by addr_q.
  • Response. HREADYOUT = 1 and HRESP = OKAY always — the registers are accessed in one cycle (zero-wait).
  • The IDLE-qualification in action. Between accesses, the master drives HTRANS = IDLE while HADDR might still point to this peripheral. Because sel_q requires a real HTRANS, 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_q from the write's address phase (captured), and the next data-phase read uses addr_q from 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.

A summary card on building a simple AHB-Lite slave: capture the address phase, act in the data phase, constant response.
Figure 4 — a strong answer in one card: address phase (HREADY high) → register sel_q (HSEL && real HTRANS), write_q, addr_q; data phase → write HWDATA→reg if sel_q && write_q, read mux → HRDATA; zero-wait → HREADYOUT=1, HRESP=OKAY always (no real FSM). The senior point: register the address-phase context, act in the data phase, drive HREADYOUT high + OKAY for a zero-wait slave.

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.

  1. The structure. Describe the two-stage structure: address-phase capture and data-phase act.
  2. Register the context. Explain why you register the address-phase signals and use the registered values.
  3. Read the waveform. From Figure 2, trace the write's capture-then-write and the read's capture-then-mux.
  4. Qualify the select. Explain the sel_q = HSEL && real HTRANS computation and what it prevents.
  5. 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 pipelineaddress phase: register sel_q (= HSEL && a real HTRANS), write_q (= HWRITE), addr_q (gated on HREADY high); data phase: act on the captured context.
  • Act on the registered context — write HWDATA to the register if (sel_q && write_q); read combinationally mux the addressed register onto HRDATA (by addr_q).
  • Register the context, don't use live signals — the live HADDR/HWRITE have advanced to the next transfer in the data phase (off-by-one-phase bug otherwise).
  • Qualify the selectsel_q = HSEL && (NONSEQ or SEQ), not HSEL alone (which triggers spurious IDLE/BUSY accesses).
  • Zero-wait → no real FSMHREADYOUT = 1 and HRESP = OKAY always; 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.