Skip to content
VLSI Mentor

Wishbone · Module 12

Peripheral Selection

Correct select logic is half of a correct interconnect. Measured: a read returning 0x33330001 where the target answered 0x11110001, with every component conformant.

Chapter 12.3 finished the decoder. It produces a one-hot select and a local offset, and so far nothing consumes either of them.

How does the selected target receive the transfer — and how does its answer, and only its answer, get back?

1. The Qualification Rule

An unselected target must not see a transfer. Not "should ignore one" — must not see one, and the difference is where the responsibility sits.

The specification's STB_O description settles it:

The SLAVE asserts either ACK_I, ERR_I or RTY_I in response to every assertion of STB_O.

A conformant slave answers every strobe it receives. That is the whole of the slave interface's obligation as Chapter 3.4 framed it, and it has no exception for transfers meant for somebody else. So a slave that receives a strobe for a transfer it does not own will answer it — correctly, obediently, and disastrously. There is no "ignore it" behaviour available to a conformant slave, which means the only way to stop an unselected target responding is to not strobe it.

RULE 3.35 puts the same fact from the slave's side: all three termination signals are generated from the logical AND of CYC_I and STB_I. Drop either and the target produces nothing, because it has nothing to produce it from.

So the rule is one line per target:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  tgt_cyc[i] = cyc_i && sel[i]
  tgt_stb[i] = stb_i && sel[i]

And the address, write data, WE and SEL are broadcast unqualified to everyone. That sounds reckless and is not: RULE 3.60 makes the master qualify ADR_O, DAT_O(), SEL_O, WE_O and TAGN_O with STB_O, so they are meaningful only while a strobe accompanies them. A target with no strobe has no reason to look at them, so broadcasting costs one wire's worth of fanout and saves an entire mux per target.

2. The Response Rule

Upstream, the interconnect chooses. It does not combine.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  ack = sel[0] ? ack_0 : sel[1] ? ack_1 : sel[2] ? ack_2 : ack_default

The alternative reads better and is wrong:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  ack = ack_0 | ack_1 | ack_2 | ack_default
  dat = dat_0 | dat_1 | dat_2 | dat_default

The OR is correct if and only if every unselected target drives exactly zero. That is an assumption about RTL the integrator did not write, checked by nothing, and not required by Wishbone.

RULE 3.65 is the rule usually cited in the OR's defence, and it says the opposite of what the defence needs:

SLAVE interfaces MUST qualify the following signals with [ACK_O], [ERR_O] or [RTY_O]: [DAT_O()].

That is a statement about when data is meaningful, addressed to the consumer. It tells a master when it may believe DAT_I. It does not oblige a slave to drive zero at any other time — and a register file read through a combinational mux naturally does not. An OR-mux consumes DAT from targets whose termination is not asserted, which is precisely the data the rule declines to say anything about.

