Wishbone · Module 7
Data Transfer
Write data leaves the master, crosses the bus and lands in specific bytes of one register. The byte-lane mask is the only place where data the transfer never mentioned can be destroyed.
Chapter 7.1 drew the byte mask as one box in the write path. It is the stage where a write either lands in the bits it named or destroys the ones beside them.
How does write data travel from the requester into the correct bits of the correct register?
1. The Forward Payload Path
| Stage | Owner | Characteristic failure | Evidence |
|---|---|---|---|
| Client | client | asked for the wrong value | bus data matches the request; the request was wrong |
| Capture | master | payload not latched | bus data drifts mid-transfer (7.4) |
DAT_O | master | — | qualified by STB_O under RULE 3.60 |
| Byte mask | slave | ignores or misreads SEL_I | neighbouring bytes change |
| Register | slave | wrong case arm | a different register changes |
Row four is unique to writes. Every other row puts the wrong value somewhere the transfer named. That one damages data the transfer never mentioned, which is why its symptom appears in unrelated code.
2. Data Belongs to a Transaction, Not to a Wire
DAT_O carries a level on every cycle, exactly as DAT_I did in Chapter 6.3. The question is never "is there data on the bus" but "does it belong to the transfer being presented".
RULE 3.60 — "MASTER interfaces MUST qualify the following signals with [STB_O]: [ADR_O], [DAT_O()], [SEL_O()], [WE_O], and [TAGN_O]."
And §3.2.2 is more specific for a write. RULE 3.75 makes its timing normative, and that timing requires the master's address, data, WE_O and byte selects to remain valid until the rising clock edge following the negation of STB_O.
Read that carefully — it is stronger than "stable while the strobe is asserted." The payload must still be coherent at the edge that ends the transfer, which is the edge at which Chapter 7.1's slave commits. A payload that decayed one edge early would decay exactly when the slave needed it.
3. The Byte-Lane Mask
SEL_O has one bit per byte lane of the data bus. Chapter 4.7 established the mapping as a statement about wires: SEL_O[n] corresponds to DAT[8n+7 : 8n], always, and RULES 3.90 and 3.100 bind the data organisation to the specification's tables.
On a read the mask says which lanes the master will believe. On a write it says which lanes the slave will replace — and by omission, which it must leave alone.
The intuitive form first, because it is the one to reach for:
for each lane n:
if SEL[n]:
register[lane n] <= write_data[lane n]
// else: not assigned at all, so the lane keeps its valueThe unselected lane is not written with anything. It is not written with zero, not written with the old value explicitly — it is simply not the target of an assignment, so the flop holds. That is why a per-lane conditional cannot corrupt a neighbour: there is no code path that touches it.
The mask formulation computes the same thing as one expression:
lane_mask = {{8{SEL[3]}}, {8{SEL[2]}}, {8{SEL[1]}}, {8{SEL[0]}}}
next = (write_data & lane_mask) | (register & ~lane_mask)Both are correct. The per-lane loop is what this module uses, because a reviewer can see at a glance that unselected lanes are untouched. The mask version makes that a property of two bitwise operations, which is harder to check by eye and easier to get subtly wrong — invert one ~ and every unselected byte zeroes.
What must never appear is the whole-word assignment:
register <= write_data; // ignores SEL entirelyThat is correct only for a full-word write and destroys three bytes on every narrow one.
4. RTL — The Masked Update, Isolated
// ─────────────────────────────────────────────────────────────────────────
// wb_write_lane — the byte-lane masked update, pulled out on its own.
//
// PURPOSE. This is stage 4 of Figure 1, isolated from the surrounding slave
// so the update rule is reviewable without the qualification logic around
// it. It is a pure function: old value + new value + lane mask -> result.
//
// NOT A WISHBONE SLAVE — no clock, no qualifiers, terminates nothing.
//
// Both formulations from Section 3 appear here so they can be compared,
// and an assertion-free equivalence check is left to Section 8's property.
// ─────────────────────────────────────────────────────────────────────────
module wb_write_lane #(
parameter int unsigned DW = 32
) (
input logic [DW-1:0] old_i,
input logic [DW-1:0] wdat_i,
input logic [DW/8-1:0] sel_i,
output logic [DW-1:0] next_o, // per-lane form (what we use)
output logic [DW-1:0] next_mask_o // mask form (for comparison)
);
localparam int unsigned NL = DW/8;
// ── PER-LANE FORM ─────────────────────────────────────────────────────
// next_o is seeded with the OLD value, then selected lanes are
// overwritten. Seeding first is what makes the unselected lanes keep
// their contents, and it is also what prevents a latch: every bit of
// next_o is assigned on every path through the block.
always_comb begin
next_o = old_i;
for (int unsigned n = 0; n < NL; n++) begin
if (sel_i[n]) next_o[n*8 +: 8] = wdat_i[n*8 +: 8];
end
end
// ── MASK FORM ─────────────────────────────────────────────────────────
// Identical result, built from a replicated lane mask. Shown because it
// appears in a great deal of production RTL and a reader should be able
// to recognise it — but note how much harder it is to confirm by eye
// that an unselected lane survives. Dropping the `~` on the second term
// zeroes every unselected byte and still looks plausible.
logic [DW-1:0] lane_mask;
always_comb begin
for (int unsigned n = 0; n < NL; n++) begin
lane_mask[n*8 +: 8] = {8{sel_i[n]}};
end
end
assign next_mask_o = (wdat_i & lane_mask) | (old_i & ~lane_mask);
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_write_regs_lane — the running peripheral rebuilt around wb_write_lane.
//
// PURPOSE. Show the composition explicitly: qualification, a commit event,
// and a masked update that is now a separately reviewable block.
//
// Functionally identical to Chapter 7.1's wb_write_regs. The difference is
// that the update rule is no longer buried in a for-loop inside an
// always_ff, so it can be argued about — and tested — on its own.
//
// DECLARED COMMIT POLICY (LOCAL, unchanged from Chapter 7.1):
// commits the addressed register on the same rising clock edge for which
// it returns ACK_O for a qualified write.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_write_regs_lane #(
parameter int unsigned OFF_AW = 4,
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,
input logic [DW/8-1:0] sel_i,
output logic [DW-1:0] dat_o,
output logic ack_o,
output logic err_o,
output logic [DW-1:0] control_o,
output logic [DW-1:0] outdata_o
);
localparam logic [OFF_AW-1:0] O_CTRL = 4'd2;
localparam logic [OFF_AW-1:0] O_OUT = 4'd6;
localparam logic [OFF_AW-1:0] O_ID = 4'd4;
localparam logic [DW-1:0] ID_VALUE = 32'h5742_0701;
logic [DW-1:0] ctrl_q, outdata_q;
assign control_o = ctrl_q;
assign outdata_o = outdata_q;
logic xfer;
assign xfer = cyc_i & stb_i; // RULES 3.30 & 3.35
logic writable, known_off;
always_comb begin
unique case (adr_i)
O_CTRL, O_OUT: begin known_off = 1'b1; writable = 1'b1; end
O_ID: begin known_off = 1'b1; writable = 1'b0; end
default: begin known_off = 1'b0; writable = 1'b0; end
endcase
end
logic illegal;
assign illegal = ~known_off | (we_i & ~writable);
assign err_o = xfer & illegal;
assign ack_o = xfer & ~illegal;
logic commit;
assign commit = xfer & we_i & ack_o; // the declared policy
// ── THE MASKED UPDATE, ONE INSTANCE PER REGISTER ──────────────────────
// Each register gets its own lane block. The register's own current
// value goes in as `old_i`, so a lane the write did not select comes
// back out unchanged and the flop reloads what it already held.
logic [DW-1:0] ctrl_next, out_next;
wb_write_lane #(.DW(DW)) u_ctrl_lane (
.old_i(ctrl_q), .wdat_i(dat_i), .sel_i(sel_i),
.next_o(ctrl_next), .next_mask_o());
wb_write_lane #(.DW(DW)) u_out_lane (
.old_i(outdata_q), .wdat_i(dat_i), .sel_i(sel_i),
.next_o(out_next), .next_mask_o());
always_ff @(posedge clk_i) begin
if (rst_i) begin
ctrl_q <= '0;
outdata_q <= '0;
end else if (commit) begin
// Only the ADDRESSED register loads. The other lane block is still
// computing a result every cycle — that costs nothing and is simply
// not used, which is the normal shape for a small register file.
unique case (adr_i)
O_CTRL: ctrl_q <= ctrl_next;
O_OUT: outdata_q <= out_next;
default: ; // unreachable under commit
endcase
end
end
always_comb begin
dat_o = '0; // RULE 3.65
if (xfer && !we_i && ack_o) begin
unique case (adr_i)
O_CTRL: dat_o = ctrl_q;
O_OUT: dat_o = outdata_q;
O_ID: dat_o = ID_VALUE;
default: dat_o = '0;
endcase
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_write_regs_nosel — THE IGNORED-SEL BUG, isolated for simulation.
//
// NOT A REFERENCE DESIGN. Identical to wb_write_regs_lane except that the
// commit assigns the WHOLE WORD instead of the masked result:
//
// ctrl_q <= dat_i; instead of ctrl_q <= ctrl_next;
//
// Every bus signal it produces is conformant. It acknowledges correctly,
// it refuses read-only offsets correctly, and for a FULL-WORD write it is
// indistinguishable from the correct slave. Only a narrow write exposes
// it, and what it destroys is data the transfer never named.
// ─────────────────────────────────────────────────────────────────────────
module wb_write_regs_nosel #(
parameter int unsigned OFF_AW = 4,
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,
input logic [DW/8-1:0] sel_i,
output logic [DW-1:0] dat_o,
output logic ack_o,
output logic err_o,
output logic [DW-1:0] control_o,
output logic [DW-1:0] outdata_o
);
localparam logic [OFF_AW-1:0] O_CTRL = 4'd2;
localparam logic [OFF_AW-1:0] O_OUT = 4'd6;
logic [DW-1:0] ctrl_q, outdata_q;
assign control_o = ctrl_q;
assign outdata_o = outdata_q;
logic xfer; assign xfer = cyc_i & stb_i;
logic writable;
always_comb begin
unique case (adr_i)
O_CTRL, O_OUT: writable = 1'b1;
default: writable = 1'b0;
endcase
end
assign err_o = xfer & we_i & ~writable;
assign ack_o = xfer & ~(we_i & ~writable);
logic commit; assign commit = xfer & we_i & ack_o;
always_ff @(posedge clk_i) begin
if (rst_i) begin
ctrl_q <= '0; outdata_q <= '0;
end else if (commit) begin
// ── THE BUG: sel_i is never consulted ───────────────────────────
unique case (adr_i)
O_CTRL: ctrl_q <= dat_i;
O_OUT: outdata_q <= dat_i;
default: ;
endcase
end
end
assign dat_o = '0; // read path omitted
endmoduleReading the group
Purpose. The lane block is the update rule alone; the composed slave shows it in place; the third slave omits it so the damage can be measured.
Interface. wb_write_lane is a pure combinational function — no clock, no reset, no qualifiers. The two slaves present identical Wishbone interfaces.
State. Lane block: none, so no reset is needed and none is present. A reviewer checking RULE 3.20 should confirm a block is stateless rather than hunt for a missing reset. The slaves hold two registers each.
Combinational logic. Lane block: the seeded per-lane overwrite and the mask formulation. Slaves: xfer, legality, two exclusive terminations, commit, and two lane instances.
Sequential logic. One masked load per commit, into the addressed register only.
Write start. The slave has no start event — only the first cycle in which xfer is true. commit is the event it manufactures, and it is true for exactly one cycle per transfer.
Address. adr_i, the local offset from the decode (Chapter 6.2).
Write data. dat_i, consumed in the commit cycle.
Byte enables. sel_i, consumed in the same cycle by the lane block.
Commit. xfer & we_i & ack_o, the declared policy from Chapter 7.1.
Waiting. Neither slave waits. Chapter 7.4 adds latency, and the ack_o term in commit stops being redundant there.
Reset. Synchronous, active high, per RULES 2.30 and 3.00.
Failure modes. Section 7.
Simplifications. The mask formulation's output is left unconnected in the composed slave — it exists to be compared in simulation, not to be used twice. wb_write_regs_nosel omits the read path entirely since it exists only to be written to.
5. Waveform — One Byte In, Three Bytes Preserved
A narrow write: SEL selects one lane
7 cyclesSEL_O = 0010 selects lane 1, which is DAT[15:8] — the byte 0xCC of 0xAABBCCDD.
CONTROL goes 0x11223344 → 0x1122CC44. Byte 1 became 0xCC; bytes 0, 2 and 3 kept 0x44, 0x22 and 0x11.
What wb_write_regs_nosel shows instead is 0xAABBCCDD — the whole word, with three bytes of unrelated state destroyed. The bus trace is identical in both cases, which Section 8 measures.
Note the lane arithmetic, because it is the step people get backwards. Lane 1 is bits 15 down to 8, not bits 8 up to 15 of the value — the mapping is positional on the bus. Chapter 4.7 §2 covered the endianness question this raises, and it is a system decision made in the master, not in the slave.
6. Simulation — Partial Write and the Ignored Mask
Simulation C — the correct slave.
=== SIMULATION C - partial write ===
CONTROL seeded 0x11223344
write DAT=0xaabbccdd SEL=0010
per-lane result 0x1122cc44
mask-form result 0x1122cc44 (identical)
CONTROL after commit 0x1122cc44
bytes changed 1
bytes preserved 3Only lane 1 moved, and the two formulations from Section 3 agreed exactly — which is worth knowing, because the mask form is what a reader will meet in production RTL.
Simulation C2 — the same stimulus against the ignored-SEL slave.
=== the same write, SEL ignored ===
CONTROL seeded 0x11223344
CONTROL after commit 0xaabbccdd
bytes changed 4
bytes destroyed that the write never named 3
bus-trace differences between the two slaves: 0Three bytes of unrelated state were destroyed, and the two slaves produced byte-identical bus traces. A protocol checker attached to either passes.
This is the write-side twin of Chapter 6.3's finding. There, a conformant bus hid a master that captured at the wrong edge. Here it hides a slave that overwrote data nobody asked it to touch — and unlike the read case, the damage is permanent.
7. Failure Modes and Discriminating Evidence
Symptom: the right register holds the wrong value; all four bytes are wrong.
Candidate causes. The master captured the wrong data, or the client asked for the wrong value.
Discriminating evidence. Compare req_dat_i in the acceptance cycle against DAT_O on the bus. Equal means the client asked for it; different means the master's capture is at fault. One comparison splits them.
Likely RTL location. The master's dat_q load, or nothing — the bug may be upstream of the bus entirely.
Symptom: the right register holds the wrong value; only some bytes are wrong.
Candidate causes. The byte mask selected the wrong lanes, or the lane mapping is reversed.
Discriminating evidence. Compare SEL_O against which bytes actually moved. If lane 1 was selected and lane 2 changed, the mapping is off by one; if lane 3 changed when lane 0 was selected, the lanes are reversed — which is the endianness confusion Chapter 4.7 §2 warned about, and it lives in the master.
Symptom: bytes the write never mentioned changed.
Candidate causes. The commit assigns the whole word and ignores SEL_I.
Discriminating evidence. Seed the register with a known non-zero value, write one byte, read back. Neighbouring bytes taking the write data's bytes is conclusive — and the giveaway is that they take the write data's bytes rather than zero.
Likely RTL location. The commit assignment. Look for reg_q <= dat_i with no mask.
Property. P1 in Section 8.
Symptom: a different register changed.
Candidate causes. Wrong address on the bus, wrong decode, or a wrong case arm in the commit.
Discriminating evidence. ADR_O, then the decoded select, then the local offset, then which register's enable asserted. The first mismatch names the stage — the same walk Chapter 6.2 §7 used for reads.
Symptom: nothing changed and the transfer succeeded.
Candidate causes. SEL_O was all-zero.
Discriminating evidence. SEL_O = 0000 with ACK returned. A legal transfer that selects no lanes commits nothing — the slave did exactly what it was told. Chapter 4.7 §4 treated an empty mask as almost always a master bug and worth erroring on.
Symptom: a write to a read-only register silently succeeds.
Candidate causes. The writable term omits that offset.
Discriminating evidence. Check ERR_O for that transfer. Absent means the slave never classified it as read-only, which is a legality bug rather than a data-path one.
8. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_write_data_checker — payload and masking properties.
//
// P1 is a DESIGN OBLIGATION derived from the SEL_O() signal description
// (SEL names where valid data is PLACED on a write) — the specification
// constrains what SEL MEANS, and "therefore an unselected lane must not
// change" is a sound inference about a slave's internal state rather than
// a stated rule. P2 and P3 are SPECIFICATION. P4 is LOCAL POLICY.
// ─────────────────────────────────────────────────────────────────────────
module wb_write_data_checker #(
parameter int unsigned DW = 32,
parameter int unsigned NL = 4
) (
input logic clk_i,
input logic rst_i,
input logic cyc_o,
input logic stb_o,
input logic we_o,
input logic [DW-1:0] dat_o,
input logic [NL-1:0] sel_o,
input logic ack_i,
input logic err_i,
input logic rty_i,
// white-box, slave side
input logic commit,
input logic [DW-1:0] ctrl_q,
input logic [DW-1:0] ctrl_prev // ctrl_q one cycle earlier
);
default disable iff (rst_i);
// P1 — DESIGN OBLIGATION, and the property this chapter exists for.
// An UNSELECTED lane of the committed register must be unchanged.
// Generated per lane so a failure names WHICH lane was destroyed,
// turning "the register is wrong" into a one-line diagnosis.
generate
for (genvar n = 0; n < NL; n++) begin : g_lane
property p_unselected_lane_preserved;
@(posedge clk_i)
(ctrl_q[n*8 +: 8] != ctrl_prev[n*8 +: 8]) |-> $past(sel_o[n]);
endproperty
a_unselected_lane_preserved :
assert property (p_unselected_lane_preserved)
else $error("lane %0d of CONTROL changed while unselected", n);
end
endgenerate
// P2 — SPECIFICATION (RULE 3.60, and §3.2.2 via RULE 3.75). The whole
// payload holds still while the write is outstanding. The end
// boundary is the termination: omitting it would forbid a legal
// back-to-back write on the next cycle, which is the over-strong
// trap Chapter 4.6 Section 10 described.
property p_payload_stable;
@(posedge clk_i) (cyc_o && stb_o && we_o && !(ack_i||err_i||rty_i))
|=> ($stable(dat_o) && $stable(sel_o) && $stable(we_o));
endproperty
a_payload_stable : assert property (p_payload_stable)
else $error("RULE 3.60 / 3.2.2: write payload moved while outstanding");
// P3 — SPECIFICATION (RULE 3.60). A write presents a non-empty payload
// qualification. NOT a rule that SEL must be non-zero — an empty
// mask is legal and simply commits nothing — so this checks only
// that SEL is driven, never unknown, while qualified.
property p_sel_driven;
@(posedge clk_i) (cyc_o && stb_o) |-> !$isunknown(sel_o);
endproperty
a_sel_driven : assert property (p_sel_driven)
else $error("SEL_O unknown while a transfer was presented");
// P4 — LOCAL POLICY. State changes only at a commit event, checked
// against the slave's own declared term rather than against ACK_I
// (Chapter 7.1 Section 11 explains why that distinction matters).
property p_state_only_on_commit;
@(posedge clk_i) (ctrl_q != ctrl_prev) |-> $past(commit);
endproperty
a_state_only_on_commit : assert property (p_state_only_on_commit)
else $error("LOCAL: register changed outside a commit event");
endmoduleP1 is generated per lane deliberately, for the reason Chapter 4.7 §7 gave: a single property over the whole word reports "data changed" and leaves the engineer to work out which byte and why. Per-lane generation names the lane in the message, and the lane number maps directly onto a SEL bit and a DAT range.
P1 needs white-box access and there is no alternative — the bus during the ignored-SEL bug is fully conformant, as Section 6 measured. That is the sixth time this course has met that boundary, after lost atomicity (4.9), repeated writes (5.3), the RULE 3.55 stall (5.4), capture-edge bugs (6.3) and repeated read side effects (6.5). Chapter 5.8 §8 collected the pattern.
Tooling limitation. Icarus Verilog has no SVA support and cannot execute any of these. They are reviewed by inspection; all three modules are elaborated and both simulations in Section 6 were run.
9. Common Mistakes
"SEL can be ignored on a 32-bit bus."
Wrong mental model: everything writes whole words anyway.
Concrete bug: the whole-word assignment. Correct until the first narrow write, then it destroys three bytes.
Observable evidence: neighbouring bytes taking the write data's bytes, with a perfectly conformant bus trace.
Correct model: SEL_O() names where valid data is placed on a write. Ignoring it is only safe if the interface contract says partial writes are unsupported — and that is a claim about today's driver, not about the interface.
"Partial write means the unselected bytes become zero."
Wrong mental model: the mask clears what it does not select.
Concrete bug: a mask formulation with a dropped ~, or an update that seeds next with zero instead of the old value.
Observable evidence: unselected bytes reading zero rather than their previous contents — which looks like the whole-word bug but is subtly different, and the difference tells you which line to fix.
Correct model: unselected lanes are not assigned at all. Seeding with the old value is what expresses that.
"Once the data is on DAT_O, the slave owns it."
Wrong mental model: the bus hands data over.
Concrete bug: a master that changes DAT_O mid-transfer, believing the slave already has it.
Observable evidence: a committed value that matches neither the requested one nor anything the client currently holds.
Correct model: the data belongs to a qualified transaction, and §3.2.2 requires it to stay valid until the edge after the strobe negates. A combinational slave has nowhere to keep an early copy — PERMISSION 3.10 describes exactly such a slave.
10. Interview Reasoning
The implementation I would write:
always_comb begin
next = reg_q; // seed with the OLD value
for (int n = 0; n < 4; n++)
if (sel[n]) next[n*8 +: 8] = wdat[n*8 +: 8];
end
...
if (commit) reg_q <= next;The argument for a reviewer is the seed line. next starts as the current contents, so a lane the write did not select is never the target of an assignment and comes back out unchanged. There is no code path that touches it — that is a stronger claim than "the mask happens to preserve it", and a reviewer can check it by eye in one line.
It also prevents a latch, because every bit of next is assigned on every path through the always_comb.
The mask formulation is equivalent and I would recognise it but not reach for it:
lane_mask = {{8{sel[3]}}, {8{sel[2]}}, {8{sel[1]}}, {8{sel[0]}}};
next = (wdat & lane_mask) | (reg_q & ~lane_mask);Same result — I measured both forms agreeing exactly. But confirming that an unselected byte survives now requires reasoning about two bitwise terms and one inversion, and dropping that ~ zeroes every unselected byte while still looking entirely plausible.
What I would put in the testbench, because the failure is invisible otherwise. Seed the register with a distinctive non-zero value — 0x11223344, not zero — then write one byte and check the other three. Seeding with zero hides the whole-word bug completely, because zeroed neighbours and preserved-zero neighbours are the same bits.
And the assertion, generated per lane so a failure names the byte:
(reg_q[n*8 +: 8] != reg_prev[n*8 +: 8]) |-> $past(sel[n])Why the assertion matters more here than on a read. A bus-level checker cannot see this at all — I measured zero trace differences between the correct slave and one that ignores SEL entirely. The damage is to internal state, and it is permanent.
11. Understanding Check
12. What's Next
The payload now reaches the right bits of the right register, with the lanes the transfer never named left alone.
Both slaves so far have committed on the same edge they acknowledged, because that is the policy Chapter 7.1 declared. That coincidence is a design choice, not a law — and a slave that separates the two has to keep them coherent by other means.
When should a write slave acknowledge, and how does its termination relate to the state update?
Chapter 7.3 — ACK Timing answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- 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
Data Transfer
Selection routes a request; it does not move a value. Write data fans out, read data fans in through a multiplexer that every target widens, byte lanes decide which parts of a word participate, and a register narrower than the bus has to be placed on a lane rather than merely connected.
- 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
Transaction Lifecycle
One Wishbone transaction from a master's decision to act through the slave's termination and back to the caller: what is fixed by the protocol, what every implementation may vary, and what a real simulation of the assembled system shows at each step.
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.
