Skip to content
VLSI Mentor

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_IChapter 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:

WhereWhat the address means
Master ADR_OA system address
INTERCON inputA system address, compared against the map
INTERCON outputA local offset, masked to the hit region
Slave ADR_IA 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:

CarriesSignal
Which wordADR_O, from bit 2 upward on a 32-bit byte-granular port
Which bytes within that wordSEL_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

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

Reading 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 cycles
A Wishbone master over nine clock cycles. In cycle zero the address bus carries a stale value from a previous transfer while the strobe is low, so it means nothing. In cycle one the master asserts both cycle and strobe with a new address, which is now qualified and meaningful. The slave is busy, so it withholds acknowledge through cycles one, two and three, and the address does not change at all during that interval. In cycle four acknowledge is asserted and the transfer terminates. From cycle five the strobe is low again and the address bus retains its last value, which no slave may act on.stale value, STB low — means nothingstale value, STB low —means nothingqualified: address is now meaningfulqualified: address is nowmeaningfulunchanged while waiting — RULE 3.60unchanged while waiting —RULE 3.60terminated; address irrelevant againterminated; addressirrelevant againCLK_ICYC_OSTB_OADR_O0x1000_0400x1000_4010x1000_4010x1000_4010x1000_4010x1000_4010x1000_4010x1000_4010x1000_401ACK_It0t1t2t3t4t5t6t7t8
Figure 1 — the address is meaningful only while STB_O is asserted, and must not move until the transfer terminates.

Cycle 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// 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");
endmodule

The 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_O on a master is write data; DAT_O on 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

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.