3. RTL — The Router

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_soc_router — decode, qualify, and route the response back.
//
// This is the piece Chapter 4.3's decoder did not contain. A decoder says
// WHO owns the address. A router acts on that answer in both directions:
//
//   DOWNSTREAM   only the selected target's CYC_I/STB_I are asserted
//   UPSTREAM     only the selected target's ACK/ERR/DAT reach the master
//
// Both halves are necessary and neither is sufficient.
//
// DOWNSTREAM — the qualification rule:
//
//     tgt_cyc[i] = cyc_i && sel[i]
//     tgt_stb[i] = stb_i && sel[i]
//
//   An unselected target sees stb_i LOW, so by the STB_O description it owes
//   no response, and by RULE 3.35 it must not produce one. This is what
//   makes "only one target answers" a property of the wiring rather than a
//   hope about the targets' behaviour. Note that the address and write data
//   are broadcast unconditionally — that is harmless precisely because
//   RULE 3.60 makes them meaningful only while STB is asserted.
//
// UPSTREAM — the response mux:
//
//     ack_o = sel[0] ? a0 : sel[1] ? a1 : ... : default
//
//   Selection, not combination. The tempting alternative is
//
//     ack_o = a0 | a1 | a2                 (and dat_o = d0 | d1 | d2)
//
//   which appears to work whenever exactly one target is selected and the
//   rest are quiet. It relies on every unselected target driving exactly
//   zero on a bus it does not own — an assumption about code the integrator
//   does not control, checked nowhere, and false for any target that parks
//   a value on its data output. wb_broken_or_mux is that design, and
//   Chapter 12.4 measures what it returns.
//
// Selection costs a multiplexer. It buys a guarantee.
// ─────────────────────────────────────────────────────────────────────────
module wb_soc_router #(
  parameter int unsigned BYTE_AW  = 32,
  parameter int unsigned DW       = 32,
  parameter bit          HAS_DFLT = 1'b1,  // Chapter 12.6 sets this to 0
  // Derived where the ports can see it. Word address width = byte address
  // width minus the bits the port does not carry.
  localparam int unsigned WAW = BYTE_AW - $clog2(DW / 8)
) (
  // master side
  input  logic                 cyc_i,
  input  logic                 stb_i,
  input  logic                 we_i,
  input  logic [WAW-1:0]       adr_i,          // system WORD address
  input  logic [DW-1:0]        dat_i,
  output logic [DW-1:0]        dat_o,
  output logic                 ack_o,
  output logic                 err_o,

  // decoder result
  input  logic [2:0]           sel_i,          // {timer, gpio, ram}
  input  logic                 unmapped_i,
  input  logic [WAW-1:0]       offset_i,

  // target side — RAM
  output logic                 ram_cyc_o,
  output logic                 ram_stb_o,
  input  logic [DW-1:0]        ram_dat_i,
  input  logic                 ram_ack_i,
  input  logic                 ram_err_i,
  // target side — GPIO
  output logic                 gpio_cyc_o,
  output logic                 gpio_stb_o,
  input  logic [DW-1:0]        gpio_dat_i,
  input  logic                 gpio_ack_i,
  input  logic                 gpio_err_i,
  // target side — TIMER
  output logic                 timer_cyc_o,
  output logic                 timer_stb_o,
  input  logic [DW-1:0]        timer_dat_i,
  input  logic                 timer_ack_i,
  input  logic                 timer_err_i,
  // target side — default responder
  output logic                 dflt_cyc_o,
  output logic                 dflt_stb_o,
  input  logic [DW-1:0]        dflt_dat_i,
  input  logic                 dflt_ack_i,
  input  logic                 dflt_err_i,

  // shared, broadcast: meaningful only while a STB qualifies them (RULE 3.60)
  output logic                 tgt_we_o,
  output logic [WAW-1:0]       tgt_adr_o,
  output logic [DW-1:0]        tgt_dat_o
);
  // ── downstream qualification ────────────────────────────────────────────
  assign ram_cyc_o   = cyc_i && sel_i[0];
  assign ram_stb_o   = stb_i && sel_i[0];
  assign gpio_cyc_o  = cyc_i && sel_i[1];
  assign gpio_stb_o  = stb_i && sel_i[1];
  assign timer_cyc_o = cyc_i && sel_i[2];
  assign timer_stb_o = stb_i && sel_i[2];

  // The default responder is selected by the ABSENCE of a real match, and
  // only while a transfer is actually presented. Computing it from
  // unmapped_i rather than from ~|sel_i keeps the decoder the single source
  // of the classification.
  assign dflt_cyc_o = HAS_DFLT && cyc_i && unmapped_i;
  assign dflt_stb_o = HAS_DFLT && stb_i && unmapped_i;

  assign tgt_we_o  = we_i;
  assign tgt_adr_o = offset_i;
  assign tgt_dat_o = dat_i;

  // ── upstream selection ──────────────────────────────────────────────────
  always_comb begin
    // Explicit defaults first. With HAS_DFLT = 0 an unmapped access falls
    // through to exactly this: no termination at all, forever. That is not
    // an oversight in the router; it is the architecture Chapter 12.6
    // compares against, and it is what "no default slave" actually means.
    ack_o = 1'b0;
    err_o = 1'b0;
    dat_o = '0;
    if (sel_i[0]) begin
      ack_o = ram_ack_i;    err_o = ram_err_i;    dat_o = ram_dat_i;
    end else if (sel_i[1]) begin
      ack_o = gpio_ack_i;   err_o = gpio_err_i;   dat_o = gpio_dat_i;
    end else if (sel_i[2]) begin
      ack_o = timer_ack_i;  err_o = timer_err_i;  dat_o = timer_dat_i;
    end else if (HAS_DFLT && unmapped_i) begin
      ack_o = dflt_ack_i;   err_o = dflt_err_i;   dat_o = dflt_dat_i;
    end
  end
