Wishbone · Module 24
Read Logic
RULE 3.65 qualifies DAT_O with the termination, so everything the read caused is qualified too. Fire a FIFO pop on the presented clock instead and four words leave for one read — a write can be repeated, a popped word is gone.
There is a rule in Wishbone B3 that Modules 1 through 23 never quoted. It is three lines long, it sits in the middle of chapter 3, and it governs the whole of this chapter:
RULE 3.65 — "SLAVE interfaces MUST qualify the following signals with
[ACK_O],[ERR_O]or[RTY_O]:[DAT_O()]."
It is the slave-side mirror of RULE 3.60. A master qualifies its request with [STB_O]; a slave qualifies its answer with the termination. [DAT_O()] is meaningful on the termination clock and at no other time.
That sounds like a statement about wires. It is really a statement about time, and its consequence reaches much further than the data bus — because if the data is qualified by the termination, then anything that produced that data must be qualified by it too.
1. Four Sources, One Bus
A real peripheral's [DAT_O()] is a multiplexer. This one has four inputs:
// SRC_REG a stored register. Reading it changes nothing.
// SRC_FIFO a queue. READING IT POPS. <-- side effect
// SRC_COUNT a free-running counter. Reading it changes nothing, but
// the value is only true for one clock - see 24.2 on why
// that is not the same as "unstable".
// SRC_STATUS a flag word. READING IT CLEARS. <-- side effectTwo of the four change state when read. That is not exotic — a FIFO data port and a read-to-clear interrupt status are in almost every peripheral ever shipped — and it is the reason [DAT_O()]'s timing matters more than it looks.
The multiplexer is written as continuous assigns, for a reason this curriculum keeps relearning:
// Continuous assigns throughout. Indexing fifo_q with lvl_q inside an
// always_comb makes Icarus warn about the sensitivity list; a
// continuous assign has no sensitivity list to get wrong.
logic [DW-1:0] raw;
assign raw = (src == SRC_FIFO) ? fifo_head :
(src == SRC_COUNT) ? count_q :
(src == SRC_STATUS) ? {24'd0, stat_q} : reg_val_i;And the width of the register clips what reaches the bus:
// A narrow register must not present bytes it does not own. The map
// supplies the lanes; this masks to them, so a 32-bit read of an 8-bit
// register returns the byte and zeros, never neighbouring state.
logic [DW-1:0] masked;
assign masked = raw & {{8{lanes_i[3]}}, {8{lanes_i[2]}},
{8{lanes_i[1]}}, {8{lanes_i[0]}}};"Never neighbouring state" is the security-relevant half of that comment. A slave that returns whatever happened to be on the internal bus for lanes the register does not own is leaking, and nothing in the protocol will tell you.
2. The Rule, In One Line Of RTL
// ── RULE 3.65, in one line ────────────────────────────────────────────
// Read data is presented ONLY while a termination is asserted. The
// defect drives it always.
logic qualified;
assign qualified = DAT_ALWAYS ? 1'b1 : term_i;
assign dat_o = (qualified && present_i) ? masked : '0;DAT_ALWAYS is the wrong version, and it is worth being precise about how wrong it is.
A conformant master samples [DAT_I()] only on the termination clock. So a slave that drives read data continuously produces correct behaviour against every well-behaved master in the system. It is invisible, exactly like the RULE 3.30 gate defect that Chapter 23.2 had to build a rule-breaking testbench to expose.
So why does it matter? Three reasons, none of which a functional test will find:
- Power. A 32-bit bus toggling on every clock instead of on terminations.
- Leakage. Data from the last-addressed register sits on the bus between transfers, visible to anything that can observe the fabric.
- The multiplexer never rests, so its timing arc is live every clock rather than only when it is needed.
3. Proving It Requires Watching The Wire
wb_conformance — Module 23's five-rule monitor — cannot see this. It watches [CYC], [STB], [ADR], [SEL], [WE] and the three terminations. Nothing watched the data.
So Module 24 adds a monitor:
// THIS MONITOR USES THE STRICTEST READING: non-zero [DAT_O()] with no
// termination asserted is a violation. That is a LOCAL POLICY of the
// CHECKER, it is stricter than the rule requires, and saying so matters -
// a checker that is stricter than its rule will fail conformant designs.B3 says QUALIFIED, not ZERO. A slave that holds its last value between transfers is arguably qualified too, since the master is not permitted to look. So the checker reports two numbers: the strict test (non-zero when unqualified) and the weaker, more defensible one (the data changed while nobody was allowed to look).
C6 weak test (changed) correct 4 DAT_ALWAYS 5The correct slave trips the weak test four times — its own [DAT_O()] returns to zero when the termination drops, which is a change. A checker stricter than its rule will fail conformant designs, and publishing both numbers is how you stay honest about which one you are enforcing.
4. Reading Data Back Requires Being A Master About It
The first version of this chapter's testbench read every source and got zero from all of them. That was not a DUT bug. It sampled dat_o after the phase had ended, when RULE 3.65 says it is meaningless by design. The slave was correct and the instrument was wrong.
Rather than describe that mistake, the testbench now measures both, on the same correct slave, in the same run:
off source on ACK after the phase verdict
0 reg 0x11223344 0x00000000 PASS
4 reg/16 0x0000beef 0x00000000 PASS
2 status 0x0000000a 0x00000000 PASS
7 reg/16 0x00000000 0x00000000 PASS
1 counter 0x0000002f then 0x00000035 PASS
THE SECOND COLUMN IS THE SAME SLAVE, SAMPLED AFTER
THE PHASE ENDED. It reads 0x00000000 from every
source, and the slave is entirely correct: RULE 3.65
qualifies [DAT_O()] with the termination, so outside
that clock the value is meaningless BY DESIGN.
The first version of this testbench sampled there and
reported five failures against a working slave. // ── CAPTURE REGISTERS, WHICH THIS TESTBENCH NEEDED AND DID NOT HAVE ──
// The first version of SIM B sampled dat_o AFTER the phase ended and
// read zero from every source. That was not a DUT bug: RULE 3.65 says
// [DAT_O()] is qualified by the termination, so outside the
// termination clock it is meaningless BY DESIGN. A master latches it
// on the termination, and so must a testbench.The counter returned two different values on two reads and that is a PASS. A free-running counter's value is true for exactly one clock. That is not instability — it is the register's meaning. RULE 2.15 item 11 exists partly for this: a datasheet that does not say the sequence is UNDEFINED has implied a determinism the hardware does not have.
5. The Consequence: A Read That Pops
Here is where RULE 3.65 stops being about wires.
If [DAT_O()] is qualified by the termination, then the FIFO pop that produced it must be qualified by the termination. Fire it on the presented clock instead and the FIFO pops once per wait state.
// ── THE SIDE-EFFECT CLOCK ─────────────────────────────────────────────
// `accept_i` is the termination clock. `read_i` is every presented
// clock. RULE 3.65 qualifies [DAT_O()] with the termination, so the
// effect that produced that data must be qualified the same way.
logic effect_now;
assign effect_now = SIDE_ON_STB ? read_i : accept_i;Chapter 23.3 found this shape on the write side, where a write-only strobe fired WAITS+1 times. The read side is worse, and the reason is not symmetry:
A write can be repeated. A popped word is gone.
6. The Measurement
Three rigs, same defective-or-correct RTL, one read of the FIFO:
rig waits FIFO pops for 1 read
zero wait states 0 1 correct
three wait states 3 1 correct
three waits + defect 3 4 THE DEFECT
-> The defect popped 4 words for ONE read, which
is WAITS+1. Three words left the FIFO and the
master received one of them. The other two are
NOT RECOVERABLE - there is no re-read that can
bring them back.
AND THE ZERO-WAIT RIG POPPED EXACTLY ONCE, with the
same defective RTL, because at zero wait states the
presented clock and the accepted clock ARE THE SAME
CLOCK. The fast path is the one that tests clean.WAITS + 1 again. The severity of the bug is set by the slave's own latency, so it gets worse exactly when the system is under load — and a bench test against a fast memory reports it clean.
The FIFO's own accounting confirms where the words went:
FIFO level left: correct 3 defective 0
underflows: correct 0 defective 0Zero underflows on the defective rig. It did not read past the end — it consumed exactly what was there, four words for one read, and reported nothing. An underflow counter would not have caught this, because nothing underflowed.
7. Push And Pop On The Same Clock
A FIFO read port has a second timing problem that has nothing to do with Wishbone and everything to do with being a FIFO: hardware can push on the clock the bus pops.
// the FIFO. A push and a pop on the same clock is a shift, not a
// conflict, and is handled as one expression for that reason.
if (push_i && !pop_now) begin
if (lvl_q < FIFO_DEPTH[3:0]) begin
fifo_q[lvl_q[2:0]] <= push_dat_i;
lvl_q <= lvl_q + 4'd1;
end
end else if (pop_now && !push_i) beginThree cases, written as three explicit arms rather than two independent if statements. The reason is the same one Chapter 23.3 found on the write-one-to-clear path: two separate non-blocking assignments to lvl_q do not race in simulation, they resolve deterministically and silently in favour of whichever was written last. Push-and-pop written as two independent statements produces a level count that is wrong by one, every time they coincide, with no warning from any tool.
The simultaneous case is genuinely a shift: one word leaves the head, one arrives at the tail, and the level does not change.
end else if (pop_now && push_i) begin
if (empty) begin
fifo_q[0] <= push_dat_i; // straight through
npop_q <= npop_q + 16'd1;
end else beginThe empty case is separate again, because a push into an empty FIFO that is simultaneously being popped is a pass-through: the word never occupies a slot. Get that one wrong and the FIFO reports a pop it did not perform, or loses the word entirely.
None of this is in Wishbone B3, and none of it is reachable from the bus. It is the slave's internal contract with its own hardware side, and the only reason it appears in a Wishbone chapter at all is that
pop_nowis derived from the bus's commit clock — so a defect in the termination timing and a defect in the FIFO accounting produce the same symptom.
8. What No Protocol Checker Sees
The negative-control gate runs this defect past six checkers:
rig C1 C2 C3 C4 C5 C6 FUNC
correct 0 0 0 8 0 0 ok
DAT_ALWAYS 3.65 0 0 0 8 0 50 ok
SIDE_ON_STB 0 0 0 8 0 0 OVER-POPPEDRead the two defective rows against each other:
DAT_ALWAYSbreaks a rule and works. C6 catches it, every other checker says clean, and the FUNC column saysokbecause the data the master received was correct.SIDE_ON_STBbreaks no rule and destroys data. Every checker says clean, including the new one, and only the functional column knows.
The rule violation is harmless and the harmless-looking one violates no rule. A conformance suite would pass the data-destroying slave and flag the one that merely wastes power.
9. The Datasheet Entry That Has To Exist
RULE 2.15 does not have a line for "this register changes when you read it". It should. Since it does not, the header carries it explicitly:
// read side effects : YES - offsets tagged SRC_FIFO and SRC_STATUS
// change state when read. See the WARNING below.A map that does not flag read side effects is actively dangerous, because the reasonable assumption — that reading is safe — is the one every debugger, every memory-dump tool and every "let me just look at the registers" session relies on. A debugger that dumps this peripheral's map empties its FIFO and clears its interrupt flags, and the map is the only place that can warn anybody.
10. What This Chapter Did Not Build
- No
[ERR_O]generation. Reads of reserved offsets error, but the policy lives in Chapter 24.5. - No termination timing. The read path consumes a commit clock; Chapter 24.4 decides when that clock is.
- No write path. Chapter 24.3 owns it.
- No burst reads.
[CTI_I()]is registered-feedback territory; PERMISSION 4.05 makes it optional and Chapter 22.5 measured it. - No read shadowing. A read returns the live source, which is why the counter moves between reads.
Next: Chapter 24.3 — Write Logic goes the other direction and finds a conflict the read path never has: two writers, one register, the same clock — and no bus transaction can provoke it.
Continue learning
Related tutorials
- Related topic
Wait States
While a read waits, the master waits for an answer. While a write waits, it holds the data that will mutate the device. Payload coherence and one-shot commits are the two obligations that follow.
- Related topic
Slave Delays
A delayed slave must hold a request while it works. Two capture styles measured: one costs a clock of latency it can never recover, the other couples you to the master's conformance.
- Related topic
Transaction Restart
A retry needs at least one non-presented clock or it is not a second transfer at all. Measured across RETRY_DELAY, and against a target whose early commit turned one write into three.
- Related topic
Data Masking
Mask first, register semantics second. Measured: a command firing from a lane the transfer never delivered, and a status register surviving a word of ones.
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.
