Wishbone · Module 3
Data Flow
One Wishbone access, followed through every block in both directions: what the master drives, where the address changes form, which signals are broadcast and which are decoded, and how read data and termination find their way back to exactly one requester.
Three modules now exist: a master that owns its request, a slave that answers and refuses to know, and an INTERCON that holds the map. This chapter wires them together and follows single accesses through the result.
For one Wishbone transfer, exactly what information moves, in which direction, through which block — and where does it change form?
1. The Complete System
// ─────────────────────────────────────────────────────────────────────────
// wb_soc — the Module 3 system, assembled from the modules built in
// Chapters 3.3, 3.4 and 3.5.
//
// PURPOSE. Show the wiring, which is where the data-flow story actually
// lives. Nothing new is implemented here — this module is composition.
//
// wb_copy_master (3.3) → wb_intercon (3.5) → three wb_gpio_slave (3.4)
//
// The three slaves are instances of the SAME design at three different
// bases. None of them knows its base; the INTERCON's map parameter does.
// That is the reuse property of Chapters 3.4 and 3.5 in one line of code.
//
// Reset is SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_soc #(
parameter int unsigned AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned NS = 3
) (
input logic clk_i,
input logic rst_i,
// Local control of the copy engine
input logic start_i,
input logic [AW-1:0] src_i,
input logic [AW-1:0] dst_i,
input logic [15:0] count_i,
output logic busy_o,
output logic done_o,
output logic error_o,
// Pins of the three peripheral instances
output logic [7:0] p0_out, p1_out, p2_out,
output logic [7:0] p0_oe, p1_oe, p2_oe,
input logic [7:0] p0_in, p1_in, p2_in
);
// ── Master ↔ INTERCON nets ───────────────────────────────────────────
logic m_cyc, m_stb, m_we, m_ack, m_err, m_rty;
logic [AW-1:0] m_adr;
logic [DW/8-1:0] m_sel;
logic [DW-1:0] m_dat_w; // master → slave
logic [DW-1:0] m_dat_r; // slave → master
// ── INTERCON ↔ slave nets. Note what is SHARED and what is PER-SLAVE:
// one address, one write-data bus, one cycle signal, one direction and
// one byte-select bus are broadcast to all three. Only the strobe and
// the response signals are per-slave.
logic s_cyc, s_we;
logic [NS-1:0] s_stb;
logic [AW-1:0] s_adr; // LOCAL offset, already masked
logic [DW/8-1:0] s_sel;
logic [DW-1:0] s_dat_w;
logic [NS*DW-1:0] s_dat_r_flat;
logic [NS-1:0] s_ack, s_err, s_rty;
wb_copy_master #(.AW(AW), .DW(DW), .CW(16)) u_master (
.clk_i(clk_i), .rst_i(rst_i),
.start_i(start_i), .src_i(src_i), .dst_i(dst_i), .count_i(count_i),
.busy_o(busy_o), .done_o(done_o), .error_o(error_o),
.cyc_o(m_cyc), .stb_o(m_stb), .we_o(m_we), .adr_o(m_adr),
.sel_o(m_sel), .dat_o(m_dat_w), .dat_i(m_dat_r),
.ack_i(m_ack), .err_i(m_err), .rty_i(m_rty));
wb_intercon #(.AW(AW), .DW(DW), .NS(NS)) u_intercon (
.clk_i(clk_i), .rst_i(rst_i),
// Master side
.m_cyc_i(m_cyc), .m_stb_i(m_stb), .m_we_i(m_we), .m_adr_i(m_adr),
.m_sel_i(m_sel), .m_dat_i(m_dat_w), .m_dat_o(m_dat_r),
.m_ack_o(m_ack), .m_err_o(m_err), .m_rty_o(m_rty),
// Slave side
.s_cyc_o(s_cyc), .s_stb_o(s_stb), .s_we_o(s_we), .s_adr_o(s_adr),
.s_sel_o(s_sel), .s_dat_o(s_dat_w), .s_dat_i_flat(s_dat_r_flat),
.s_ack_i(s_ack), .s_err_i(s_err), .s_rty_i(s_rty));
// ── Three instances of ONE slave design, at three bases the slaves do
// not know. Every port below except the strobe and the responses is
// the same net on all three instances.
wb_gpio_slave #(.AW(AW), .DW(DW), .PINS(8)) u_gpio0 (
.clk_i(clk_i), .rst_i(rst_i),
.cyc_i(s_cyc), .stb_i(s_stb[0]), .we_i(s_we), .adr_i(s_adr),
.sel_i(s_sel), .dat_i(s_dat_w), .dat_o(s_dat_r_flat[0*DW +: DW]),
.ack_o(s_ack[0]), .err_o(s_err[0]), .rty_o(s_rty[0]),
.pins_out(p0_out), .pins_oe(p0_oe), .pins_in(p0_in));
wb_gpio_slave #(.AW(AW), .DW(DW), .PINS(8)) u_gpio1 (
.clk_i(clk_i), .rst_i(rst_i),
.cyc_i(s_cyc), .stb_i(s_stb[1]), .we_i(s_we), .adr_i(s_adr),
.sel_i(s_sel), .dat_i(s_dat_w), .dat_o(s_dat_r_flat[1*DW +: DW]),
.ack_o(s_ack[1]), .err_o(s_err[1]), .rty_o(s_rty[1]),
.pins_out(p1_out), .pins_oe(p1_oe), .pins_in(p1_in));
wb_gpio_slave #(.AW(AW), .DW(DW), .PINS(8)) u_gpio2 (
.clk_i(clk_i), .rst_i(rst_i),
.cyc_i(s_cyc), .stb_i(s_stb[2]), .we_i(s_we), .adr_i(s_adr),
.sel_i(s_sel), .dat_i(s_dat_w), .dat_o(s_dat_r_flat[2*DW +: DW]),
.ack_o(s_ack[2]), .err_o(s_err[2]), .rty_o(s_rty[2]),
.pins_out(p2_out), .pins_oe(p2_oe), .pins_in(p2_in));
endmoduleThe line worth pausing on is the instantiation. Three instances of one design, differing only in which strobe bit they receive and which pins they drive. Not one of them contains an address constant, and moving any of them is a change to the INTERCON's BASE_FLAT parameter alone.
That is the whole architecture paying off, and it is only possible because Chapter 3.4's slave refused to know its base and Chapter 3.5's INTERCON accepted the map.
2. The Two Paths
One thing changes form, and it changes in one place. A system address enters the INTERCON; a local offset leaves it. Nothing else is transformed — write data, direction and byte selects pass through untouched, and read data and terminations come back untouched.
That single transformation is where most integration bugs live, because it is the only point where two halves of the system hold different ideas about what an address means. Section 5's debugging order is built around it.
3. Case A — the Master Writes a GPIO Output Register
Base 0x4000_1000 is u_gpio1; offset 0x04 is OUT. The master writes 0x0000_00FF to 0x4000_1004.
Step by step, with the block that owns each step:
| # | What happens | Owner | Established in |
|---|---|---|---|
| 1 | Request latched; Wishbone outputs driven from registers | MASTER | 3.3 |
| 2 | CYC_O and STB_O asserted; request held until termination | MASTER | RULE 3.60 |
| 3 | 0x40001 matches region 1 → hit[1] | INTERCON | 3.5 |
| 4 | s_adr_o masked to 0x004 | INTERCON | the form change |
| 5 | CYC, DAT, WE, SEL broadcast to all three | INTERCON | 3.5 §2 |
| 6 | s_stb_o[1] asserted; [0] and [2] low | INTERCON | the selection |
| 7 | cyc_i & stb_i true → xfer; offset 0x04 = OUT; sel_i[0] set → out_q written | SLAVE 1 | RULE 3.35 |
| 8 | ack_o asserted; err_o low | SLAVE 1 | RULE 3.45 |
| 9 | Reduction selects slave 1's ACK → m_ack_o | INTERCON | 2.3 |
| 10 | ACK_I observed → cycle closed, done_o pulses | MASTER | 3.3 |
Two observations worth extracting.
Slaves 0 and 2 saw everything except a strobe. They received the address, the write data, the direction and CYC_I — and did nothing, because cyc_i & stb_i was false for them. That is what makes broadcasting safe.
The write data was never decoded, masked or multiplexed. It went out untouched and was consumed by exactly one slave. Only the address changed, and only in the INTERCON.
4. Case B — the Master Reads a Status Register
Same system; the master reads 0x4000_2008, which is u_gpio2's read-only IN register.
Steps 1 through 6 are identical in shape — decode selects region 2, the offset becomes 0x008, s_stb_o[2] is asserted. The return path is where this case differs, and it is where the work is.
| # | What happens | Owner |
|---|---|---|
| 7 | xfer true; we_i low; offset 0x08 = IN; dat_o driven with in_sync_q, zero-extended | SLAVE 2 |
| 8 | Slaves 0 and 1 drive dat_o = '0, because xfer is false for them | SLAVES 0, 1 |
| 9 | ack_o asserted by slave 2 only | SLAVE 2 |
| 10 | AND-OR reduction: m_dat_o = (0 | 0 | slave2_data) | INTERCON |
| 11 | ACK_I and DAT_I presented to the master in the same cycle | INTERCON |
| 12 | hold_q <= dat_i on the ACK_I edge — and nowhere else | MASTER |
Step 8 is the one that is easy to skip and expensive to get wrong. The reduction is an OR. If slave 0 or 1 drove anything non-zero while unselected, it would be ORed into slave 2's value and the master would receive a number belonging to neither. Nothing would report an error.
The INTERCON cannot prevent this — it can select, but it cannot silence a slave that insists on driving. That is exactly why Chapter 3.4's property P3 is bound at the slave: the obligation sits where the capability sits.
Step 12 is the master's half of the same discipline. Read data is meaningful in the termination cycle. A capture one cycle later collects whatever the reduction produced next — which, once the master has dropped its strobe, is zero.
5. Debugging — Walk the Path in Order
An access that misbehaves can fail at any of six points. Walking them in a fixed order turns a search into four observations.
Step 1 — did the master present a legal request? Probe m_cyc_i, m_stb_i, m_adr_i. If the address is wrong, the fault is upstream of Wishbone entirely — in whatever computed it. If the address changes while the strobe is asserted and nothing has terminated, it is a master stability violation (Chapter 3.3, P2) and the peripheral is blameless.
Step 2 — did the decode select the right slave? Probe s_stb_o. This single vector separates four causes. All zero on a mapped address means the map constants are wrong. Two bits set means overlapping regions. One wrong bit means a wrong base. All bits set means a broadcast strobe rather than a decoded one.
Step 3 — did the address change form correctly? Compare s_adr_o with m_adr_i. Equal means the mask is missing and every register will appear shifted by the base. Masked by the wrong region's size means the offset loop and the hit index disagree.
Step 4 — did the slave act? Probe the slave's cyc_i, stb_i and its internal state. Both qualifiers asserted with no state change means the slave's own decode rejected the offset — check err_o, which is the slave telling you so.
Step 5 — did the slave terminate, and only the right one? Probe all three slaves' ack_o/err_o. A termination from an unstrobed slave is a RULE 3.35 violation in that slave — it is ignoring CYC_I, or terminating on something else entirely.
Step 6 — did the response reach the master intact? Compare the master's DAT_I against the selected slave's DAT_O in the termination cycle. Differing, with the value looking like two registers ORed together, means an unselected slave is driving.
6. Where the Critical Path Runs
The composition in Section 1 has one long combinational cone, and knowing its shape is more useful than knowing its length.
master address register
→ INTERCON comparators (parallel, cheap, flat with slave count)
→ strobe + offset mask
→ slave's internal decode and read mux
→ slave's DAT_O and termination
→ INTERCON response reduction (grows with slave count, in steps)
→ master's capture registerWhat grows and what does not. The comparators are parallel: adding a slave adds one, and nothing gets deeper. The response reduction takes one more input per slave, on every bit of the data width — the fan-in asymmetry from Chapter 2.3.
Two honest limits on what can be claimed here.
The specification says nothing about timing closure. How many slaves a flat INTERCON supports before this path becomes the critical one depends on the device, the clock target, the data width and the synthesiser. Anyone quoting a number without a synthesis report for the part in question is guessing.
Not every Wishbone implementation has this structure. A slave may register its termination and read data rather than producing them combinationally, which shortens this cone at the cost of a cycle. Chapter 3.4's slave chose the combinational form because it is never busy, and a design that needs the timing can register instead without changing any interface. A sentence in the specification is often quoted against this — the CLK_I signal description says all Wishbone output signals "are registered at the rising edge of CLK_I". It is descriptive text rather than a numbered rule, and the numbered requirements it is sometimes confused with (RULES 5.00 and 5.05) ask for synchronous methodology under one clock, which a combinational termination satisfies. Chapter 4.1 works through the distinction.
What is safe to say: the response path is the structure that grows, and both standard fixes — registering the response, or going hierarchical — cost a cycle and are affordable only because Wishbone signals termination rather than fixing a latency.
7. Common Mistakes
"The interconnect transforms the data."
Wrong mental model: everything passing through the fabric is processed.
Concrete bug: byte-lane rearrangement or width adaptation added inside the INTERCON "while we are here", which then applies to every slave whether or not it wanted it.
Observable evidence: a peripheral that works standalone and returns byte-swapped values in the system.
Correct model: exactly one thing changes form — the address, masked to a local offset. Write data, direction, byte selects, read data and terminations pass through untouched.
"An unselected slave's read data does not matter."
Wrong mental model: the multiplexer ignores it.
Concrete bug: on an AND-OR reduction, a slave driving non-zero DAT_O while unstrobed ORs into the selected slave's value.
Observable evidence: reads returning a value sharing bits with two different registers.
Correct model: the INTERCON selects but cannot silence. Driving '0 when unqualified is the slave's obligation, which is why the property is bound there.
"If the slave has the right data, the read will work."
Wrong mental model: correctness of the value is sufficient.
Concrete bug: the master captures DAT_I a cycle after the termination, by which time the reduction is producing zero.
Observable evidence: every read returns zero, or the previous access's value — a clean off-by-one that looks like a software bug.
Correct model: read data is meaningful in the termination cycle. The value and the moment are both part of the contract.
"Trace the failing access from the slave backwards."
Wrong mental model: the wrong value came out of the slave, so start there.
Concrete bug: hours spent in a peripheral's register logic when the decode selected the wrong slave in the first place.
Observable evidence: a peripheral that reads correctly in its own testbench.
Correct model: walk forward. Request before response, because a wrong response to a wrong request tells you nothing. s_stb_o is the single highest-information probe in the system.
8. Interview Reasoning
At the master: the request is latched and the Wishbone outputs driven from registers rather than from the client's live signals, so stability is structural. CYC_O and STB_O are asserted and everything is held until a termination arrives.
At the INTERCON, four things happen. The address is compared against the region table. The upper bits are masked away, so what leaves is a local offset — the only thing in the whole access that changes form. The cycle signal, write data, direction and byte selects are broadcast to every slave. And exactly one strobe is asserted.
At the selected slave: CYC_I and STB_I are both true, so this is a transfer for it. It decodes the local offset, honours the byte selects, writes the register, and asserts exactly one termination.
On the way back: the INTERCON multiplexes that slave's termination down to the master's single response port. The master observes it, closes the cycle, and reports completion.
The detail that shows real understanding: the unselected slaves saw the address, the write data and CYC_I, and did nothing — because CYC_I & STB_I was false for them. That is what makes broadcasting safe, and it is why only the strobe needs decoding.
9. Understanding Check
10. What's Next
Data flow is now traceable in both directions: the request fans out with one transformation, the response fans in with none, and six ordered probes localise any fault to one block.
What the traces have described is where things go. They have not described the transfer as a thing with a beginning, a duration and an end — which is what a master's state machine actually reasons about, and what an arbiter in a larger system would observe.
What is the high-level lifetime of one Wishbone transaction, from a master's decision to act through the slave's termination and back to the caller — and what may vary along the way without breaking anything?
Chapter 3.7 — Transaction Lifecycle closes Module 3 and runs the whole system in simulation. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- 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
Read Cycle Flow
One Wishbone read followed through nine stages from a client request to a returned value: what each stage contributes, why the forward and return paths are asymmetric, and where a read that hangs actually stopped.
- Related topic
Write Cycle Flow
A read asks a question and the slave supplies the answer; a write carries the answer with it and changes the device. One Wishbone write from client intent to committed register, and why the ownership reversal changes everything downstream.
- 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.
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.