endmodule

Reading it

The downstream half is six lines and has no subtlety in it. Each target's CYC_I and STB_I are the master's, gated by that target's select bit. The gate is the whole mechanism.

The upstream half is an always_comb with explicit defaults first. That ordering is not stylistic: with HAS_DFLT = 0, an unmapped access falls through every branch and the defaults stand — no acknowledge, no error, no data, indefinitely. Chapter 12.6 measures that, and it is not an oversight in the router; it is what "no default slave" means, written out.

The default responder is selected from unmapped_i, not from ~|sel_i. The two are equal — Chapter 12.1's P2 asserts exactly that — and deriving it from the decoder's own output keeps one source for the classification rather than two expressions that must be kept in agreement.

The if / else if chain is a priority mux over a vector that is one-hot. Priority is therefore never exercised, and that is deliberate: if the one-hot guarantee ever failed, this router would pick the lowest-numbered target rather than producing an ambiguous result. It degrades to something definite instead of something unpredictable — but the guarantee is upstream, at elaboration, and this is not a substitute for it.

4. The Router in Place

A block diagram of the interconnect. The master's request enters on the left. The decoder produces a select vector which the router uses twice. Downstream, the router gates the cycle and strobe signals so that only the selected target, here the GPIO, receives a qualified transfer; the RAM and the timer receive no strobe. The address, write data and write enable are broadcast to all targets unqualified. Upstream, the router selects the acknowledge, error and read data of the selected target only, and returns them to the master.masterCYC, STB, ADR, DATdecodersel = 010routergate down, select upRAMSTB_I = 0 — notaskedGPIOSTB_I = 1 — selectedTIMERSTB_I = 0 — notaskedone answerACK and DAT fromGPIOrequestselCYC, STBACK, DAT12

The dashed edges to RAM and TIMER carry the address and write data and no strobe. They are connected and they are not asked. That distinction is the qualification rule made visible — and it is what the two flat rows in Section 8's measurement report.

Only one arrow returns. In the broken version of this picture, three arrows return and are merged before reaching the master.

5. Simulation — SIM D: Only the Owner Is Asked

Four accesses through the complete system, each a single read or write of the shape Chapter 8.1 and Chapter 8.2 established. Each target drives a distinct signature — GPIO 0x1111_xxxx, TIMER 0x2222_xxxx, RAM whatever was written — so a misroute appears in the data column rather than having to be inferred.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM D - only the owner is asked, only the owner answers ===
    every target drives a distinctive signature, so a misroute
    shows up in the data rather than having to be deduced.

    access            sel  qualified STB   returned    class
                           TIM GPIO RAM
    GPIO  OUT         010   0   1   0    0x11110001   ACK
    TIMER LOAD        100   1   0   0    0x22220001   ACK
    RAM   word 2      001   0   0   1    0x33330002   ACK
    unmapped          000   0   0   0    0xdead0000   ERR

    each row strobes exactly one target, or none. An unselected
    target sees STB low, so it owes no response at all.

Reading it

Read the three strobe columns across each row: one 1, or none. These are not the select bits re-printed — they are the STB_I each target's port actually received, probed at the target's own pins. The qualification rule is measured rather than assumed.

