Wishbone · Module 7
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.
Module 6 followed a read: the master asked, the slave answered, and a wrong answer corrupted only the master's copy.
A write reverses that, and the reversal is the whole module.
What is the complete path of one Wishbone write, from client intent to changed peripheral state?
1. The Specification's Own Write Timing
Module 6 leaned on RULE 3.60 (the master qualifies ADR_O, DAT_O(), SEL_O() and WE_O with STB_O) and RULE 3.65 (the slave qualifies its read data with its termination). A write needs one more, and it is more specific than either.
RULE 3.75 — "All MASTER and SLAVE interfaces that support SINGLE READ or SINGLE WRITE cycles MUST conform to the timing requirements given in sections 3.2.1 and 3.2.2."
That makes §3.2.2's timing normative by reference, and §3.2.2 says three things Module 7 rests on.
One — the master presents everything at once. At the first clock edge of the cycle the master drives the address, the write data, WE_O asserted and the byte selects, and asserts CYC_O and STB_O. There is no separate data phase; Chapter 6.2 established that Classic has one handshake, and a write uses that same single handshake to carry more.
Two — the payload stays valid past the end. The master's address, data, WE_O and SEL_O remain valid until the rising clock edge following the negation of STB_O. That is stronger than "stable while the strobe is asserted" — the payload must still be coherent at the edge that ends the transfer, and Chapter 7.4 is built on it.
Three — the slave latches in response to its own acknowledgment. §3.2.2's timing shows the slave capturing the data at the edge that ends the cycle, coincident with the acknowledge it returned.
2. The Nine Stages
| Stage | Contributes | Owner | Chapter |
|---|---|---|---|
| 1 Client request | address, data, byte enables | client | 5.5 |
| 2 Master captures + presents | ADR_O, DAT_O, WE_O=1, SEL_O, qualifiers | master | 7.2 |
| 3 Decode | global address → select + local offset | interconnect | 6.2 |
| 4 Byte mask | which lanes of the register change | slave | 7.2 |
| 5 Commit | the register takes its new value | slave | 7.2, 7.3 |
6 ACK_O | the transfer is over | slave | 7.3 |
| 7 Merge | one termination reaches the master | interconnect | 6.3 |
| 8 Completion | one pulse to the client | master | 5.6 |
| 9 Release | CYC_O/STB_O negated | master | 5.2 |
Compare with Chapter 6.1's nine stages. The read had a read multiplexer and a capture register; the write has a byte mask and a commit. The read's return path carried data; this one carries a single bit.
Stage 5 has no counterpart in a read at all, and it is the only stage in either module that changes something permanently.
3. The Running Peripheral, Extended
Module 6's peripheral was read-only apart from CONTROL. Module 7 needs writable state, so the same device gains registers rather than being replaced.
| Byte | Word | Register | Access | Purpose |
|---|---|---|---|---|
0x00 | 0 | STATUS | read-only | flags |
0x04 | 1 | COUNT | read-only | free-running counter |
0x08 | 2 | CONTROL | read/write | the workhorse for partial writes |
0x0C | 3 | INPUT_DATA | read-only | external input (6.1) |
0x10 | 4 | ID | read-only | constant |
0x14 | 5 | IRQ | read-to-clear | from 6.5; not written here |
0x18 | 6 | OUTPUT_DATA | read/write | a value driven toward the datapath |
0x20 | 8 | EVENTS | write-one-to-clear | 7.4 |
0x24 | 9 | COMMAND | write-only, pulses | 7.4 |
Word 7 stays unmapped, exactly as Chapter 6.1 §8 and Chapter 6.6's Trace B relied on.
OFF_AW widens from 3 to 4 to make room. That is an extension of the same device, not a redefinition — every offset Module 6 named keeps its meaning and its access rules.
The byte/word convention is unchanged. Chapter 4.3 established that a 32-bit port carries ADR_O(n..2), so the bus sees word addresses and a datasheet's byte offset is four times the word address. Chapter 6.2 §2 gives the three representations.
4. RTL — The Write Master and the Register Bank
// ─────────────────────────────────────────────────────────────────────────
// wb_write_master — the Module 7 running master.
//
// PURPOSE. Turn a local client write request into a Wishbone write and
// report completion. It is Chapter 6.1's wb_read_master with the payload
// added: where the read master latched an address and byte selects, this
// one latches a data word as well, and drives WE_O high.
//
// THAT EXTRA REGISTER IS THE CHAPTER. On a read, a master that loses its
// payload gets a wrong answer. On a write, it CORRUPTS THE DEVICE — and
// Chapter 7.4 measures exactly that.
//
// CLIENT CONTRACT (LOCAL POLICY, not Wishbone — RULE 2.15 requires a
// datasheet to state this kind of thing):
// * req_i is accepted only in a cycle where acc_o is also asserted;
// * one transaction outstanding at a time;
// * done_o pulses once per accepted request, with status_o valid.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00, 3.20).
// ─────────────────────────────────────────────────────────────────────────
module wb_write_master #(
parameter int unsigned AW = 30, // WORD address width
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
// ── local client side (not Wishbone) ────────────────────────────────
input logic req_i,
input logic [AW-1:0] req_adr_i, // WORD address
input logic [DW-1:0] req_dat_i, // value to write
input logic [DW/8-1:0] req_sel_i, // which byte lanes
output logic acc_o,
output logic busy_o,
output logic done_o,
output logic [1:0] status_o, // 0=ok 1=error 2=retry
// ── Wishbone MASTER interface ───────────────────────────────────────
output logic cyc_o,
output logic stb_o,
output logic we_o,
output logic [AW-1:0] adr_o,
output logic [DW-1:0] dat_o,
output logic [DW/8-1:0] sel_o,
input logic ack_i,
input logic err_i,
input logic rty_i
);
localparam logic [1:0] ST_OK = 2'd0, ST_ERR = 2'd1, ST_RTY = 2'd2;
// ── STATE ─────────────────────────────────────────────────────────────
// The PRIVATE COPY of the whole transaction. RULE 3.60 qualifies all four
// of these with STB_O, and §3.2.2 requires them to stay valid until the
// edge following STB_O's negation — neither is satisfiable across an
// unbounded wait unless they come from registers.
logic active_q;
logic [AW-1:0] adr_q;
logic [DW-1:0] dat_q;
logic [DW/8-1:0] sel_q;
assign acc_o = req_i & ~active_q;
assign busy_o = active_q;
// ── BUS OUTPUTS, ALL FROM THE PRIVATE COPY ────────────────────────────
// PERMISSION 3.40 lets both qualifiers share one signal: this master
// never negates STB_O mid-transfer.
assign cyc_o = active_q;
assign stb_o = active_q;
assign adr_o = adr_q;
assign dat_o = dat_q;
assign sel_o = sel_q;
// WE_O is a CONSTANT here, not a register. This master only writes, so
// the direction cannot drift mid-transfer — which satisfies RULE 3.60's
// stability obligation structurally rather than by promise. Chapter 6.1's
// read master tied it low for the mirror-image reason.
assign we_o = 1'b1;
logic terminated;
assign terminated = ack_i | err_i | rty_i;
always_ff @(posedge clk_i) begin
if (rst_i) begin
active_q <= 1'b0; // RULE 3.20
adr_q <= '0;
dat_q <= '0;
sel_q <= '0;
done_o <= 1'b0;
status_o <= ST_OK;
end else begin
done_o <= 1'b0; // done_o is a PULSE
if (acc_o) begin
// ── WRITE START ─────────────────────────────────────────────────
// Address, DATA and byte enables captured together, once. From the
// next edge the client may change all three freely.
active_q <= 1'b1;
adr_q <= req_adr_i;
dat_q <= req_dat_i;
sel_q <= req_sel_i;
end else if (active_q && terminated) begin
// ── TERMINATION ─────────────────────────────────────────────────
// A write has nothing to capture — that is the whole asymmetry
// with Chapter 6.1, whose master captured dat_i here. All this
// master does is record which termination arrived and release.
status_o <= ack_i ? ST_OK : (err_i ? ST_ERR : ST_RTY);
active_q <= 1'b0;
done_o <= 1'b1;
end
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_write_regs — the running peripheral, write path included.
//
// PURPOSE. Turn a qualified write into a committed register update, and
// terminate. This is stages 4, 5 and 6 of Figure 1.
//
// ── DECLARED COMMIT POLICY (LOCAL, not Wishbone) ──────────────────────
// This slave commits the addressed register on the SAME rising clock
// edge for which it is returning ACK_O for a qualified write.
//
// Both effects are therefore driven by one combinational term, `commit`.
// They cannot disagree, because there is nothing to disagree WITH — no
// stored context, no second schedule. Chapter 7.3 builds a slave that
// separates them and shows what that costs.
//
// TERMINATION TIMING. Combinational, which PERMISSION 3.30 explicitly
// allows ("i.e. there is a combinatorial logic path between [STB_I] and
// [ACK_O]") and OBSERVATION 3.40 credits with one transfer per clock.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_write_regs #(
parameter int unsigned OFF_AW = 4, // 16 word offsets
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
// Wishbone SLAVE interface
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, // the value being written
input logic [DW/8-1:0] sel_i,
output logic [DW-1:0] dat_o,
output logic ack_o,
output logic err_o,
// device side
input logic [DW-1:0] input_data_i,
input logic [7:0] flags_i,
output logic [DW-1:0] control_o, // observable committed state
output logic [DW-1:0] outdata_o
);
localparam int unsigned NL = DW/8;
localparam logic [OFF_AW-1:0] O_STATUS = 4'd0; // byte 0x00 RO
localparam logic [OFF_AW-1:0] O_COUNT = 4'd1; // byte 0x04 RO
localparam logic [OFF_AW-1:0] O_CTRL = 4'd2; // byte 0x08 RW
localparam logic [OFF_AW-1:0] O_INPUT = 4'd3; // byte 0x0C RO
localparam logic [OFF_AW-1:0] O_ID = 4'd4; // byte 0x10 RO
localparam logic [OFF_AW-1:0] O_OUT = 4'd6; // byte 0x18 RW
localparam logic [DW-1:0] ID_VALUE = 32'h5742_0701; // "WB", module 7 rev 1
logic [DW-1:0] ctrl_q, outdata_q, count_q;
assign control_o = ctrl_q;
assign outdata_o = outdata_q;
// ── THE QUALIFIED TRANSFER ────────────────────────────────────────────
// RULE 3.30 forbids responding to any slave signal while CYC_I is
// negated; RULE 3.35 requires the termination to be generated from the
// AND of CYC_I and STB_I.
logic xfer;
assign xfer = cyc_i & stb_i;
// ── LEGALITY ──────────────────────────────────────────────────────────
logic known_off, writable;
always_comb begin
unique case (adr_i)
O_STATUS, O_COUNT, O_CTRL, O_INPUT, O_ID, O_OUT: known_off = 1'b1;
default: known_off = 1'b0;
endcase
end
always_comb begin
unique case (adr_i)
O_CTRL, O_OUT: writable = 1'b1;
default: writable = 1'b0;
endcase
end
// A write to a read-only offset is REFUSED rather than ignored. Silently
// dropping it would tell the client the write succeeded — Chapter 4.11's
// argument that an error is the honest answer and silence never is.
logic illegal;
assign illegal = ~known_off | (we_i & ~writable);
// RULE 3.35 both; RULE 3.45 mutually exclusive by construction.
assign err_o = xfer & illegal;
assign ack_o = xfer & ~illegal;
// ── THE COMMIT EVENT ──────────────────────────────────────────────────
// ONE named term, used for every state-changing path below. It contains
// ack_o, so it is true for exactly one cycle per transfer — which is the
// declared policy above, made structural.
//
// This slave never waits, so `commit` and `xfer & we_i` happen to
// coincide. Writing ack_o in anyway is deliberate: the moment a wait
// state appears the two diverge, and Chapter 7.4 measures a slave that
// learned this the hard way.
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;
count_q <= '0;
end else begin
count_q <= count_q + 32'd1;
if (commit) begin
// ── BYTE-LANE MASKED UPDATE ─────────────────────────────────────
// Each lane is gated independently, so an unselected lane is not
// written at all and keeps whatever it held. Chapter 7.2 takes
// this apart; Chapter 4.7 established why SEL exists.
unique case (adr_i)
O_CTRL: begin
for (int unsigned n = 0; n < NL; n++)
if (sel_i[n]) ctrl_q[n*8 +: 8] <= dat_i[n*8 +: 8];
end
O_OUT: begin
for (int unsigned n = 0; n < NL; n++)
if (sel_i[n]) outdata_q[n*8 +: 8] <= dat_i[n*8 +: 8];
end
default: ; // unreachable: commit needs writable
endcase
end
end
end
// ── READ PATH ─────────────────────────────────────────────────────────
// Unchanged in shape from Chapter 6.3: RULE 3.65 qualifies a slave's
// DAT_O with its termination, and driving '0 otherwise keeps a merged
// return path clean. Present here so the same device still reads.
always_comb begin
dat_o = '0;
if (xfer && !we_i && ack_o) begin
unique case (adr_i)
O_STATUS: dat_o = {24'd0, flags_i};
O_COUNT: dat_o = count_q;
O_CTRL: dat_o = ctrl_q;
O_INPUT: dat_o = input_data_i;
O_ID: dat_o = ID_VALUE;
O_OUT: dat_o = outdata_q;
default: dat_o = '0;
endcase
end
end
endmoduleReading the pair
Purpose. The master converts a client intention into a qualified write and carries the payload; the register bank masks it into the addressed register and terminates.
Interface. The master's client side is Module 6's contract plus req_dat_i, and minus rdat_o — a write returns no data. The slave gains control_o and outdata_o so a testbench can observe committed state directly.
State. Master: the outstanding flag plus three payload registers. Slave: two writable registers and the free-running counter.
Combinational logic. Master: acc_o and every bus output from state. Slave: xfer, offset legality, writability, two exclusive terminations, and commit.
Sequential logic. Master: capture at acceptance, release at termination. Slave: the counter, and one masked update per commit.
Write start. CYC_O and STB_O rise together at the edge following req_i & ~active_q, with all three payload registers already loaded at that same edge. RULE 3.25's "no later than" is met by rising together.
Address. From adr_q, written once.
Write data. From dat_q, written once. This is the register Module 6's master did not have, and Chapter 7.4 shows what its absence costs.
Byte enables. From sel_q, written once.
Commit. xfer & we_i & ack_o — the declared policy, made structural.
ACK. Combinational from the qualified transfer and legality, per PERMISSION 3.30.
Waiting. This pair never waits; ack_o is asserted in the presented cycle. Chapter 7.4 adds latency.
Reset. Synchronous, active high. active_q <= 0 negates both qualifiers, satisfying RULE 3.20 through the state encoding.
Failure modes. Section 6.
Simplifications. No interconnect yet — adr_i is assumed already decoded to a local offset, which Chapter 6.2 built. One outstanding transaction. EVENTS and COMMAND from Section 3's map arrive in Chapter 7.4, where delayed completion makes their semantics matter.
5. The Canonical Write, Edge by Edge
A single Wishbone write
6 cyclesNarrate it — the same discipline Chapter 6.1 §5 trained, now on the write side.
Before edge 2. The master drives CYC_O, STB_O, ADR_O = 2, WE_O = 1, DAT_O = 0x0000000F and SEL_O = 1111. The slave, answering combinationally, already has ACK_O asserted and commit true. Nothing has been observed or committed yet — these are levels on wires.
At edge 2 — both the sampling edge and the commit edge. The slave samples a qualified write and its commit term is true, so ctrl_q loads. In the same instant the master samples ack_i asserted. This is the edge the declared policy names.
After edge 2. active_q has cleared, so both qualifiers negate. CONTROL now reads 0x0000000F. done_o pulses. The slave, seeing STB_I negate, drops ACK_O — RULE 3.50, which OBSERVATION 3.10 calls automatic.
Note what is absent. There is no capture register in the master and no data on the return path. Chapter 6.1's Figure 2 had DAT_I carrying a value for exactly one cycle; a write has nothing coming back but one bit.
And note that §3.2.2's payload-validity requirement is satisfied trivially here — the payload is still valid at edge 2, the edge following the cycle in which STB_O was asserted. Chapter 7.4 is where that requirement stops being trivial.
6. Failure Modes and Discriminating Evidence
Symptom: the write reports success and the register did not change.
Candidate causes. Six, and they are separable in a fixed order.
Discriminating evidence. Walk the path from Figure 1:
| Probe | If wrong, the fault is |
|---|---|
ADR_O vs the intended word address | the master, or a byte/word conversion (6.2) |
WE_O asserted during the transfer | the master — a read was issued, not a write |
decoded select / STB_I at the intended slave | the interconnect |
the slave's writable term for that offset | a read-only register — check ERR_O, not ACK_O |
SEL_O at the slave | all-zero selects nothing; 7.2 |
the slave's commit term | the commit condition itself |
The ERR_O check is the cheap one to do early. A write to STATUS, COUNT, INPUT_DATA or ID terminates with ERR_O and changes nothing — which is correct behaviour, not a bug, and a client that ignores status_o will read it as a silent failure.
Symptom: the write completes and the wrong register changed.
Candidate causes. Wrong address on the bus, wrong decode, or a wrong case arm in the commit.
Discriminating evidence. Compare ADR_O, then the decoded select, then the local offset, then which register's enable asserted. The first mismatch names the stage.
Symptom: the right register changed to the wrong value.
Candidate causes. The master's captured data differs from what the client asked for, or the byte mask selected the wrong lanes.
Discriminating evidence. Compare req_dat_i at acceptance against DAT_O on the bus, then against what the register took. Chapter 7.2 §7 separates these, and the distinguishing question is whether all bytes are wrong or only some.
Symptom: bytes the write never mentioned changed too.
Candidate causes. The commit ignores SEL_I and assigns the whole word.
Discriminating evidence. Write one byte into a register with a known non-zero value and read it back. Neighbouring bytes becoming the write data's bytes — usually zero — is conclusive, and it is Chapter 4.7's failure seen from the write side.
Symptom: a write happens more than once.
Candidate causes. The commit is gated on the request being presented rather than accepted.
Discriminating evidence. Invisible for an idempotent register — writing 0x0F four times leaves 0x0F. It becomes visible on a counter, a FIFO push or a command pulse. Chapter 7.4 builds one and measures it.
7. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_write_flow_checker — the write-path invariants introduced here.
//
// P1 and P2 are SPECIFICATION. P3 and P4 are LOCAL DESIGN POLICY: the
// specification says nothing about when a slave's flops load, so a commit
// property can only be checked against a slave's DECLARED policy.
// ─────────────────────────────────────────────────────────────────────────
module wb_write_flow_checker #(
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic cyc_o,
input logic stb_o,
input logic we_o,
input logic ack_i,
input logic err_i,
input logic rty_i,
input logic busy_o,
input logic done_o,
// white-box, slave side
input logic commit,
input logic [DW-1:0] ctrl_q
);
default disable iff (rst_i);
// P1 — SPECIFICATION (RULE 3.25). A strobe never exists outside a cycle.
property p_stb_implies_cyc;
@(posedge clk_i) stb_o |-> cyc_o;
endproperty
a_stb_implies_cyc : assert property (p_stb_implies_cyc)
else $error("RULE 3.25: STB_O asserted without CYC_O");
// P2 — SPECIFICATION (RULE 3.60, as stability across a presentation).
// WE_O is qualified by STB_O, so a presented write stays a write.
// Holds trivially here because we_o is a constant — which is the
// point of making it one.
property p_direction_stable;
@(posedge clk_i) (cyc_o && stb_o && !(ack_i||err_i||rty_i)) |=> $stable(we_o);
endproperty
a_direction_stable : assert property (p_direction_stable)
else $error("RULE 3.60: WE_O changed while the write was outstanding");
// P3 — LOCAL POLICY (this slave's declared commit rule). Application
// state changes only at a commit event. Catches a register enable
// that is gated on something looser than `commit`.
property p_state_only_on_commit;
@(posedge clk_i) $changed(ctrl_q) |-> $past(commit);
endproperty
a_state_only_on_commit : assert property (p_state_only_on_commit)
else $error("LOCAL: register changed outside a commit event");
// P4 — LOCAL POLICY. One completion per bus termination, never without
// an outstanding transaction.
property p_done_follows_termination;
@(posedge clk_i) done_o
|-> $past(busy_o && cyc_o && stb_o && (ack_i || err_i || rty_i));
endproperty
a_done_follows_termination : assert property (p_done_follows_termination)
else $error("completion pulse without a matching bus termination");
endmoduleP3 is the property this module will keep extending. Note it is checked against commit — the slave's own named term — rather than against ACK_I. A property written against ACK_I would silently encode the "ACK writes the register" mental model this chapter's callout rejects, and would be wrong for the registered slave in Chapter 7.3.
Tooling limitation. Icarus Verilog has no SVA support and cannot execute any of these. They are reviewed by inspection; the synthesizable RTL is elaborated and simulated in Section 8.
8. Simulation — The Basic Write, Measured
wb_write_master was pointed at wb_write_regs and asked to write each register once, including two read-only offsets and one unmapped one.
=== SIMULATION A - basic write ===
word register wrote CONTROL OUTPUT status WE SEL done
2 CONTROL 0x0000000f 0x0000000f 0x00000000 OK 1 1111 1
6 OUTPUT_DATA 0xdeadbeef 0x0000000f 0xdeadbeef OK 1 1111 1
0 STATUS 0x11111111 0x0000000f 0xdeadbeef ERR 1 1111 1
4 ID 0x22222222 0x0000000f 0xdeadbeef ERR 1 1111 1
7 (unmapped) 0x33333333 0x0000000f 0xdeadbeef ERR 1 1111 1
completions: 5 requests -> 5 done pulses
commit events: 2 (one per writable target)Both writable registers took exactly the value requested, and neither disturbed the other.
The three refused writes returned ERR and changed nothing. STATUS and ID are read-only; word 7 is unmapped. The register columns are identical across all three rows, which is the observable form of "an errored transfer changes nothing" — Chapter 4.11 §2's obligation, now visible on the write side where it actually matters.
commit events: 2 against 5 requests is the number to watch. Three transfers were presented, terminated and completed without any commit — exactly as intended.
WE_O was asserted on every transfer, confirming P2 held throughout.
9. Common Mistakes
"WE_O high means a write is happening."
Wrong mental model: the direction bit is the trigger.
Concrete bug: a slave whose commit is gated on we_i without the qualifiers, writing on bus residue between transfers.
Observable evidence: registers changing with no software access, often just after reset release.
Correct model: WE_O says which direction; CYC_O & STB_O say whether a transfer exists. Chapter 4.6 traced the mirror-image bug, where a missing WE_I made every read write.
"ACK causes the register write."
Wrong mental model: the termination is a write-enable.
Concrete bug: reasoning that breaks the moment a slave registers its acknowledge, because the two events are then a cycle apart.
Observable evidence: a mental model that predicts the wrong cycle on any slave with latency — and predictions that are wrong about when become wrong about what once a payload starts moving.
Correct model: ACK_O is a protocol termination; the commit is an internal decision the slave must declare. Chapter 7.3 measures a slave where they differ.
"A write that returns ACK succeeded."
Wrong mental model: protocol completion equals architectural success.
Concrete bug: none in the bus — but a write that reached the wrong register, or wrote the wrong bytes, terminates perfectly normally.
Observable evidence: a clean bus trace and a device in the wrong state. Chapter 7.5 is built around exactly this gap.
Correct model: ACK_O proves the transfer ended. It proves nothing about decode, payload association or masking.
10. Interview Reasoning
Before the acceptance edge. A client presents a write request — an address, a data word and byte enables. The master, idle, asserts its accept signal. That conjunction is the acceptance boundary, and it is the last moment the client's inputs matter.
At the acceptance edge. The master latches all three payload fields into private registers and sets its outstanding flag. That third register is what distinguishes this from a read master: RULE 3.60 qualifies DAT_O as well as ADR_O and SEL_O, and §3.2.2 requires all of them to stay valid until the edge after the strobe negates.
Before the next edge. CYC_O and STB_O are asserted, WE_O is high, and the address, data and byte enables are on the bus. The interconnect decodes, selects one slave and presents a local offset. The selected slave decides the transfer is legal and — if it answers combinationally — already has ACK_O asserted and its commit term true.
At the termination edge. The slave samples a qualified write; its commit term is true, so the addressed register's selected byte lanes load. In the same instant the master samples ack_i asserted. For this slave those are the same edge, because that is its declared policy — not because the specification says so.
After that edge. The master clears its outstanding flag, both qualifiers negate, and one completion pulse goes to the client. The slave, seeing STB_I negate, drops ACK_O.
What I would emphasise as the actual content. Two things. The master carries the payload, so losing it corrupts the device rather than the master. And the commit event is a slave design decision that must be stated — "ACK writes the register" is true for this slave and false for one that registers its response.
If the slave needs longer, nothing changes except that the termination edge arrives later — and the payload must stay coherent for every cycle in between, which is where the interesting failures live.
11. Understanding Check
12. What's Next
The path is visible end to end: a master that carries a payload, a decode, a byte mask, a commit, and a single bit coming back.
Stage 4 was drawn as one box. It is the stage where the write either lands in the right bits or destroys the ones next to it — and it is the only place in either module where data the transfer never mentioned can be lost.
How does write data travel from the requester into the correct bits of the correct register?
Chapter 7.2 — Data Transfer answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
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.
- 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
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
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.
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.
