Skip to content
VLSI Mentor

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:

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

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

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

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

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

  1. Power. A 32-bit bus toggling on every clock instead of on terminations.
  2. Leakage. Data from the last-addressed register sits on the bus between transfers, visible to anything that can observe the fabric.
  3. 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:

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        C6 weak test (changed)  correct 4  DAT_ALWAYS  5

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

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

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

A read with two wait states, shown as the slave's view. From IDLE the slave moves to PRESENTED when CYC_I and STB_I are both asserted. It remains in PRESENTED for as many clocks as it needs, driving no termination and, under RULE 3.65, no read data. When it is ready it moves to TERMINATE, where it asserts ACK_O, drives DAT_O, and fires any read side effect such as a FIFO pop or a status clear. All three of those happen on the same single clock, which is the point. It then returns to IDLE when STB_I is negated. The defective version fires the side effect on every clock spent in PRESENTED instead, popping once per wait state.IDLEPRESENTEDTERMINATECYC_I and STB_ICYC_I and STB_Inot ready — DAT_O stays inertnot ready — DAT_O stays inertnot ready —DAT_O stays…ready: ACK_O, DAT_O and popready: ACK_O,DAT_O and popSTB_I negated (RULE 3.50)STB_I negated (RULE 3.50)STB_Inegated…

6. The Measurement

Three rigs, same defective-or-correct RTL, one read of the FIFO:

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

A FIFO read with three wait states, comparing the correct side-effect clock against the defective one. CYC_I and STB_I are asserted from cycle 0 and held, because RULE 3.60 obliges the master to hold the request while it is unanswered. The slave asserts ACK_O only on cycle 3. Under RULE 3.65 the correct slave drives DAT_O only on that cycle, and pops the FIFO once, on that cycle. The defective slave pops on cycles 0, 1, 2 and 3 — every clock the request was presented — so four words leave the FIFO and the master receives one.presented — the defect pops herepresented — the defect popshereaccepted — DAT_O and the pop belong hereaccepted — DAT_O and thepop belong hereCLK_ISTB_IACK_ODAT_O000000555500pop okpop badt0t1t2t3t4t5t6t7t8t9

The FIFO's own accounting confirms where the words went:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      FIFO level left:  correct 3   defective 0
      underflows:       correct 0   defective 0

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

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

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

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

The 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_now is 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:

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

Read the two defective rows against each other:

  • DAT_ALWAYS breaks a rule and works. C6 catches it, every other checker says clean, and the FUNC column says ok because the data the master received was correct.
  • SIDE_ON_STB breaks 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:

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

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.