The data column identifies the responder independently. 0x1111_0001 could only have come from GPIO; 0x2222_0001 only from TIMER. Two independent pieces of evidence agree on every row, which is what makes the rows worth printing rather than a single "pass".

The last row strobes nothing at all and still terminates. sel = 000, every target quiet, ERR returned with 0xdead0000. Something answered for an address no target owns, and it is not one of the three — Chapter 12.6 is about what it is and why it must exist.

Nothing in this table distinguishes a correct response path from an ORed one. All four rows would be identical on the broken router, which is the next section's point.

6. Simulation — SIM E: The Same Accesses, Combined Instead of Selected

One change, and it is not in the interconnect. The TIMER is replaced by a version whose read data is the selected register all the time, instead of only while it is being asked. Its transfers behave identically; only its idle output differs.

First the GPIO, which is the target SIM D and SIM E both read from:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// The three targets of the running SoC, plus the default responder.
//
// Each is deliberately minimal: Module 12 is about getting a transfer to the
// right target, not about designing peripherals. What matters here is that
// every target
//   * sees ONLY a local word offset — never a system address
//   * answers ONLY when its own CYC_I and STB_I are asserted
//   * drives a DISTINCTIVE data signature, so a routing mistake is visible
//     in the data rather than having to be inferred
//
// The signatures are the instrument for Chapter 12.4:
//   GPIO  0x1111_xxxx     TIMER 0x2222_xxxx     RAM   whatever was written
// A read that returns 0x2222_.... from a GPIO access has been misrouted, and
// no further reasoning is needed to establish that.
// ─────────────────────────────────────────────────────────────────────────

// ── GPIO: two registers, 0x1111 signature ───────────────────────────────
module wb_gpio_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,
  output logic [DW-1:0]     out_q_o         // observation
);
  localparam logic [OFF_AW-1:0] O_DIR = OFF_AW'('h0);
  localparam logic [OFF_AW-1:0] O_OUT = OFF_AW'('h1);

  logic xfer, mapped;
  logic [DW-1:0] dir_q, out_q;

  assign xfer   = cyc_i && stb_i;
  // Only two words of this 1024-word window are implemented. Everything
  // else inside the window is a HOLE — Chapter 12.7's subject. Answering a
  // hole with ERR is this peripheral's policy, not a Wishbone requirement.
  assign mapped = (adr_i == O_DIR) || (adr_i == O_OUT);
  assign ack_o  = xfer &&  mapped;
  assign err_o  = xfer && !mapped;

  always_comb begin
    dat_o = '0;
    if (xfer && mapped)
      dat_o = (adr_i == O_DIR) ? dir_q : out_q;
  end

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      dir_q <= 32'h1111_0000;
      out_q <= 32'h1111_0001;
    end else if (xfer && mapped && we_i) begin
      if (adr_i == O_DIR) dir_q <= dat_i;
      else                out_q <= dat_i;
    end
  end
  assign out_q_o = out_q;
endmodule

Note dat_o in there: gated on xfer && mapped. It drives zero unless it is being asked — which is the behaviour the ORed mux depends on, and which nothing requires.

The two timers, side by side. The difference is one term in one assign:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── TIMER: two registers, 0x2222 signature ──────────────────────────────
module wb_timer_slave #(
  parameter int unsigned OFF_AW = 10,
  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,
  input  logic [DW-1:0]     dat_i,
  output logic [DW-1:0]     dat_o,
  output logic              ack_o,
  output logic              err_o,
  output logic [DW-1:0]     load_q_o
);
  localparam logic [OFF_AW-1:0] O_CTRL = OFF_AW'('h0);
  localparam logic [OFF_AW-1:0] O_LOAD = OFF_AW'('h1);

  logic xfer, mapped;
  logic [DW-1:0] ctrl_q, load_q;

  assign xfer   = cyc_i && stb_i;
  assign mapped = (adr_i == O_CTRL) || (adr_i == O_LOAD);
  assign ack_o  = xfer &&  mapped;
  assign err_o  = xfer && !mapped;

  always_comb begin
    dat_o = '0;
    if (xfer && mapped)
      dat_o = (adr_i == O_CTRL) ? ctrl_q : load_q;
  end

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      ctrl_q <= 32'h2222_0000;
      load_q <= 32'h2222_0001;
    end else if (xfer && mapped && we_i) begin
      if (adr_i == O_CTRL) ctrl_q <= dat_i;
      else                 load_q <= dat_i;
    end
  end
  assign load_q_o = load_q;
