Skip to content
VLSI Mentor

Wishbone · Module 24

Write Logic

Two byte-lane masks, not one, and a conflict no bus transaction can provoke: hardware and the bus writing the same register on the same clock. Hardware wins by statement order, and the defensive-looking guard that reverses it destroys events silently.

Chapter 24.2 had one hard problem: a read that changes state. The write path has a harder one, and it is harder for a reason worth stating up front.

No sequence of bus transactions can provoke it. Every read-path defect in the last chapter is reachable by issuing the right access. The central defect in this one needs hardware activity to coincide with a bus access on a single clock — which in silicon means "under load" and in simulation means the testbench has to arrange it deliberately.

1. Two Masks, Not One

RULE 3.60 puts [SEL_O()] in the set of signals [STB_O] qualifies, alongside [ADR_O], [DAT_O()] and [WE_O]. A master asserting SEL_O = 4'b0010 is asking for one byte to change.

Chapter 23.3 honoured that against a flat file of equal-width registers. A real map makes it two questions, not one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//   sel_i     what the MASTER asked for
//   lanes_i   what the REGISTER actually occupies  (from wb_slave_map)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  logic [SW-1:0] asked, mask;
  assign asked = IGNORE_SEL ? {SW{1'b1}} : sel_i;
  assign mask  = asked & lanes_i;

The AND of the two is what gets written. That is what makes a 32-bit write to an 8-bit register safe: the register's own lane mask clips it, and the three bytes the master supplied for lanes that do not exist are discarded rather than landing on a neighbour.

Measured end to end:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      write 0x11223344 to offset 0, all lanes
        read back 0x11223344            PASS
      write 0x0000ee00 to offset 0, lane 1 only
        read back 0x1122ee44            PASS
        -> one lane moved, three did not.

      offset 4 is 16 bits. Write 0xffffa5a5, lanes 0011:
        read back 0x0000a5a5            PASS
        -> the register's OWN lane mask clipped it. The
           0xffff the master supplied for lanes it does
           not own was discarded, not written to a
           neighbour.

2. A Slave That Ignores SEL Breaks No Rule

IGNORE_SEL writes all four lanes regardless of what was asked. It is a real and common defect, and the interesting thing about it is what rule it breaks:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//   IGNORE_SEL  writes all four lanes regardless of [SEL_I()]. RULE 3.60
//               is a MASTER obligation, so a slave that ignores SEL is
//               not literally breaking that rule - IT IS BREAKING THE
//               CONTRACT THE RULE EXISTS TO CREATE. Chapter 24.3 is
//               careful about that distinction.

RULE 3.60 says what a MASTER must qualify. There is no rule in B3 that says a slave must honour [SEL_I()] — the specification simply assumes a slave that receives a byte-select will use it. So a checker that verifies rule numbers finds nothing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      rig                  C1   C2   C3   C4   C5   C6   FUNC
      correct               0    0    0    8    0    0    ok
      IGNORE_SEL            0    0    0    8    0    0   WRONG BYTES

        offset 0 read back      correct 0x1122ee44
                                IGNORE_SEL 0x0000ee00

0x1122EE44 against 0x0000EE00. The correct slave changed one byte; the defective one destroyed three. Six protocol checkers, all silent.

The distinction matters because it tells you what kind of test would have caught it. Not a conformance suite — a functional test that writes one lane and reads the other three back.

3. Where A Write Is Allowed To Land

Three conditions refuse a write, and all three are this slave's policy rather than B3's:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//   conditions generating [ERR_O]  (RULE 2.15 item 4 obliges this line):
//       a write to a RESERVED offset
//       a write to a READ-ONLY offset
//       a MISALIGNED access when STRICT_ALIGN is set

RULE 2.15 item 4 is what makes that comment mandatory rather than decorative:

"If a SLAVE supports the optional [ERR_O] signal, then the WISHBONE DATASHEET MUST describe the conditions under which the signal is generated."

All three fire:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      the three ERR_O conditions, which RULE 2.15 item 4
      obliges the datasheet to name:
        write to RESERVED offset 5    ERR count 1
        write to READ-ONLY offset 1   ERR count 1
        MISALIGNED 32-bit to offset 4 ERR count 1

PERMISSION 3.20 is explicit that the response is not B3's business — "This specification does not dictate what the MASTER does in response to [ERR_I]" — and says nothing at all about when a slave should raise it. Unconstrained behaviour, compulsory documentation. Chapter 24.5 argues about whether [ERR_O] is the right vehicle for each of the three.

The write path as a decision. From IDLE the slave moves to DECODE when CYC_I and STB_I are asserted with WE_I high. DECODE consults the map: if the offset is reserved, or the policy is read-only, or the access is misaligned, the slave moves to REFUSE and asserts ERR_O without changing any state. Otherwise it moves to COMMIT, where on a single clock it applies the AND of the master's byte selects and the register's own lane mask, writes only those lanes, and asserts ACK_O. A write-one-to-clear register clears the bits written as one instead of storing; a write-only register stores nothing and fires a strobe. Both REFUSE and COMMIT return to IDLE when STB_I is negated.IDLEDECODEREFUSECOMMITCYC_I and STB_I and WE_ICYC_I and STB_I and WE_ICYC_I andSTB_I and…reserved / read-only / misalignedreserved / read-only / misalignedreserved /read-only /…permitted — mask = SEL and lanespermitted — mask= SEL and lanesERR_O, no state changedERR_O, no state changedERR_O, nostate…ACK_O, lanes writtenACK_O, lanes written

4. Two Writers, One Register, One Clock

Here is the defect that cannot be reached from the bus.

A peripheral's registers usually have two writers. The bus writes a control word; hardware writes a status word. Point them at the same offset on the same clock and only one value survives.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Bus and hardware want the same register on the same clock.
  logic collide;
  assign collide = bus_write && hw_set_i && (hw_off_i == off_i);

The correct answer is that hardware wins, and the reason is not arbitrary: the bus wrote a value it chose, and hardware wrote a value that happened. A lost bus write is a command the software can re-issue. A lost hardware event is an interrupt that never fires, a byte that never arrived, an error flag that nobody will ever see again.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // ── THE HARDWARE WRITE ──────────────────────────────────────────
      // Written AFTER the bus write in the same always_ff, so on a
      // collision the hardware value is the surviving non-blocking
      // assignment. HARDWARE WINS, and it wins because of statement
      // order, which is a fragile thing to depend on silently - hence
      // this comment and the explicit counter below.

"It wins because of statement order" is an uncomfortable sentence to write about production RTL, and it is the truth. Two non-blocking assignments to the same variable in one always_ff resolve in favour of the later one, deterministically, with no warning from any simulator or synthesiser. The behaviour is correct and the mechanism is invisible — which is why hw_lost_o exists purely to be zero.

5. The Defect Written By A Careful Engineer

BUS_WINS is what somebody writes when they add the hardware port second and guard it so it does not "fight" the bus:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // BUS_WINS reverses it by suppressing the hardware write on a
      // collision, which is what a designer writes when they add the
      // hardware port second and guard it "so it does not fight the bus".
      if (hw_set_i) begin
        if (BUS_WINS && collide) begin
          nlost_q <= nlost_q + 16'd1;      // the event is destroyed

That guard is a reasonable-looking thing to write. It is defensive, it is explicit, and it is exactly backwards.

The measurement aims a hardware write at the commit clock of a bus write:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      rig          collisions  hw writes  hw lost  offset 0
      correct             1          1        0  0x44444444
      BUS_WINS            1          0        1

      -> HARDWARE WON on the correct rig: offset 0 holds
         0x44444444, the hardware value, not the
         0xbbbbbbbb the bus wrote on the same clock.

      -> BUS_WINS LOST 1 hardware event(s). The bus
         transaction succeeded. ACK_O was asserted. The
         master has no way to learn that anything was
         destroyed, and no Wishbone rule was broken -
         B3 does not know this slave has a second
         writer.
A bus write and a hardware write colliding on one clock. CYC_I, STB_I and WE_I are asserted and the master supplies 0xBBBBBBBB. On the same clock the hardware side asserts its own write of 0x44444444 to the same offset. The slave asserts ACK_O, so from the bus the transaction succeeded. On the correct slave the register afterwards holds the hardware value, because the hardware assignment is later in the same always_ff block. On the BUS_WINS slave the hardware write is suppressed and the register holds the bus value, with the hardware event destroyed and nothing reported.collision: ACK_O asserted either waycollision: ACK_O assertedeither wayhardware wins / hardware losthardware wins / hardwarelostCLK_ISTB_IWE_IDAT_IBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBhw_setACK_Oreg ok00044444444444444444444reg bad000BBBBBBBBBBBBBBBBBBBBt0t1t2t3t4t5t6t7

6. Why This One Ships

Every other defect in Module 24 is reachable by a test that issues the right bus access. This one is not, and that changes how it has to be hunted:

defecthow a test reaches it
IGNORE_SELwrite one lane, read the others back
SIDE_ON_STBread a FIFO with wait states
DAT_ALWAYSwatch the wire between transfers
NO_NEGATEdrop [STB] and look at [ACK_O]
BUS_WINSarrange a hardware event on a specific clock

The first four are stimulus. The fifth is scheduling. A random-access test with a background hardware process will hit it eventually and report it as an intermittent, irreproducible lost interrupt — which is how these are usually found, months later, on real traffic.

collisions_o exists so the test can assert the collision happened. A negative-control rig that quietly never collided would report zero lost events and look like a pass, which is precisely the failure mode the gate in Chapter 24.4 had to be rebuilt to avoid.

The testbench asserts the collision happened before it trusts any result from the rig:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    if (g_col == 0) begin
      $display("      FAIL: the collision never occurred - the testbench");
      $display("            did not aim at the commit clock"); errs++;
    end else if (v !== 32'h4444_4444) begin
      $display("      FAIL: hardware did not win on the correct rig");

That check never fired, which is the only reason the hw lost column below means anything. A rig whose collision silently never happened reports zero lost events and looks exactly like a pass.

7. Write-One-To-Clear, Carried Forward Unchanged

ACC_W1C behaves as Chapter 23.3 defined it, and the lane policy is repeated here because it is a policy and not a derivation:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        if (policy_i == ACC_W1C) begin
          // write a 1 to clear, a 0 leaves alone. Lane 0 only - a stated
          // LOCAL POLICY carried over from Chapter 23.3 unchanged.
          if (mask[0])
            reg_q[off_i][7:0] <= reg_q[off_i][7:0] & ~dat_i[7:0];

Writing a zero leaves a flag set. That is the entire reason W1C exists: a read-modify-write cycle cannot destroy a flag that arrived between the read and the write. It is also why W1C registers and the hardware-wins rule are the same problem seen twice — both exist so that an event which happened is never erased by a write that did not know about it.

8. A Lane Write Is Not A Partial Update Of A Value

Byte lanes raise a question B3 has no vocabulary for, and it is worth asking because the answer is uncomfortable.

A 32-bit configuration word written one lane at a time passes through states that the software never intended. Between the first lane write and the last, the register holds a value that is half old and half new — and the hardware reading that register does not know a sequence is in progress.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
          for (b = 0; b < SW; b = b + 1)
            if (mask[b]) reg_q[off_i][b*8 +: 8] <= dat_i[b*8 +: 8];

Each lane lands atomically. The register does not. The loop above is one clock, so a single-cycle write of any lane pattern is atomic — but two accesses are two clocks, and nothing in Wishbone Classic can join them.

B3 has no atomicity primitive for this. It has RMW cycles — PERMISSION 3.60 makes them optional and RULE 3.85 governs their timing — and an RMW cycle locks the bus for a read-then-write on one address. It does not let a master group two writes to two lanes of the same register into one indivisible update, because from the bus they are simply two transfers.

So a peripheral with a multi-byte field that hardware acts on continuously has three options, all of them local policy:

approachwhat it costs
require a single full-width writeSTRICT_ALIGN refuses lane writes to that offset — but then RULE 3.60's [SEL_O()] is decorative for that register
a shadow register plus a commit bittwo transfers instead of one, and a commit bit that must be documented
tolerate the intermediate statesfree, and correct only if every intermediate value is harmless

This slave takes the third, because its 16-bit config word at offset 4 is read by nothing while it is being written. That is a property of this design, not a general result, and it is exactly the kind of thing that belongs in a datasheet and never appears in one.

9. The Commit Clock Is Not The Slave’s Business

One line in the assembled slave does more work than it looks:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // The commit clock, qualified by the access being one that should have
  // an effect. A terminated-with-ERR access commits the TERMINATION and
  // must not commit the DATAPATH.
  logic effect_ok;
  assign effect_ok = commit_o && ack_o && present && !reserved;

A refused write still terminates. [ERR_O] is a termination; the phase ends; the master moves on. What must not happen is the datapath acting on it. Separating "the transfer ended" from "the transfer had an effect" is the single wire that keeps Chapter 24.4's four termination schemes from each needing their own write path.

10. What This Chapter Did Not Build

  • No write buffering. A write commits on its own termination clock; nothing is queued.
  • No write-to-read forwarding. A read on the clock after a write sees the new value because the register file is one clock deep, not because anything forwards.
  • No [RTY_O]. A write that cannot land is refused, not deferred. Chapter 24.5 argues about whether that is right.
  • No burst writes. Classic only.
  • No 64-bit port. RULE 3.95 governs that layout; this module is 32-bit throughout.

Next: Chapter 24.4 — ACK Generation is the Critical chapter of this module. Both datapaths here take a commit clock as an input and never ask where it came from. That chapter decides — and finds that B3 predicts the cost of the answer, to the clock.

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.