Wishbone · Module 4
ADR_O
A Wishbone address names a word, not a byte, and means nothing unless the strobe qualifies it: address width, decode, the master-offset split, and the stability obligation RULE 3.60 imposes.
Chapter 4.2 finished the two SYSCON signals. Everything from here is part of a transfer, and the address is where a transfer starts.
Chapter 2.2 covered address spaces and Chapter 3.5 covered decoding. This chapter is about the signal itself — and it contains one fact that surprises almost everyone the first time.
What does ADR_O represent, when may anyone trust it, and how wide is it actually?
1. Ownership and Qualification
ADR_O is a master output. The same wires arrive at a slave as ADR_I, per the interface-relative naming convention of Chapter 3.1 §2.
RULE 3.60 — "MASTER interfaces MUST qualify the following signals with [STB_O]: [ADR_O], [DAT_O()], [SEL_O()], [WE_O], and [TAGN_O]."
That word qualify carries the whole contract. The address is meaningful when — and only when — STB_O is asserted. Two consequences follow, and they point in opposite directions.
For a slave: never act on the address bus without a qualified transfer. A slave watching ADR_I alone sees a continuous stream of values, most of which are residue from previous transfers or whatever the master's register happens to hold.
For a master: the address must be stable for as long as the transfer is presented. Chapter 3.3 built a master that drives the bus from a latched copy precisely so this is structural rather than a promise the client has to keep.
Engineering consequence, not a rule. The specification does not say the master must drive zeros between transfers, and most do not. A waveform showing an address while STB_O is low is not a defect — it is an unqualified value, and reading significance into it is the mistake.
2. What the Address Does Not Do
It does not select a slave. This is worth stating flatly because the instinct is strong. A slave does not compare ADR_I against its own base and decide the transfer is for it — Chapter 3.4 §6 catalogued what goes wrong when it does: the base address ends up inside the slave, the slave cannot be instantiated twice, and it answers at unintended addresses whenever its comparison is incomplete.
Selection is a separate signal. The INTERCON decodes the address and asserts exactly one slave's STB_I — Chapter 3.5 §2. The address that reaches the slave alongside that strobe is a local offset, already masked.
So the same wires carry different information at different points in the fabric, and it is the only transformation in a Wishbone access:
| Where | What the address means |
|---|---|
Master ADR_O | A system address |
| INTERCON input | A system address, compared against the map |
| INTERCON output | A local offset, masked to the hit region |
Slave ADR_I | A local offset in the slave's own numbering |
3. How Wide Is It? — Granularity and the Missing Low Bits
Here is the fact that catches people.
The specification requires a core's datasheet to specify three things: port size (8, 16, 32 or 64 bits), granularity (8, 16, 32 or 64 bits) and maximum operand size. Maximum port size is 64 bits.
The address array's lower boundary is determined by the port size and granularity — and the specification's own example for a 32-bit port with 8-bit granularity is ADR_O(n..2).
Bits 1 and 0 are not present.
Why. With a 32-bit port and byte granularity there are four byte lanes, and SEL_O(3..0) says which of them participate. Byte selection is therefore already expressed — encoding it a second time in the low address bits would be redundant and, worse, ambiguous: what should a slave do if ADR_O[1:0] says byte 2 while SEL_O says lanes 0 and 1?
So the division of labour is:
| Carries | Signal |
|---|---|
| Which word | ADR_O, from bit 2 upward on a 32-bit byte-granular port |
| Which bytes within that word | SEL_O |
This is why Chapter 4.7 says byte selects are not "just the low address bits". They are the replacement for them.
4. RTL — The Address Path, End to End
// ─────────────────────────────────────────────────────────────────────────
// wb_adr_path — the three places a Wishbone address is handled, in one file
// so the transformation is visible.
//
// wb_adr_master drives a STABLE, QUALIFIED address (RULE 3.60)
// wb_adr_decode turns a system address into select+offset (Chapter 3.5)
// wb_adr_slave interprets the LOCAL offset only (Chapter 3.4)
//
// WORD-ADDRESSED PORT. Unlike Module 3's simplification, the master here
// presents a WORD address — the specification's ADR_O(n..2) form for a
// 32-bit port with byte granularity. Byte selection is SEL_O's job
// (Chapter 4.7), so the two low bits of a byte pointer do not appear.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_adr_master #(
parameter int unsigned BYTE_AW = 32, // byte-address width
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic go_i,
input logic [BYTE_AW-1:0] byte_adr_i, // ordinary pointer value
output logic cyc_o,
output logic stb_o,
// The WORD address: the low $clog2(DW/8) bits of the byte pointer are
// not carried. For DW=32 this is ADR_O(31..2) presented as [29:0].
output logic [BYTE_AW-1-$clog2(DW/8):0] adr_o,
input logic ack_i,
input logic err_i,
input logic rty_i
);
localparam int unsigned SHIFT = $clog2(DW / 8); // 2 for a 32-bit port
// Elaboration check: a byte-granular port must have a byte-multiple width.
initial begin
if ((DW % 8) != 0)
$fatal(1, "wb_adr_master: DW=%0d is not a multiple of 8", DW);
end
logic active_q;
logic [BYTE_AW-1-SHIFT:0] adr_q;
// The bus is driven from the LATCHED copy, never from byte_adr_i. That is
// what makes RULE 3.60's stability obligation structural rather than a
// rule the client must remember (Chapter 3.3).
assign cyc_o = active_q;
assign stb_o = active_q;
assign adr_o = adr_q;
always_ff @(posedge clk_i) begin
if (rst_i) begin
active_q <= 1'b0; // RULE 3.20
adr_q <= '0;
end else if (!active_q) begin
if (go_i) begin
// Drop the byte-select bits: they are SEL_O's information.
adr_q <= byte_adr_i[BYTE_AW-1:SHIFT];
active_q <= 1'b1;
end
end else if (ack_i || err_i || rty_i) begin
active_q <= 1'b0;
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_adr_decode — system WORD address → one-hot select + local WORD offset.
//
// Regions are expressed in BYTES in the parameters, because that is how an
// address map is written and read, and converted to word terms internally.
// Keeping the parameter in the units of the document it comes from is a
// small thing that prevents a whole class of factor-of-four errors.
// ─────────────────────────────────────────────────────────────────────────
module wb_adr_decode #(
parameter int unsigned BYTE_AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned NS = 3,
parameter logic [NS*32-1:0] BASE_FLAT =
{ 32'h4000_2000, 32'h4000_1000, 32'h4000_0000 },
parameter logic [NS*32-1:0] SIZE_FLAT =
{ 32'h0000_1000, 32'h0000_1000, 32'h0000_1000 }
) (
input logic [BYTE_AW-1-$clog2(DW/8):0] adr_i, // system WORD address
output logic [NS-1:0] sel_o, // one-hot
output logic [BYTE_AW-1-$clog2(DW/8):0] offset_o, // local WORD offset
output logic unmapped_o
);
localparam int unsigned SHIFT = $clog2(DW / 8);
localparam int unsigned WAW = BYTE_AW - SHIFT;
function automatic logic [31:0] base_of(input int unsigned i);
return BASE_FLAT[i*32 +: 32];
endfunction
function automatic logic [31:0] size_of(input int unsigned i);
return SIZE_FLAT[i*32 +: 32];
endfunction
// The same two values expressed in WORD units. Converting here, once and
// in a named place, is what keeps byte-valued parameters from being
// compared against a word address by accident.
function automatic logic [WAW-1:0] wbase_of(input int unsigned i);
return WAW'(base_of(i) >> SHIFT);
endfunction
function automatic logic [WAW-1:0] wmask_of(input int unsigned i);
return WAW'((size_of(i) - 32'd1) >> SHIFT);
endfunction
initial begin
for (int unsigned i = 0; i < NS; i++) begin
if (size_of(i) != (32'd1 << $clog2(size_of(i))))
$fatal(1, "wb_adr_decode: region %0d size %0h not a power of two",
i, size_of(i));
if ((base_of(i) & (size_of(i) - 32'd1)) != 32'd0)
$fatal(1, "wb_adr_decode: region %0d base %0h not naturally aligned",
i, base_of(i));
// A region smaller than one bus word cannot be addressed at all.
if (size_of(i) < 32'(DW / 8))
$fatal(1, "wb_adr_decode: region %0d size %0h is smaller than one word",
i, size_of(i));
for (int unsigned j = i + 1; j < NS; j++)
if ((base_of(i) < base_of(j) + size_of(j)) &&
(base_of(j) < base_of(i) + size_of(i)))
$fatal(1, "wb_adr_decode: regions %0d and %0d overlap", i, j);
end
end
logic [NS-1:0] hit;
always_comb begin
// Compare in WORD terms: both sides of the comparison come from the
// word-valued helpers, so the units can never silently disagree.
for (int unsigned i = 0; i < NS; i++)
hit[i] = ((adr_i & ~wmask_of(i)) == wbase_of(i));
end
assign unmapped_o = ~(|hit);
assign sel_o = hit;
always_comb begin
offset_o = adr_i; // unmapped passes through
// Priority-free-safe: at most one hit[i] can be set, which the
// elaboration overlap check above guarantees.
for (int unsigned i = 0; i < NS; i++)
if (hit[i]) offset_o = adr_i & wmask_of(i);
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_adr_slave — interprets a LOCAL WORD offset and nothing else.
//
// Note what is absent: no base address, no comparison against a system
// address, no knowledge that other slaves exist. That absence is what lets
// this design be instantiated three times (Chapter 3.6).
// ─────────────────────────────────────────────────────────────────────────
module wb_adr_slave #(
parameter int unsigned OFF_AW = 10, // local WORD-offset width
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic cyc_i,
input logic stb_i,
input logic we_i,
input logic [OFF_AW-1:0] adr_i, // LOCAL WORD offset
input logic [DW-1:0] dat_i,
output logic [DW-1:0] dat_o,
output logic ack_o,
output logic err_o
);
// Register offsets in WORD units. The documented byte offsets 0x00, 0x04,
// 0x08 become word offsets 0, 1, 2 — the factor of four that the missing
// low address bits account for.
localparam logic [OFF_AW-1:0] W_CTRL = 'd0; // byte 0x00
localparam logic [OFF_AW-1:0] W_STATUS = 'd1; // byte 0x04
localparam logic [OFF_AW-1:0] W_DATA = 'd2; // byte 0x08
logic [DW-1:0] ctrl_q, data_q;
logic xfer;
assign xfer = cyc_i & stb_i; // RULES 3.30 & 3.35, see Chapter 4.8
logic legal;
always_comb begin
unique case (adr_i)
W_CTRL, W_STATUS, W_DATA: legal = 1'b1;
default: legal = 1'b0;
endcase
end
assign err_o = xfer & ~legal;
assign ack_o = xfer & legal;
always_ff @(posedge clk_i) begin
if (rst_i) begin
ctrl_q <= '0;
data_q <= '0;
end else if (xfer && we_i && legal) begin
unique case (adr_i)
W_CTRL: ctrl_q <= dat_i;
W_DATA: data_q <= dat_i;
default: ; // W_STATUS is read-only
endcase
end
end
always_comb begin
dat_o = '0;
if (xfer && !we_i && legal) begin
unique case (adr_i)
W_CTRL: dat_o = ctrl_q;
W_STATUS: dat_o = {{(DW-1){1'b0}}, 1'b1};
W_DATA: dat_o = data_q;
default: dat_o = '0;
endcase
end
end
endmoduleReading the three modules
Purpose. Show the address in each of its three forms and make the word/byte distinction concrete rather than a footnote.
Ports that matter. The master's adr_o is [BYTE_AW-1-$clog2(DW/8):0] — narrower than the byte pointer it was derived from, which is the whole point. The slave's offsets are word-valued: 0x04 in the datasheet becomes word 1 in RTL.
Ownership. The master drives; the decoder transforms; the slave consumes. No signal is driven from two places.
Combinational logic. In the decoder: one equality per region, an OR-reduction for unmapped_o, and a masked offset. In the slave: legality, termination and the read mux.
Sequential logic. The master's active_q and latched adr_q. The slave's two application registers. Nothing else survives an edge.
Timing. The address is latched once at the edge go_i is accepted and does not move until the transfer terminates — RULE 3.60's stability obligation, met structurally.
Qualification. Everything the slave does is gated on xfer. The address alone causes nothing.
Reset. Synchronous, active high. The master's qualifiers are negated (RULE 3.20); the slave's application registers are cleared, which is an educational choice per Chapter 4.2.
Simplifications. SEL_O is absent — it arrives in Chapter 4.7. No RTY_O. Regions must be at least one word.
Failure modes. Deriving adr_o from byte_adr_i directly rather than the latched copy breaks stability the first time a slave waits. Comparing byte-valued parameters against a word address without shifting produces a factor-of-four error. Forgetting the mask makes every slave see a system address.
5. Waveform — The Validity Window
ADR_O: qualified, stable, then irrelevant again
9 cyclesCycle 0 is the one worth staring at. The address bus is carrying 0x1000_040 — a leftover — and STB_O is low. A slave that watched the address bus alone would see that value and could act on it. Nothing about it is invalid; it is simply unqualified, and Chapter 4.8 is the signal that makes the distinction.
Cycles 1 to 4 are the stability obligation. The address is presented and does not move for four cycles because the slave is not ready. The master's logic contains nothing that could move it, because it drives from a latched copy.
6. Failure Modes and Discriminating Evidence
Symptom: every register of every peripheral appears at four times its documented offset.
Candidate causes. A word-addressed port connected to a byte-addressed master, or byte-valued region parameters compared against a word address without shifting.
Discriminating evidence. Read one register at its documented byte offset and again at four times it. If the second works, the address is being interpreted in the wrong units — and the factor tells you the port width: four for a 32-bit port, eight for 64-bit.
Likely RTL location. The decoder's comparison, or the boundary between a byte-address master and a word-address slave.
Property. An elaboration check comparing the units of the parameters against the port width, as wb_adr_decode does.
Symptom: a design works against fast slaves and returns data from the wrong address against slow ones.
Candidate causes. The address is driven from the client's live signals rather than a latched copy, so it moves during the wait.
Discriminating evidence. Trigger on stb_o asserted with no termination and watch adr_o. Any change in that window is a RULE 3.60 violation and the fault is in the master, not the slave.
Likely RTL location. The assign adr_o = … line.
Property. P2 in Section 7.
Symptom: a peripheral responds at addresses outside its documented region.
Candidate causes. A decode comparison examining too few address bits — the aliasing failure of Chapter 2.2 §5.
Discriminating evidence. Probe the slave's STB_I while accessing an address far outside its region. A strobe asserted is conclusive, and the address that triggered it tells you which bits are being ignored.
Likely RTL location. The decoder's hit expression.
Symptom: a slave works standalone and misbehaves in the system, with registers shifted by a constant.
Candidate causes. The slave receives the full system address rather than a masked local offset.
Discriminating evidence. Compare the slave's ADR_I against the master's ADR_O on a known access. Equal means the mask is missing.
Likely RTL location. The INTERCON's offset computation.
7. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_adr_checker — address-path properties.
//
// P1 and P2 are SPECIFICATION requirements derived from RULE 3.60.
// P3 is a LOCAL DESIGN POLICY of this module's decoder and is labelled.
// ─────────────────────────────────────────────────────────────────────────
module wb_adr_checker #(
parameter int unsigned AW = 30,
parameter int unsigned NS = 3
) (
input logic clk_i,
input logic rst_i,
input logic cyc_o,
input logic stb_o,
input logic [AW-1:0] adr_o,
input logic ack_i,
input logic err_i,
input logic rty_i,
input logic [NS-1:0] dec_sel,
input logic dec_unmapped
);
default disable iff (rst_i);
logic terminated;
assign terminated = ack_i | err_i | rty_i;
// P1 — SPECIFICATION (RULE 3.60, as stability). While a transfer is
// presented and not yet terminated, the qualified address must not
// move. This is the property that separates a master driven from a
// register from one driven from its client's live inputs.
property p_adr_stable_while_presented;
@(posedge clk_i) (stb_o && !terminated) |=> $stable(adr_o);
endproperty
a_adr_stable : assert property (p_adr_stable_while_presented)
else $error("RULE 3.60: ADR_O moved while the transfer was outstanding");
// P2 — SPECIFICATION (RULE 3.25, the cheap half). A strobe without a
// cycle means the address is being qualified outside a bus cycle.
property p_stb_implies_cyc;
@(posedge clk_i) stb_o |-> cyc_o;
endproperty
a_stb_implies_cyc : assert property (p_stb_implies_cyc)
else $error("RULE 3.25: STB_O asserted without CYC_O");
// P3 — LOCAL POLICY, not a Wishbone rule. This decoder is built so that
// at most one region matches and `unmapped` is its complement. The
// SPECIFICATION does not prescribe a decode structure at all
// (Chapter 3.5) — an INTERCON is defined by its job, so this is a
// claim about THIS fabric and must not be bound to another one.
property p_decode_onehot0;
@(posedge clk_i) $onehot0(dec_sel);
endproperty
a_decode_onehot0 : assert property (p_decode_onehot0)
else $error("local policy: two regions matched one address");
property p_unmapped_complement;
@(posedge clk_i) dec_unmapped == (dec_sel == '0);
endproperty
a_unmapped_complement : assert property (p_unmapped_complement)
else $error("local policy: unmapped disagrees with the select vector");
endmoduleThe P1/P3 split matters here more than usual. P1 and P2 are enforceable against any conforming master. P3 is a property of this fabric's decoder, and the specification prescribes no decode structure whatsoever — Chapter 3.5 established that INTERCON is defined by its job. Binding P3 to a crossbar that legitimately routes one address to several ports would produce failures on a correct design.
Tooling limitation. Icarus has no SVA support; this checker was reviewed by inspection only. The synthesisable modules above are elaborated.
8. Common Mistakes
"The address selects the slave."
Wrong mental model: each slave recognises its own addresses.
Concrete bug: a base address inside the slave. It cannot be instantiated twice, cannot be relocated without editing, and answers at unintended addresses whenever its comparison is incomplete.
Observable evidence: adding a second instance of the peripheral requires editing the peripheral.
Correct model: the address names a location. The INTERCON turns it into a selection and hands the slave a local offset.
"ADR_O is always as wide as a pointer."
Wrong mental model: the address bus carries a byte address.
Concrete bug: connecting a byte-address master to a word-address slave, so every register lands at four times its documented offset.
Observable evidence: a peripheral whose registers all read correctly at 4 × their documented offsets.
Correct model: the lower boundary is set by port size and granularity — ADR_O(n..2) in the specification's own 32-bit byte-granular example. Byte selection is SEL_O's. Which convention a core uses is a datasheet fact.
"The address on the bus is the address."
Wrong mental model: whatever is on ADR_O is what a slave sees.
Concrete bug: debugging a slave against the master's address while the INTERCON is masking it, and concluding the slave's decode is broken when it is receiving exactly what it should.
Observable evidence: a register that appears shifted by the region's base.
Correct model: the address is transformed once, in the INTERCON — system address in, local offset out. It is the only transformation in a Wishbone access (Chapter 3.6).
"An address on the bus means an access is happening."
Wrong mental model: the bus is idle when the address is zero.
Concrete bug: a slave or a monitor that reacts to address changes, acting on residue between transfers.
Observable evidence: register state changing with no corresponding software access, often matching the previous transfer's address.
Correct model: RULE 3.60 qualifies the address with STB_O. Between transfers the bus carries a value nobody may act on, and that is not a defect.
9. Interview Reasoning
Narrower than a byte pointer. The specification's own example for a 32-bit port with 8-bit granularity is ADR_O(n..2) — bits 1 and 0 are not carried.
Why: with a 32-bit port and byte granularity there are four byte lanes, and SEL_O(3..0) already says which participate. Encoding byte position a second time in the low address bits would be redundant, and worse, ambiguous — there would be no defined answer if ADR_O[1:0] and SEL_O disagreed.
So the division of labour is: the address says which word; SEL_O says which bytes within it. That is why byte selects are not "just the low address bits" — they are the replacement for them.
The practical consequence, and the reason this is worth knowing: connecting a byte-address master to a word-address slave puts every register at four times its documented offset. The factor identifies the port width — four for 32-bit, eight for 64-bit — which makes it one of the faster bugs to diagnose once you have seen it.
The honest qualification: many real cores do carry a full byte address and ignore the low bits, because it makes pointer arithmetic and the software view line up. Both conventions exist; which one a core uses is a datasheet question.
10. Understanding Check
11. What's Next
The address is settled: qualified by the strobe, stable while presented, transformed once into a local offset, and narrower than a byte pointer because byte selection lives elsewhere.
An address says where. It says nothing about what is being moved, and the two data paths that carry that are the next two chapters — beginning with the one whose name causes the most confusion in the whole interface.
DAT_Oon a master is write data;DAT_Oon a slave is read data. What does an interface owe the data it drives, and why is that not one bus?
Chapter 4.4 — DAT_O answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
Address Phase
Classic has one handshake, not an address channel. The address names a device and a register inside it, exists in three representations, and must stay coherent for the whole transfer.
- Related topic
Need for Standardized Interconnects
An address map answers where a register lives. It says nothing about which wires carry the request, when they are valid, how the target reports completion, or what happens on an error. Three peripherals with three private interfaces produce three adapters, three verification efforts and three ways to be wrong — which is the argument for standardising the interface rather than the map.
- Related topic
SoC Communication
Six chapters built the pieces; this one assembles them into a working fabric and traces three real accesses through it. The result works, and reading the nine unwritten rules a third party would need is what makes the case for a published protocol concrete rather than theoretical.
- Related topic
Transaction Lifecycle
One Wishbone transaction from a master's decision to act through the slave's termination and back to the caller: what is fixed by the protocol, what every implementation may vary, and what a real simulation of the assembled system shows at each step.
Standards & specifications
- Governing standard
- Wishbone SoC Interconnection Architecture (OpenCores)(opens OpenCores in a new tab)
Defines the Wishbone signal set, the bus cycles built from it and the interface rules a portable IP core must follow. It deliberately leaves interconnect topology, address map and arbitration policy to the integrator, so those are system decisions rather than requirements of the specification.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the Wishbone curriculum.