endmodule

// ── TIMER, written the ordinary way ─────────────────────────────────────
// wb_timer_parking_slave is functionally identical to wb_timer_slave for
// every transfer it is asked to perform. The single difference is what its
// DAT_O carries when it is NOT being asked: the selected register, all the
// time, instead of zero.
//
// THIS IS CONFORMANT, and that is the whole point of publishing it.
// RULE 3.65 says the SLAVE must qualify DAT_O() with ACK_O, ERR_O or RTY_O.
// That is a statement about when the data is MEANINGFUL — it tells a
// consumer when it may believe the bus. It does not oblige the slave to
// drive zero at every other moment, and a register file read through a
// combinational mux naturally does not.
//
// It is also the more common shape in real RTL, because it is what you get
// when you write the read mux without thinking about the strobe at all.
//
// A correct interconnect is indifferent to which version is installed. An
// interconnect that ORs its inputs is not, because ORing consumes DAT from
// targets whose termination is not asserted — precisely the data RULE 3.65
// says nothing about.
module wb_timer_parking_slave #(
  parameter int unsigned OFF_AW = 10,
  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,
  input  logic [DW-1:0]     dat_i,
  output logic [DW-1:0]     dat_o,
  output logic              ack_o,
  output logic              err_o,
  output logic [DW-1:0]     load_q_o
);
  localparam logic [OFF_AW-1:0] O_CTRL = OFF_AW'('h0);
  localparam logic [OFF_AW-1:0] O_LOAD = OFF_AW'('h1);

  logic xfer, mapped;
  logic [DW-1:0] ctrl_q, load_q;

  assign xfer   = cyc_i && stb_i;
  assign mapped = (adr_i == O_CTRL) || (adr_i == O_LOAD);
  assign ack_o  = xfer &&  mapped;
  assign err_o  = xfer && !mapped;

  // THE ONLY DIFFERENCE: no xfer term. The register output is simply on the
  // wire. Compare wb_timer_slave, three lines of which gate this on xfer.
  assign dat_o = (adr_i == O_CTRL) ? ctrl_q : load_q;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      ctrl_q <= 32'h2222_0000;
      load_q <= 32'h2222_0001;
    end else if (xfer && mapped && we_i) begin
      if (adr_i == O_CTRL) ctrl_q <= dat_i;
      else                 load_q <= dat_i;
    end
  end
  assign load_q_o = load_q;
endmodule

That version is conformant. RULE 3.65 tells a consumer when DAT_O() is meaningful; it does not oblige a slave to drive zero the rest of the time. It is also the more common shape in real RTL, because it is what a register read mux looks like when nobody thought about the strobe.

