Skip to content
VLSI Mentor

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

A block diagram of a Wishbone write path arranged in two rows. The top row is the forward path running left to right: the client issues a write request carrying an address, a data value and byte enables; the master captures all of that and presents it on the bus with write enable asserted; the interconnect decodes the address to select one slave and produce a local offset; the slave applies the byte lane mask to decide which bytes of the addressed register will change; and the register file commits the new value. The bottom row is the return path running right to left and carries only an acknowledge, which the interconnect merges and returns to the master, which then reports one completion to the client. The contrast with a read is that no data travels backwards.1. CLIENTadr + data + byte enables2. MASTERcaptures, then presents3. DECODEselect + local offset4. MASKwhich bytes change5. COMMITregister takes the value6. ACK_Otermination only7. MERGEone return path8. DONEone completion pulse12
Figure 1 — the write path. Both the question and its answer travel forward; only the termination comes back.
StageContributesOwnerChapter
1 Client requestaddress, data, byte enablesclient5.5
2 Master captures + presentsADR_O, DAT_O, WE_O=1, SEL_O, qualifiersmaster7.2
3 Decodeglobal address → select + local offsetinterconnect6.2
4 Byte maskwhich lanes of the register changeslave7.2
5 Committhe register takes its new valueslave7.2, 7.3
6 ACK_Othe transfer is overslave7.3
7 Mergeone termination reaches the masterinterconnect6.3
8 Completionone pulse to the clientmaster5.6
9 ReleaseCYC_O/STB_O negatedmaster5.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.

ByteWordRegisterAccessPurpose
0x000STATUSread-onlyflags
0x041COUNTread-onlyfree-running counter
0x082CONTROLread/writethe workhorse for partial writes
0x0C3INPUT_DATAread-onlyexternal input (6.1)
0x104IDread-onlyconstant
0x145IRQread-to-clearfrom 6.5; not written here
0x186OUTPUT_DATAread/writea value driven toward the datapath
0x208EVENTSwrite-one-to-clear7.4
0x249COMMANDwrite-only, pulses7.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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// 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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// 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
endmodule

Reading 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 cycles
Six clock cycles showing one complete Wishbone write. Cycle one is idle with all qualifiers negated. In cycle two the master asserts both the cycle and strobe signals, drives word address two on the address output, asserts write enable, places the value zero zero zero zero zero zero zero f on the data output and asserts all four byte select lines. The slave answers combinationally so its acknowledge is asserted in the same cycle. At the edge ending cycle two the slave commits the value and the master samples the acknowledge. In cycle three both qualifiers are negated, the acknowledge has fallen in response, the control register now reads zero zero zero zero zero zero zero f, and the master's completion pulse is asserted.presented; slave answerspresented; slave answerscommitted + reportedcommitted + reportedCLK_ICYC_OSTB_OWE_OADR_O----0x002----------------DAT_O----0000000F----------------SEL_O000011110000000000000000ACK_ICONTROL00000000000000000000000F0000000F0000000F0000000Fdone_ot0t1t2t3t4t5
Figure 2 — one complete write of CONTROL. The payload is presented for one cycle; the register changes at the edge that ends it.

Narrate 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:

ProbeIf wrong, the fault is
ADR_O vs the intended word addressthe master, or a byte/word conversion (6.2)
WE_O asserted during the transferthe master — a read was issued, not a write
decoded select / STB_I at the intended slavethe interconnect
the slave's writable term for that offseta read-only register — check ERR_O, not ACK_O
SEL_O at the slaveall-zero selects nothing; 7.2
the slave's commit termthe 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// 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");
endmodule

P3 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === 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

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.