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:
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.
ack = sel[0] ? ack_0 : sel[1] ? ack_1 : sel[2] ? ack_2 : ack_defaultThe alternative reads better and is wrong:
ack = ack_0 | ack_1 | ack_2 | ack_default
dat = dat_0 | dat_1 | dat_2 | dat_defaultThe 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
// ─────────────────────────────────────────────────────────────────────────
// 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
endmoduleReading 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
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.
=== 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:
// ─────────────────────────────────────────────────────────────────────────
// 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;
endmoduleNote 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:
// ── 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;
endmoduleThat 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:
// ── 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;
endmoduleThree lines, and the downstream qualification is untouched. The same select vector still gates every target's strobe, exactly as SIM D measured.
=== 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
// ─────────────────────────────────────────────────────────────────────────
// 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);
endmoduleP4 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 elaboration — Chapter 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
Related tutorials
- Related topic
CPU to Peripheral Communication
A CPU reaches hardware outside itself by reading and writing addressed locations, and a peripheral is hardware it cannot execute. Everything a driver does has to be expressed as a read or a write of a location the peripheral answers for — and once more than a couple of peripherals exist, wiring each one to the core separately stops scaling. That is the problem an on-chip bus is the answer to.
- 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
STB_O
Bus wires always carry values; STB_O is what turns a set of values into a request. Qualification, the termination every strobe is owed, and why silence is the one response a slave may never give.
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.
