Wishbone · Module 18
Routing
Forward and return routing as one property with five parts, measured on both topologies — and five broken interconnects in which every master and every slave still obeys every rule in B3.
Chapter 18.2 built a second topology and asserted that both route correctly. The assertion has not been examined.
How do the forward and return paths stay coherent — and what does it look like when one of them does not?
1. Two Directions, and Only One of Them Gets Built First
FORWARD ROUTING is master → owner → request context → decoded slave. It is the direction people draw, the direction the address points, and the direction that is obviously necessary.
RETURN ROUTING is selected slave → termination and data → originating master. It is the direction that is forgotten, and a system with a perfect forward path and a careless return path is the most common serious interconnect defect there is.
Here is why it is easy to get wrong. On a shared bus, the return route can be derived from the forward one: the shared address is the owner's address, so decoding it live tells you both where the request went and where the answer came from. Chapter 18.1 §3 made exactly that argument and it was correct there.
On a crossbar it is false. A master waiting at S0 has its own address on its own pins while S1 answers somebody else. Decode that master's address live and you hand it S1's answer. The return route has to be keyed on which slave granted this master, which is state the forward route does not need.
So "routing" is not one mechanism. It is two, and the second one's correctness argument is topology-specific — which is precisely the kind of reasoning that does not survive a copy-paste from one design to the next.
2. What a Route Audit Actually Checks
Turn the five agreements into eight comparisons and you have something executable.
| check | |
|---|---|
| destination | the slave with an active phase is the one its owner's address decodes to |
| forward context | that slave's ADR, WE and DAT are its owner's, all of them |
| no route | no phase is presented at a slave with no owner |
| exclusivity | no master holds two slaves at once |
| stability | no slave's owner moves while a phase is unanswered |
| return ownership | no master sees a termination from a slave it does not hold |
| data source | read data matches the slave that granted the master |
| signature | read data matches the slave the address intended |
The last two are deliberately both present, and they are not redundant. The structural check asks whether the wiring is self-consistent; the signature check is a known-answer test that survives a defect in which the structural check's own premises have moved. Section 6 contains a defect that the structural check misses and the signature check catches.
3. RTL — The Route Probe
One probe serves both topologies, which is only possible because both are describable in the same five terms.
// ─────────────────────────────────────────────────────────────────────────
// wb_route_probe — the route-provenance audit.
//
// SIMULATION ONLY. int counters, no synthesis intent, and nothing here is a
// Wishbone feature. It is written once and used for BOTH topologies, which
// is only possible because both are described in the same five terms:
//
// ORIGIN which master issued this
// INTENDED which slave its address decodes to
// ACTUAL which slave actually saw a cycle
// SOURCE which slave produced the termination and data
// RECIPIENT which master received them
//
// A transaction is correct when all five agree. Every defect in Module 18
// breaks exactly one of the agreements, and this module names which.
//
// The shared bus has one global owner; the crossbar has one owner per
// slave. The probe takes the PER-SLAVE view, and the shared-bus rig derives
// it from its global owner plus the decoded destination. That is why one
// probe serves both topologies rather than two probes serving one each.
//
// DATA PROVENANCE IS CHECKED TWICE, DELIBERATELY:
// structurally the value the master received equals the value the slave
// that granted it was driving;
// by signature the value equals SIG of the INTENDED slave plus the
// offset, which is a known-answer test that survives a
// defect in which the structural check's own premises move.
// ─────────────────────────────────────────────────────────────────────────
module wb_route_probe #(
parameter int unsigned AW = 12,
parameter int unsigned DW = 32,
parameter int unsigned SEL_BIT = 11,
parameter int unsigned OFF_BITS = 8,
parameter logic [31:0] SIG0 = 32'hA000_0000,
parameter logic [31:0] SIG1 = 32'hB100_0000
) (
input logic clk_i,
input logic rst_i,
// masters, as seen at their own ports
input logic m0_cyc_i, m0_stb_i, m0_we_i,
input logic [AW-1:0] m0_adr_i,
input logic [DW-1:0] m0_wdat_i, m0_rdat_i,
input logic m0_ack_i, m0_err_i,
input logic m1_cyc_i, m1_stb_i, m1_we_i,
input logic [AW-1:0] m1_adr_i,
input logic [DW-1:0] m1_wdat_i, m1_rdat_i,
input logic m1_ack_i, m1_err_i,
// slaves, as seen at their own ports
input logic s0_cyc_i, s0_stb_i, s0_we_i,
input logic [AW-1:0] s0_adr_i,
input logic [DW-1:0] s0_wdat_i, s0_rdat_i,
input logic s0_ack_i, s0_err_i,
input logic s1_cyc_i, s1_stb_i, s1_we_i,
input logic [AW-1:0] s1_adr_i,
input logic [DW-1:0] s1_wdat_i, s1_rdat_i,
input logic s1_ack_i, s1_err_i,
// per-slave ownership, 0 NONE / 1 M0 / 2 M1
input logic [1:0] sown0_i, sown1_i,
// violations
output int unsigned v_dest_o, // request reached the wrong slave
output int unsigned v_fwd_o, // forward context is not the owner's
output int unsigned v_noroute_o, // a phase presented with no owner
output int unsigned v_excl_o, // a master owns two slaves at once
output int unsigned v_stable_o, // owner moved under an open phase
output int unsigned v_retown_o, // a master saw a foreign termination
output int unsigned v_src_o, // data did not come from the granter
output int unsigned v_sig_o, // data is not the intended slave's
// concurrency
output int unsigned clk_active0_o, clk_active1_o,
output int unsigned clk_both_o, clk_none_o, clk_one_o,
// contention, per destination
output int unsigned con_s0_o, con_s1_o,
// terminations
output int unsigned n_ack0_o, n_ack1_o, n_err0_o, n_err1_o,
output int unsigned n_m0term_o, n_m1term_o
);
localparam logic [1:0] NONE = 2'd0, M0 = 2'd1, M1 = 2'd2;
logic m0_dest, m1_dest;
assign m0_dest = m0_adr_i[SEL_BIT];
assign m1_dest = m1_adr_i[SEL_BIT];
logic a0, a1; // slave path active this clock
assign a0 = s0_cyc_i && s0_stb_i;
assign a1 = s1_cyc_i && s1_stb_i;
logic t0, t1; // slave terminating
assign t0 = s0_ack_i || s0_err_i;
assign t1 = s1_ack_i || s1_err_i;
logic m0t, m1t;
assign m0t = m0_ack_i || m0_err_i;
assign m1t = m1_ack_i || m1_err_i;
// what each slave's owner intended
logic dest_bad0, dest_bad1, fwd_bad0, fwd_bad1;
always_comb begin
dest_bad0 = 1'b0; fwd_bad0 = 1'b0;
if (a0) begin
if (sown0_i == M0) begin
dest_bad0 = (m0_dest != 1'b0);
fwd_bad0 = (s0_adr_i != m0_adr_i) || (s0_we_i != m0_we_i)
|| (s0_we_i && (s0_wdat_i != m0_wdat_i));
end else if (sown0_i == M1) begin
dest_bad0 = (m1_dest != 1'b0);
fwd_bad0 = (s0_adr_i != m1_adr_i) || (s0_we_i != m1_we_i)
|| (s0_we_i && (s0_wdat_i != m1_wdat_i));
end
end
dest_bad1 = 1'b0; fwd_bad1 = 1'b0;
if (a1) begin
if (sown1_i == M0) begin
dest_bad1 = (m0_dest != 1'b1);
fwd_bad1 = (s1_adr_i != m0_adr_i) || (s1_we_i != m0_we_i)
|| (s1_we_i && (s1_wdat_i != m0_wdat_i));
end else if (sown1_i == M1) begin
dest_bad1 = (m1_dest != 1'b1);
fwd_bad1 = (s1_adr_i != m1_adr_i) || (s1_we_i != m1_we_i)
|| (s1_we_i && (s1_wdat_i != m1_wdat_i));
end
end
end
// return ownership: a master may see a termination only from a slave it
// owns, and only on a clock that slave is terminating
logic ret_bad0, ret_bad1;
assign ret_bad0 = m0t && !((sown0_i == M0 && t0) || (sown1_i == M0 && t1));
assign ret_bad1 = m1t && !((sown0_i == M1 && t0) || (sown1_i == M1 && t1));
// data provenance, structural and by signature
logic [DW-1:0] exp0_src, exp1_src, exp0_sig, exp1_sig;
logic [OFF_BITS-1:0] off0, off1;
assign off0 = m0_adr_i[OFF_BITS-1:0];
assign off1 = m1_adr_i[OFF_BITS-1:0];
assign exp0_src = (sown1_i == M0) ? s1_rdat_i : s0_rdat_i;
assign exp1_src = (sown1_i == M1) ? s1_rdat_i : s0_rdat_i;
assign exp0_sig = (m0_dest ? SIG1 : SIG0) + {{(DW-OFF_BITS){1'b0}}, off0};
assign exp1_sig = (m1_dest ? SIG1 : SIG0) + {{(DW-OFF_BITS){1'b0}}, off1};
logic src_bad0, src_bad1, sig_bad0, sig_bad1;
assign src_bad0 = m0_ack_i && !m0_we_i && (m0_rdat_i !== exp0_src);
assign src_bad1 = m1_ack_i && !m1_we_i && (m1_rdat_i !== exp1_src);
assign sig_bad0 = m0_ack_i && !m0_we_i && (m0_rdat_i !== exp0_sig);
assign sig_bad1 = m1_ack_i && !m1_we_i && (m1_rdat_i !== exp1_sig);
logic excl_bad;
assign excl_bad = ((sown0_i == M0) && (sown1_i == M0))
|| ((sown0_i == M1) && (sown1_i == M1));
logic [1:0] p0_q, p1_q;
logic open0_q, open1_q;
logic c0, c1; // both masters want this slave
assign c0 = (m0_cyc_i && !m0_dest) && (m1_cyc_i && !m1_dest);
assign c1 = (m0_cyc_i && m0_dest) && (m1_cyc_i && m1_dest);
always_ff @(posedge clk_i) begin
if (rst_i) begin
v_dest_o <= 0; v_fwd_o <= 0; v_noroute_o <= 0; v_excl_o <= 0;
v_stable_o <= 0; v_retown_o <= 0; v_src_o <= 0; v_sig_o <= 0;
clk_active0_o <= 0; clk_active1_o <= 0;
clk_both_o <= 0; clk_none_o <= 0; clk_one_o <= 0;
con_s0_o <= 0; con_s1_o <= 0;
n_ack0_o <= 0; n_ack1_o <= 0; n_err0_o <= 0; n_err1_o <= 0;
n_m0term_o <= 0; n_m1term_o <= 0;
p0_q <= NONE; p1_q <= NONE; open0_q <= 1'b0; open1_q <= 1'b0;
end else begin
v_dest_o <= v_dest_o + (dest_bad0 ? 1 : 0) + (dest_bad1 ? 1 : 0);
v_fwd_o <= v_fwd_o + (fwd_bad0 ? 1 : 0) + (fwd_bad1 ? 1 : 0);
v_noroute_o <= v_noroute_o + ((a0 && sown0_i == NONE) ? 1 : 0)
+ ((a1 && sown1_i == NONE) ? 1 : 0);
v_excl_o <= v_excl_o + (excl_bad ? 1 : 0);
v_retown_o <= v_retown_o + (ret_bad0 ? 1 : 0) + (ret_bad1 ? 1 : 0);
v_src_o <= v_src_o + (src_bad0 ? 1 : 0) + (src_bad1 ? 1 : 0);
v_sig_o <= v_sig_o + (sig_bad0 ? 1 : 0) + (sig_bad1 ? 1 : 0);
// owner stability: the owner moved while a phase was open
if (open0_q && sown0_i != p0_q) v_stable_o <= v_stable_o + 1;
else if (open1_q && sown1_i != p1_q) v_stable_o <= v_stable_o + 1;
p0_q <= sown0_i; p1_q <= sown1_i;
open0_q <= a0 && !t0;
open1_q <= a1 && !t1;
if (a0) clk_active0_o <= clk_active0_o + 1;
if (a1) clk_active1_o <= clk_active1_o + 1;
if (a0 && a1) clk_both_o <= clk_both_o + 1;
else if (a0 || a1) clk_one_o <= clk_one_o + 1;
else clk_none_o <= clk_none_o + 1;
if (c0) con_s0_o <= con_s0_o + 1;
if (c1) con_s1_o <= con_s1_o + 1;
if (s0_ack_i) n_ack0_o <= n_ack0_o + 1;
if (s1_ack_i) n_ack1_o <= n_ack1_o + 1;
if (s0_err_i) n_err0_o <= n_err0_o + 1;
if (s1_err_i) n_err1_o <= n_err1_o + 1;
if (m0t) n_m0term_o <= n_m0term_o + 1;
if (m1t) n_m1term_o <= n_m1term_o + 1;
end
end
endmoduleReading it
The shared bus has one global owner and the crossbar has one per slave, so the probe takes the per-slave view and the shared-bus rig projects its global owner onto the destination it is driving. That is two lines of wiring and it is what makes a single audit possible across two architectures.
dest_bad0 is the forward-destination check and it reads backwards from the slave. For each slave with an active phase, find its owner, and ask what that master's address decodes to. If S0 is busy on behalf of a master whose address says S1, the request is at the wrong slave. The decoder may be perfectly correct and this check still fires — decode is a decision; this tests whether the decision was carried out.
ret_bad0 is the return-ownership check, and it is two conditions rather than one: a master may see a termination only from a slave it holds, and only on a clock that slave is terminating. The second half catches a termination invented by the interconnect.
The two data checks differ in what they trust. exp0_src asks what the granting slave was driving — so it tests the return mux against the grant. exp0_sig computes SIG + offset from the master's own address, and trusts nothing in the interconnect at all.
Everything here is int unsigned and simulation-only. A silicon version would be narrow counters behind a status register, and none of it is a Wishbone feature — a system that exposes none of it can report only that something is wrong.
The loop closes only if ACTUAL == INTENDED and RECIPIENT == ORIGIN. Both of those are the interconnect's job, and neither is checked by anything in the protocol.
4. Simulation — SIM E: All Four Routes, All Five Agreements
Two pairs of simultaneous reads on the crossbar, arranged so that each master uses each slave.
=== SIM E - five things that must agree ===
ORIGIN, INTENDED, ACTUAL, SOURCE, RECIPIENT.
Every read below is checked against the signature of the
slave its address decodes to, not against the slave that
happened to answer.
M0 -> S0 0x031 read 0xa0000031 expected 0xa0000031
M1 -> S1 0x833 read 0xb1000033 expected 0xb1000033
M0 -> S1 0x835 read 0xb1000035 expected 0xb1000035
M1 -> S0 0x037 read 0xa0000037 expected 0xa0000037
provenance audit, correct crossbar
request reached the wrong slave 0
forward context was not the owner's 0
a phase presented with no owner 0
a master owned two slaves at once 0
owner moved under an open phase 0
a master saw a foreign termination 0
data did not come from the granter 0
data was not the intended slave's 0Reading it
Four reads, four signatures, four matches. M0 → S0 got 0xA0000031; M1 → S1 got 0xB1000033; then the destinations swap and M0 → S1 gets 0xB1000035 while M1 → S0 gets 0xA0000037.
The swap is the interesting half. Between the two pairs each master changed destination, so each slave's owner changed and each master's return source changed — and the audit's eight counters stayed at zero throughout.
Read the audit as three groups. The first four counters are the forward path: destination, context, no-route, exclusivity. The fifth is retention. The last three are the return path, and they are the ones that the next section removes.
Nothing here is surprising, which is exactly why it is worth running before Section 5. A checker that has only ever reported zero has not been shown to report anything.
5. Simulation — SIM F: A Return Path Keyed on the Wrong Thing
Both masters read S0. The broken crossbar routes each master's response from the slave its address currently decodes to, instead of from the slave that granted it. Everything else — the decode, the arbiter, the forward mux, the retention — is untouched.
=== SIM F - a return path keyed on the current decode ===
Both masters read S0. The broken crossbar routes each
master's response from the slave its address CURRENTLY
decodes to, instead of from the slave that granted it.
rig M0 read M1 read S0 reads
correct 0xa0000041 0xa0000042 2
broken 0xa0000042 0xa0000042 1
measure correct broken
foreign termination seen 0 1
data not from the granter 0 0
data not the intended slave's 0 1
terminations at S0 2 1
terminations delivered to masters 2 2
endpoint conformance across BOTH rigs
STB_O without CYC_O, or two terminations at once: 0
-> every master and every slave obeyed RULE 3.25 and
RULE 3.45 in both rigs. One of the two systems is
still wrong, and no endpoint could have known.Reading it — three numbers
M0 read 0xA0000042 in the broken rig, and 0x42 is M1's offset. M0 asked for 0x041 and was handed M1's word.
S0 reads: correct 2, broken 1. The slave performed one read in the broken system and two in the correct one — and yet:
terminations delivered to masters: correct 2, broken 2. Two masters completed on one slave transfer. M0's read never happened at S0 at all; it completed on M1's ACK, with M1's data, because on that clock M0's address also decoded to S0.
Now the line that makes this chapter's point:
STB_OwithoutCYC_O, or two terminations at once: 0 — across both rigs.
Every master and every slave in both systems obeyed RULE 3.25 and RULE 3.45. A protocol checker on any port of either system reports nothing. One of the two systems is wrong and no endpoint could have known, because the defect is a relationship between ports and every rule in B3 is about a single port.
And note which checker caught it. Data not from the granter: broken 0. The structural check missed it — the granting slave genuinely was S0 and S0's data genuinely was what M0 received. The signature check caught it, because it compares against the slave the address intended and the offset the master asked for. That is why both checks are in the probe.
6. The Negative-Control Gate
Five checkers, seven interconnects — two correct, five broken by one named term each — and one stimulus.
=== NEGATIVE-CONTROL GATE ===
one stimulus, seven interconnects. SB = shared bus,
XB = crossbar. Each broken rig differs from its correct
counterpart by exactly one named term.
checker corSB misrt ORdat bcast corXB ctxmx liveR
ROUTE DESTINATION PASS FAIL PASS PASS PASS PASS PASS
DATA PROVENANCE PASS FAIL FAIL FAIL PASS FAIL FAIL
RETURN OWNERSHIP PASS PASS PASS FAIL PASS PASS FAIL
FORWARD CONTEXT PASS PASS PASS PASS PASS FAIL PASS
OWNER STABILITY PASS PASS PASS PASS PASS PASS PASS
raw counts dest sig return fwd stable
correct SB 0 0 0 0 0
misroute 12 5 0 0 0
OR read data 0 6 0 0 0
broadcast 0 5 5 0 0
correct XB 0 0 0 0 0
context mix 0 2 0 5 0
live return 0 3 3 0 0
what each master last read
correct SB M0 0xb1000098 M1 0xb1000099
misroute M0 0xb1000098 M1 0xa0000099
OR read data M0 0xb1000098 M1 0xb1000099
correct XB M0 0xb1000098 M1 0xb1000099
context mix M0 0xb1000098 M1 0xb1000098
live return M0 0xb1000099 M1 0xb1000099
NEGATIVE-CONTROL CHECKERS REQUIRED: >= 4
CHECKERS PASS BOTH CORRECT DUTs: 10/10
CHECKERS FAIL TARGET BROKEN DUT: 5/5
1 ROUTE DESTINATION <- misroute
2 DATA PROVENANCE <- OR-reduced read data
3 RETURN OWNERSHIP <- broadcast termination
4 FORWARD CONTEXT <- same-slave context mixing
5 DATA PROVENANCE <- live return decodeReading it
Both correct interconnects pass all five checkers: 10/10. Every broken one fails the checker aimed at it: 5/5.
The raw counts table is where the defects stop being labels.
misroute fires the destination checker twelve times and the signature checker five times. Twelve clocks on which a slave was in a cycle on behalf of a master whose address decoded elsewhere, producing five reads with the wrong signature. M1 read 0xA0000099 where it should have read 0xB1000099 — the right offset, from entirely the wrong peripheral.
OR read data fires only the signature checker, six times. The destination was right, the context was right, the termination went to the right master — and the data was the bitwise OR of two slaves. In this run the ORed value happened to be readable; in general it is a value neither slave holds.
broadcast fires return ownership five times and the signature checker five times. Two masters completing on one slave's termination, which is Chapter 16.3's defect arriving in a system that now also has a destination to get wrong.
context mix fires the forward-context checker five times and the signature checker twice, and its read row is the clearest single line in the table: M0 0xb1000098 M1 0xb1000098. Two masters, two different addresses, the same word.
live return fires return ownership and the signature checker three times each — SIM F, counted.
And one column is worth a sentence on its own: OWNER STABILITY passes everywhere. All five defects leave retention intact. A retention checker — the whole subject of Chapter 17.5 — catches none of them. These are routing defects, and they need routing checkers.
7. What B3 Says About Return Routing
Almost nothing, and the shape of the silence matters.
RULE 3.45 constrains a slave: "If a SLAVE supports the [ERR_O] or [RTY_O] signals, then the SLAVE MUST NOT assert more than one of the following signals at any time: [ACK_O], [ERR_O] or [RTY_O]." That is one slave's outputs. It says nothing whatever about an interconnect combining two slaves' terminations.
RULE 3.30 is the one that actually helps: "SLAVE interfaces MAY NOT respond to any SLAVE signals when [CYC_I] is negated." Gate CYC per destination and an unselected slave contributes nothing — which is why OR-ing terminations is survivable in a system that gates correctly, and catastrophic in one that does not. "ACK can be ORed because only one slave will respond" is an assumption about your own gating, and it has to be verified rather than assumed.
RECOMMENDATION 3.10 is the only place B3 asks an interconnect for behaviour beyond wiring: "Design INTERCON modules to prevent deadlock conditions." Not "route responses correctly" — deadlock, with a watchdog as the suggested remedy.
And the specification states plainly that master-side and slave-side waveforms differ once an interconnect exists: "the MASTER and SLAVE interfaces can be connected together in different ways... the actual waveforms at the SLAVE may vary from those at the MASTER." Everything in this chapter lives in that variation.
8. When a Destination Has to Be Retained
The question is not "should I add a destination register" but "is the thing I am decoding guaranteed to belong to the open transfer".
On the shared bus: yes, and Chapter 18.1 §3 gave the argument — the shared address is the owner's, the owner is retained for its whole CYC_O (RULE 3.25 plus Chapter 17.5's rule), and ADR_O is qualified by STB_O (RULE 3.60). A destination register there would store what the address already says.
On the crossbar: no. A waiting master's address is not the address of the transfer being answered. The grant is the retained state, and this module keeps it as sown0_q/sown1_q rather than as an explicit per-master destination.
ZipCPU's wbxbar keeps it the other way round — a per-master slave index, mindex[N], with the stated reason being "faster/cheaper logic on the return path, since we can now use a fully populated LUT rather than a priority based return scheme". Same invariant, different encoding, and the choice is about implementation cost.
The invariant is what travels:
The destination associated with an open transfer must not change while that transfer is unanswered, and the return path must be derived from that destination rather than from anything that can move.
9. Failure Modes and Discriminating Evidence
SYMPTOM — a master reads peripheral-looking data from a RAM address.
Candidates. Wrong destination decode. A return mux selecting the wrong slave. Stale or live destination state.
Discriminating evidence. Origin, address, decoded destination, actual destination, response source, data source — in that order. The first pair that disagrees names the stage. With signatured slaves the last one is free.
SYMPTOM — both masters complete on one ACK.
Candidates. Broadcast return. A missing ownership qualifier on one of the termination classes.
Discriminating evidence. Client completions against slave terminations. They must be equal. In SIM F they were 2 and 1. This identity needs no model of the interconnect — two counters.
SYMPTOM — a transfer hangs only for one address range.
Candidates. The decode produces no select. The selected slave never terminates. The return path for that slave is unconnected.
Discriminating evidence. Did any slave's CYC_I assert? If not, it is decode and Chapter 12.6's default-slave question. If yes and nothing came back, it is the return route or the slave.
SYMPTOM — wrong data after a long wait, only under load.
Candidates. A live return decode; the owner or destination moved during the wait.
Discriminating evidence. The destination at issue against the destination at termination, and the granting slave at both moments. Long waits are when this defect becomes reachable — with fast slaves the window is one clock.
SYMPTOM — everything passes and the system is still wrong.
Candidates. You are running a protocol checker on a routing problem.
Discriminating evidence. Section 6's table. Five broken interconnects, every endpoint rule-clean.
10. Verification
Twelve property groups, and the classification is the point.
| # | property | class |
|---|---|---|
| P1 | no master holds two slaves | local interconnect |
| P2 | no phase presented at a slave with no owner | local interconnect |
| P3 | at most one decoded destination per request | local map policy |
| P4 | a slave's phase belongs to a master addressing it | local interconnect |
| P5 | the forward context is one master's, whole | local, motivated by RULE 3.60 |
| P6 | a master sees a termination only from a slave it holds | local interconnect |
| P7 | read data comes from the granting slave | local interconnect |
| P8 | a slave's owner is stable under an open phase | local policy |
| P9 | an open transfer's destination does not move | local policy |
| P10 | STB_O implies CYC_O, at every port | RULE 3.25 |
| P11 | a slave is silent without CYC_I | RULE 3.30 |
| P12 | at most one termination per slave | RULE 3.45 |
Three of twelve are spec-derived. Module 16's ratio was four of thirteen and Module 17's three of fourteen. An interconnect study is almost entirely local by construction, and P12 is in the list mainly to be pointed at: it constrains a slave, and every defect in Section 6 satisfies it.
SVA REVIEWED BY INSPECTION ONLY. Icarus Verilog does not execute concurrent assertions; the property file was run through iverilog -g2012 and rejected. None of the properties was executed as an assertion. Every one has a named procedural equivalent in wb_route_probe that was executed, and those counters are what Sections 4 to 6 report.
Elaboration is not synthesis. All published modules elaborate individually and together under iverilog -g2012, with no duplicate module names. Every always_comb assigns all of its outputs on every path, so none infers a latch; every output has one driver by inspection; and no combinational loop exists — one would not have produced eleven deterministic runs. No synthesis tool was run and no area, timing or frequency claim appears anywhere in this module.
11. Common Mistakes
"Routing the request correctly is enough."
Why it is wrong: SIM F's forward path is perfect. The wrong master completed anyway.
"ACK can be ORed from every slave, because only one will respond."
Why it is dangerous: that sentence is an assumption about your gating, not a fact about slaves. It is true exactly when every unselected slave has CYC_I negated — RULE 3.30 — and false the moment a decode overlaps. The safer model is provenance: select by destination, and check it.
"Read data can be ORed because only one slave drives it."
Why it is wrong in this module's measurement: the OR read data rig fired the signature checker six times. A slave that is not selected still drives its output port, and B3 nowhere requires a slave to drive zeros.
"Protocol-compliant masters and slaves guarantee a correct SoC."
Why it is wrong: Section 6, in one table. Five broken interconnects, every endpoint clean.
"Address decode and routing are the same."
Why it is wrong: the misroute rig has a perfect decoder. Decode is a decision; routing is whether the decision was carried out — in both directions.
"A destination register is always needed." / "A destination register is never needed."
Why both are wrong: the shared bus does not need one and the crossbar does, and the reason is a specific argument about what the decoded address belongs to. Section 8 is the argument; neither slogan survives it.
"If the arbiter is right and the decoder is right, the interconnect is right."
Why it is wrong: context mix has a correct arbiter and a correct decoder, and hands one slave M0's address with M1's data. Two correct decisions can still be carried out incoherently.
12. Interview Reasoning
"What is the difference between decode and routing?"
Decode answers "which slave should this go to". Routing is whether the request got there and whether the answer got back. A correct decoder with a broken forward mux produces a request at the wrong slave; a correct forward path with a broken return mux produces the right transfer reported to the wrong master.
"Why is response routing as important as request routing?"
Because a master has no other way to know. It sees a termination on its own port and believes it. Nothing in a Wishbone response identifies its source — that is why this module's slaves carry signatures, and why a real system cannot.
"How would you prove the correct slave supplied read data?"
Two ways, and use both. Structurally, compare against what the granting slave was driving. By known answer, compare against a value only that slave can produce. SIM F is a defect the first check misses and the second catches.
"How would you prove the correct master received the termination?"
Client completions against slave terminations must be equal, and each completion must coincide with a termination at a slave that master holds. Two counters and one comparison.
"Why can every endpoint pass a Wishbone check while the SoC is wrong?"
Because every rule in B3 is about one port. RULE 3.25 is about a master's CYC_O and STB_O; RULE 3.30 and RULE 3.45 are about a slave's outputs. A routing defect is a relationship between ports and there is no rule about relationships.
"When is OR-ing slave terminations acceptable?"
When you have verified that at most one slave can ever be in a cycle — which is a property of your decode and your gating, not of the slaves. State it as an assumption and check it, because it is exactly the thing an overlapping address map breaks.
13. Understanding Check
In SIM F the slave performed one read and two masters completed. Which of the five agreements broke?
RECIPIENT. The origin, intended, actual and source were all S0 and M1's transfer. M0 was told about a transaction that was not its own.
The structural data check passed on SIM F and the signature check failed. Why keep both?
They trust different things. The structural check asks whether the return mux agrees with the grant — and in SIM F it did, because M0's live decode happened to match the granting slave. The signature check trusts nothing in the interconnect.
OWNER STABILITY passes on all five broken interconnects. What does that tell you about checker selection?
That a checker catches its own class and nothing else. Retention was Module 17's subject and every defect here leaves it intact. A verification plan assembled from the previous module's checkers would have passed all five.
Your interconnect ORs slave ACKs. A colleague says RULE 3.45 makes this safe. Are they right?
No. RULE 3.45 says one slave will not assert two terminations at once. It says nothing about two slaves, and the safety of OR-ing depends entirely on your own guarantee that only one slave is ever in a cycle.
Which single counter would you add first to an interconnect you did not write?
Client completions against slave terminations. It is two counters, needs no model of the topology, and it is the one that catches a broadcast return — the defect that silently corrupts a master that was doing nothing wrong.
14. What's Next
Both topologies now route correctly and both have been proved to, against defects built to break them.
What actually grows when a system gets bigger — and along which dimension does a topology "scale"?
Chapter 18.4 — Scalability counts structure rather than asserting adjectives: contention domains, arbiters, decoders and mux inputs as M and S grow. It also measures a crossbar whose second path is entirely idle, because "we have more paths" and "our traffic uses them" are different claims.
Continue learning
Related tutorials
- Related topic
Bus Ownership
Ownership has two directions, not one. Three targeted interconnect defects measured against one stimulus — and a conformance monitor that finds every one of them faultless.
- Related topic
Shared Bus
Two masters and two slaves on one path, with all four routes measured — and two requests to different slaves that serialise anyway, because that is the topology and not the protocol.
- 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.
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.