The interconnect under test is the router's upstream half, combined:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── 2. ORed responses ───────────────────────────────────────────────────
// wb_broken_or_mux — wb_soc_router's upstream half, combined instead of
// selected. The downstream qualification is UNCHANGED and still correct:
// only the selected target receives a strobe. This is what makes the defect
// so durable in review — "the select logic is right, so the data must be".
module wb_broken_or_mux #(
  parameter int unsigned DW = 32
) (
  input  logic [DW-1:0] ram_dat_i,
  input  logic          ram_ack_i,
  input  logic          ram_err_i,
  input  logic [DW-1:0] gpio_dat_i,
  input  logic          gpio_ack_i,
  input  logic          gpio_err_i,
  input  logic [DW-1:0] timer_dat_i,
  input  logic          timer_ack_i,
  input  logic          timer_err_i,
  input  logic [DW-1:0] dflt_dat_i,
  input  logic          dflt_ack_i,
  input  logic          dflt_err_i,
  output logic [DW-1:0] dat_o,
  output logic          ack_o,
  output logic          err_o
);
  // ── THE DEFECT: combination where selection was required. ──
  // Correct IF AND ONLY IF every unselected target drives exactly zero.
  // That is an assumption about other people's code, and it is checked here
  // by nothing at all.
  assign ack_o = ram_ack_i | gpio_ack_i | timer_ack_i | dflt_ack_i;
  assign err_o = ram_err_i | gpio_err_i | timer_err_i | dflt_err_i;
  assign dat_o = ram_dat_i | gpio_dat_i | timer_dat_i | dflt_dat_i;
endmodule

Three lines, and the downstream qualification is untouched. The same select vector still gates every target's strobe, exactly as SIM D measured.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM E - the same accesses, responses ORed together ===
    downstream qualification is UNCHANGED and still correct.
    only the upstream path differs.

    access          target  selected mux   ORed mux     verdict
    GPIO  OUT       GPIO    0x11110001     0x33330001   CORRUPTED
    TIMER LOAD      TIMER   0x22220001     0x22220001       agree
    RAM   word 2    RAM     0x33330002     0x33330003   CORRUPTED

    during the GPIO read, what each target was driving:
      RAM   0x00000000   selected 0
      GPIO  0x11110001   selected 1
      TIMER 0x22220001   selected 0
      dflt  0x00000000   selected 0

    TIMER is not selected and owes no answer, but its register
    output is on the wire regardless. RULE 3.65 says a slave
    qualifies DAT_O with its termination - it does not say the
    wire is zero otherwise. The OR reads it anyway.

Reading it

The GPIO read returned 0x3333_0001 where GPIO answered 0x1111_0001. 0x1111 OR 0x2222 is 0x3333. The timer's parked register was merged into a read it had nothing to do with, and the master has no way to tell.

The middle row is the one to dwell on. The TIMER read agrees. The bug is invisible on the access belonging to the target that causes it — and a TIMER register read is exactly what an engineer tests first after installing a timer.

The RAM read shows the same corruption with different arithmetic. 0x3333_0002 became 0x3333_0003, because the timer's parked 0x2222_0001 contributed its low bit. A one-bit error in a data word, which reads as a plausible value rather than as a failure.

Then the evidence block, which is the diagnosis. During the GPIO read: RAM drove zero, GPIO drove its register, the default drove zero — and TIMER, unselected, drove 0x2222_0001. Every one of those targets is behaving correctly. The only incorrect component is the thing that added them together.

7. Why Not Just Require Targets to Drive Zero?

It is a legitimate architecture, and it has to be a documented one.

It can be made to work. Mandate that every target drives zero on DAT_O unless terminating, check it with an assertion per target, and the OR becomes correct. Some buses are specified this way.

What it costs. Every target now carries an obligation Wishbone does not impose, so a conformant third-party core cannot simply be dropped in — it must be inspected, and wrapped if it parks. RULE 2.15 requires a core's datasheet to describe a great deal; it does not require it to describe idle DAT_O behaviour, so the information may not exist.

And the failure is silent when the rule is broken. An integrator who adds a parking target to a zero-driving system gets Section 6's result, with no diagnostic anywhere.

Selection has none of that. It is indifferent to what unselected targets drive, so the question never needs to be asked about any core, ever. One multiplexer per bus buys the elimination of an entire integration hazard — which is why this module treats the OR as a defect rather than an alternative, while noting that it is a defect of architecture, not of conformance.

8. Failure Modes and Discriminating Evidence

Symptom: a read returns a value that is the bitwise OR of two peripherals' registers.

Candidate causes. A combined response path, with at least one unselected target driving non-zero.

