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:
// sel_i what the MASTER asked for
// lanes_i what the REGISTER actually occupies (from wb_slave_map) 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:
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:
// 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:
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
0x1122EE44against0x0000EE00. 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:
// 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 setRULE 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:
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 1PERMISSION 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.
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.
// 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.
// ── 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_ffresolve 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 whyhw_lost_oexists 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:
// 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 destroyedThat 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:
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.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:
| defect | how a test reaches it |
|---|---|
IGNORE_SEL | write one lane, read the others back |
SIDE_ON_STB | read a FIFO with wait states |
DAT_ALWAYS | watch the wire between transfers |
NO_NEGATE | drop [STB] and look at [ACK_O] |
BUS_WINS | arrange 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_oexists 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:
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:
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.
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:
| approach | what it costs |
|---|---|
| require a single full-width write | STRICT_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 bit | two transfers instead of one, and a commit bit that must be documented |
| tolerate the intermediate states | free, 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:
// 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
Related tutorials
- Related topic
SEL_O
How a 32-bit bus writes one byte without disturbing the other three: byte lanes, the per-lane conditional write, where endianness actually lives, and why ignoring the mask destroys data the transfer never named.
- Related topic
SEL Signals
The address names a word; SEL names which bytes of it take part. The lane binding is normative, the byte numbering is not, and confusing the two is the expensive mistake.
- 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
Memory-Mapped IO
Memory-mapped I/O does not turn a peripheral into memory. It gives the peripheral's registers addresses in the processor's address space, so an ordinary load or store selects them. The address then does two jobs — name the target, name the register inside it — and the map that assigns them is a contract between software and RTL.
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.