Discriminating evidence. The returned value against each target's output at that clock. If the result is a superset of the selected target's bits, the path is combining. The signature is specific: the answer contains the right bits plus others, never the wrong bits alone.

Likely RTL location: the upstream mux, not the decoder.

Symptom: a write to one peripheral is also executed by another.

Candidate causes. Two targets strobed — either an overlapping map (Chapter 12.3) or a strobe that was not gated by sel.

Discriminating evidence. The STB_I at each target's pins, not the select vector. A one-hot select with two strobes means the qualification is broken; two select bits means the map is.

Symptom: a read returns the previous read's data.

Candidate causes. A target that parks its last value, combined with a response path that does not select — so the master captures whatever is on the merged bus.

Discriminating evidence. Whether the stale value matches the previous access's target. If so, it is a routing problem; if it matches the same target's earlier value, it is a capture problem inside that target, which is Chapter 10.1's stale-register shape.

Symptom: an unmapped access is acknowledged with plausible data.

Candidate causes. A combined path in which the default responder contributes, or a target strobed when it should not have been.

Discriminating evidence. The select vector and the default's strobe together. All four low, with a termination at the master, means the termination was manufactured by the merge — which is what P7 in Section 9 forbids.

9. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_route_props — qualification and response ownership.
// ─────────────────────────────────────────────────────────────────────────
module wb_route_props (
  input logic        clk_i,
  input logic        rst_i,
  input logic        cyc_i,
  input logic        stb_i,
  input logic [2:0]  sel_i,
  input logic        unmapped_i,
  input logic        ram_stb_i,
  input logic        gpio_stb_i,
  input logic        timer_stb_i,
  input logic        dflt_stb_i,
  input logic [31:0] m_dat_i,
  input logic [31:0] gpio_dat_i,
  input logic        m_ack_i,
  input logic        m_err_i,
  input logic        gpio_ack_i
);
  default disable iff (rst_i);

  // P4 — SPECIFICATION-DERIVED (RULE 3.35, and the STB_O description).
  // An unselected target must not receive a qualified strobe. The
  // specification makes a slave answer every STB it sees; the only way to
  // stop an unselected slave answering is to not strobe it. This property
  // is therefore about the interconnect's obligation, derived from the
  // slave's.
  property p_unselected_not_strobed;
    @(posedge clk_i) (!sel_i[1]) |-> (!gpio_stb_i);
  endproperty
  a_unselected_not_strobed: assert property (p_unselected_not_strobed);

  // P5 — LOCAL ARCHITECTURE.
  // At most one strobe leaves the router, counting the default responder.
  // Wishbone has no opinion about how many slaves an interconnect may
  // strobe at once; a design that strobed two would still be built from
  // conformant parts.
  property p_one_strobe;
    @(posedge clk_i)
      $onehot0({ram_stb_i, gpio_stb_i, timer_stb_i, dflt_stb_i});
  endproperty
  a_one_strobe: assert property (p_one_strobe);

  // P6 — LOCAL ARCHITECTURE, and the property SIM E's ORed mux violates.
  // While GPIO is the selected target and terminating, the data the master
  // sees is GPIO's data. Stated as an implication from the SELECTED
  // target's termination, because that is when RULE 3.65 makes its data
  // meaningful at all.
  property p_read_data_ownership;
    @(posedge clk_i) (sel_i[1] && gpio_ack_i) |-> (m_dat_i == gpio_dat_i);
  endproperty
  a_read_data_ownership: assert property (p_read_data_ownership);

  // P7 — LOCAL ARCHITECTURE.
  // No termination reaches the master unless someone was actually asked.
  // An acknowledged transfer that strobed nobody is a manufactured answer.
  property p_no_unasked_termination;
    @(posedge clk_i) (m_ack_i || m_err_i) |->
      (ram_stb_i || gpio_stb_i || timer_stb_i || dflt_stb_i);
  endproperty
  a_no_unasked_termination: assert property (p_no_unasked_termination);
endmodule

P4 is the only specification-derived property in the set, and it is derived rather than quoted. The specification constrains the slave — it must answer every strobe. The obligation on the interconnect follows from that: the only way to keep an unselected slave silent is to withhold the strobe.

P6 is the property SIM E violates, and its shape matters. It is written as an implication from the selected target's acknowledgement, because RULE 3.65 makes a target's data meaningful only while that target is terminating. A property comparing the master's data against a target that is not terminating would be asserting something about data the specification declines to define.

P7 forbids a termination with no addressee. An acknowledged transfer that strobed nobody is an answer nobody gave — the manufactured-response failure, and the one an ORed path can produce from a default responder's constant.

10. Common Mistakes

"ORing read data is fine, because only one slave is selected."

Wrong mental model: selection controls what the targets drive.

What is true: selection controls who is asked, not what is on the wires. An unselected target drives whatever its RTL drives, and RULE 3.65 permits that to be anything. Section 6 measures 0x1111_0001 becoming 0x3333_0001 with every component conformant.

"If two slaves ACK, OR them — only one really responded."

Wrong mental model: two acknowledgements is a signalling problem.

What is true: it is an ownership problem, and the OR destroys the evidence. Both targets received the strobe, both executed, and if either had a side effect it happened. An OR of two acknowledgements is indistinguishable from one, so the merge erases the only symptom.

"An unselected slave will ignore the transfer."

Wrong mental model: a slave can decline.

What is true: a conformant slave has no ignore behaviour. The STB_O description has it answer every strobe. "Ignoring" is not a slave-side option; it is an interconnect-side obligation to not ask.

"The address must be gated per target, like the strobe."

Wrong mental model: everything must be qualified.

What is true: RULE 3.60 already qualifies ADR_O and friends with STB_O. Broadcasting them is safe precisely because the strobe is what makes them meaningful. Gating them per target adds a mux per target and buys nothing, and the router in Section 3 broadcasts them for that reason.

"A parking target is broken and should be fixed."

Wrong mental model: the target caused the corruption.

What is true: the target is conformant and common. The interconnect that consumed its unqualified data is the defect. Fixing the target hides the bug for one core and leaves the next one to rediscover it.

11. Interview Reasoning

Both slaves executed the transfer. That is the problem; the acknowledgement is just how you find out.

The signalling question is the shallow one. On an ORed path the master sees one acknowledgement and proceeds. On a selecting path it sees the selected target's, and the other's is discarded. Neither is satisfactory, because both targets already did the work.

For a read, the consequence is wrong or merged data. For a write, both targets stored it — the side effect is duplicated, and nothing downstream can undo it.

So the right answer moves upstream immediately. Two acknowledgements means two targets were strobed, which means either an overlapping map or a strobe not gated by sel. The fix is at elaborationChapter 12.3's pairwise check — not in the response path.

What to avoid saying: that ORing them is acceptable because only one matters. The measurement in Chapter 12.3 is 1024 words with two owners, and on a write every one of them would be written twice.

12. Understanding Check

The default responder, which is wired to answer exactly when no real target is selected.

The three strobe columns are all zero because none of RAM, GPIO or TIMER owns 0x5000_0000. The decoder was right and the targets were correctly left alone.

A fourth thing received a strobe, gated on unmapped rather than on a select bit. It returns ERR with 0xdead0000, which is why the data column shows a value no target would produce.

Without it the row would read none and the transfer would still be open — which is Chapter 12.6's measurement, using this same address.

And the reason it is not simply a fourth target is that it has no window. It owns the complement of every window, which is most of the address space and is not expressible as a base and a size.

13. What's Next

Requests reach one target and one answer returns, with the qualification measured at the targets' own pins.

The peripherals in this chapter have two registers each. A memory has a thousand words, and the offset stops being a register number.

What changes when the selected target contains many locations instead of a few?

Chapter 12.5 — Memory Selection turns the local offset into a storage index, and measures two distinct global addresses reading and writing the same physical word. 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.