Wishbone · Module 19
Peripheral Integration
Four register access policies in one bank, a side effect that must fire exactly once under wait states, two policies inside one 32-bit word, and a six-checker negative-control gate in which none of the six defects is a protocol violation.
Chapter 19.4 built the seam where a core meets the bus. This one builds the seam where the bus meets a register, and it is the boundary people most often assume does not exist.
Wishbone tells you what a legal WRITE cycle looks like. What does it tell you about what the register does when you write it?
Nothing. Not one word. And that is the correct division of labour — but it means RULE 2.00 is load-bearing rather than ceremonial:
"Each WISHBONE compatible IP core MUST include a WISHBONE DATASHEET as part of the IP core documentation."
1. A Register Is Not a Memory Word
// ─────────────────────────────────────────────────────────────────────────
// wb_csr_bank — SEAM 2, with every access policy in one place.
//
// A register is not a memory word. It is a set of FIELDS, each with its own
// rule about what a read returns and what a write does, and the interconnect
// knows none of that. Everything below is LOCAL REGISTER POLICY.
//
// offset 0 SCRATCH RW plain storage. SEL_O honoured per byte.
// offset 1 IDENT RO constant + an access counter. A write is
// answered ERR_O.
// offset 2 FLAGS W1C bits are SET by hardware (set_i) and CLEARED
// by writing a 1 to them. Writing 0 does
// nothing. This is not "just a write".
// offset 3 COMMAND WO a write fires a one-clock side effect. A read
// returns zero, which is a choice and is stated.
//Four registers, four different answers to "what happens when I write this". A bus that carries all four identically is doing its job correctly; the difference is entirely inside the slave.
Every arrow leaving the decoder carries the same cycle. Nothing about the Wishbone transfer distinguishes the four destinations. The entire difference is local register policy, and it is documentation before it is RTL.
2. SIM H — The Policy Matrix, Against a Stated Expectation
Each row below was checked in the simulator against an expectation written before the access was issued. The peripheral leg inserts two wait states, so none of these is a single-clock transfer.
=== SIM H - four access policies, one register bank ===
the CSR bank at byte 0x3000 decodes only two offset bits,
which is B3's Partial Address Decoding: "each SLAVE
decodes only the range of addresses that it requires...
The remaining address bits are decoded by the
interconnection system." Two wait states on this leg.
access pol result expected verdict
write SCRATCH word RW ok 0x11223344 (write) PASS
read SCRATCH RW ok 0x11223344 0x11223344 PASS
write SCRATCH byte+1 RW ok 0x1122ee44 (write) PASS
read SCRATCH again RW ok 0x1122ee44 0x1122ee44 PASS
write IDENT RO ERR 0x00000000 (write) PASS
read IDENT RO ok 0xc5b00001 0xc5b00001 PASS
read COMMAND WO ok 0x00000000 0x00000000 PASSRow three is the one to linger on. A byte write at offset +1 changed 0x11223344 into 0x1122ee44 — one lane, in place. That requires the adapter to have generated SEL_O = 0010 and the slave to have honoured it. Both halves of that have to be right, and they are on opposite sides of the bus from each other.
if (ack_o && we_i) begin
case (off)
// SEL_O honoured per byte lane. A byte write to offset 0 must
// leave the other three lanes alone, and Chapter 19.3 measures
// an adapter that makes that impossible.
2'd0: for (b = 0; b < SW; b = b + 1)
if (sel_i[b]) scratch_q[b*8 +: 8] <= dat_i[b*8 +: 8];
// WRITE-1-TO-CLEAR. Writing a 0 to a set bit leaves it set;
// writing a 1 clears it. A hardware set on the same clock wins,
// and that collision rule is stated rather than left to chance.
2'd2: if (sel_i[0]) flags_q <= (flags_q & ~dat_i[7:0]) | set_i;
default: ;
endcase
end3. Write-One-to-Clear Is Not "Just a Write"
FLAGS, offset 8, write-1-to-clear
hardware set bits 3 and 1 flags now 0x0a
read FLAGS 0x0000000a
write 0x02 (clear bit 1) flags now 0x08
write 0x00 (a plain write) flags now 0x08
-> write-1-to-clear is not just a write. Writing zero
to a set bit leaves it set, which is the whole
point: a read-modify-write of this register cannot
accidentally clear a flag it did not know about.Writing zero to a set bit left it set. That is the entire point of the policy: a read-modify-write of this register cannot accidentally clear a flag it did not know about, which is what would happen if FLAGS were plain storage and two pieces of software touched it.
And the flags are set by hardware while the bus is doing something else, so there is a collision to resolve — a flag can be set on the same clock the core writes a one to clear it. The GPIO slave writes that rule as a single expression, deliberately:
// ── THE BIT THAT IS NOT A REGISTER ──────────────────────────────────────
// The edge flags are set by the PINS. Nothing on the bus causes them and
// nothing on the bus prevents them. A flag can be set on the same clock
// the core writes a 1 to clear it, and the rule for that collision is
// written as one expression - hardware wins - because two non-blocking
// assignments to one flag would silently keep the clear and lose the
// event.Two non-blocking assignments to one flag would silently keep the clear and lose the event. This is the same class of defect as Chapter 16.3's counter bug, and the fix is the same: one assignment, one expression, hardware wins.
4. A Side Effect Must Fire Exactly Once
This is the defect that wait states create and that a zero-wait-state testbench cannot find.
logic cmd_hit;
assign cmd_hit = we_i && (off == 2'd3);
assign cmd_fire_o = FIRE_EVERY_CLK ? (xfer && cmd_hit)
: (ack_o && cmd_hit);
assign cmd_value_o = dat_i; COMMAND, offset 12, a side effect with two wait states
one write, phase held 3 clocks commands fired 1
-> fired on the ANSWERED clock, once. A slave that
acted on every presented clock would have fired
three commands from one write.One write, phase held for three clocks, one command fired. With FIRE_EVERY_CLK the side effect keys off xfer — the request being presented — rather than ack_o, the request being accepted, and one write becomes three commands.
5. Two Policies Inside One 32-Bit Word
// ── WHY THE MASK IS IN THE SAME REGISTER AS THE FLAGS ───────────────────
// So that one 32-bit access can read both, and so that the byte lanes have
// different policies within one word: SEL_O lane 0 is W1C, lane 1 is RW.
// A slave that ignores SEL_O cannot implement that, and an adapter that
// cannot express a byte write makes it unreachable. Chapter 19.4 measures
// exactly that failure. GPIO, two policies inside one 32-bit word
write mask 0xF0 into lane 1 irq line 0
pin 4 rises flags 0x10 irq 1
read ISTAT 0x0000f010
byte write 0x10 to lane 0 flags 0x00 irq 0
-> lane 0 is write-1-to-clear and lane 1 is plain RW,
in the same register. An adapter that cannot
express a byte write cannot reach either of them
separately - which is what SIM E measured.Read 0x0000f010. Lane 1 holds the interrupt mask 0xF0, written by an earlier access; lane 0 holds the edge flag 0x10, set by a pin. One register, two policies, two lanes — and the byte write of 0x10 to lane 0 cleared the flag and dropped the interrupt line without disturbing the mask.
This is why Chapter 19.4 §3 matters at this seam. An adapter that asserts all four lanes on every access cannot reach either field separately. The register is not exotic; packing a mask next to its flags is the normal thing to do, and it is normal precisely because SEL_O exists.
6. What the DATASHEET Has To Say
RULE 2.15 enumerates what a datasheet must state. Four of its items are the whole of this chapter:
| item | what must be documented | why it cannot be inferred |
|---|---|---|
| 4 | how a master reacts to ERR_I | PERMISSION 3.20: "This specification does not dictate what the MASTER does in response to [ERR_I]." |
| 4 | the conditions under which a slave generates ERR_O | a slave may refuse for any local reason — a read-only register, an unimplemented offset |
| 7 | the port size — 8, 16, 32 or 64-bit | nothing on the wires says how wide the other end thinks it is |
| 8 | the granularity | SEL_O's array boundaries are the granularity; get it wrong and every sized access is wrong |
A slave that does not state its granularity cannot be integrated, because there is no way to know what SEL_O bit 2 means to it. That is not a rule about silicon. It is a rule about a document, and it is the specification acknowledging that the gap between two conformant interfaces is closed by writing, not by wiring.
7. The Negative-Control Gate
A checker that has only ever passed has not been shown to check anything. Six checkers are run against seven systems: the correct one, and six that differ from it by exactly one parameter.
Every one of those six parameters is a decision somebody could plausibly make, and not one of them is a Wishbone protocol violation.
=== NEGATIVE-CONTROL GATE ===
one stimulus, seven systems. Each broken system differs
from the correct one by exactly one parameter. Every
parameter is a decision somebody could plausibly make
and none of them is a Wishbone protocol violation.
checker corr DROP SEL ERR ALIAS FIRE IRQ
ONE TRANSFER PER REQUEST PASS FAIL PASS PASS PASS PASS PASS
SEL MATCHES ACCESS SIZE PASS PASS FAIL PASS PASS PASS PASS
ERROR REACHES THE CORE PASS PASS PASS FAIL PASS PASS PASS
REGISTER DECODE IS EXACT PASS PASS PASS PASS FAIL PASS PASS
SIDE EFFECT FIRES ONCE PASS PASS PASS PASS FAIL FAIL PASS
INTERRUPT REACHES THE CORE PASS PASS PASS PASS PASS PASS FAIL
raw numbers behind those verdicts
rig LSreq LSxf drop SELv traps rd12 cmds irqs
correct 10 10 0 0 1 0x00000000 1 1
REQ_DROP 10 10 3 0 1 0x00000000 1 1
SEL_IGNORED 10 10 0 8 1 0x00000000 1 1
ERR_SWALLOWED 10 10 0 0 0 0x00000000 1 1
CSR_ALIAS 10 10 0 0 2 0xc5b00001 0 1
FIRE_EVERY_CLK 10 10 0 0 1 0x00000000 3 1
IRQ_VIA_BUS 10 10 0 0 1 0x00000000 1 0
scratch register after the three sized writes
correct 0xbeef7744
SEL_IGNORED 0x77777777
(every other rig writes the scratch correctly)
CHECKERS REQUIRED: >= 5
CHECKERS DECLARED: 6
CHECKERS PASSING THE CORRECT DUT:
6/6 (any failure here would be an error below)
EACH CHECKER FAILS ITS OWN TARGET:
6/6Six checkers, six targets, six detections, and every checker passes the correct system. That is the gate.
The interesting cell is not on the diagonal
READ THE MATRIX, NOT THE DIAGONAL.
Five of the six defects are caught by exactly one
checker and are invisible to the other five. The SEL
checker does not notice a swallowed error; the trap
checker does not notice a missing interrupt; not one of
them would have found the system broken on its own.
CSR_ALIAS is the exception and it is the most useful
cell in the table. It fails TWO checkers - decode and
side effect - because address decode sits upstream of
everything the slave does. The COMMAND write never
reached COMMAND; it landed on IDENT, which is read-only,
so the bank answered ERR and the core took a second trap
it had no reason to expect. One wrong bit in a decoder
produced a missing side effect and a spurious error,
and neither symptom names the cause.CSR_ALIAS trips two checkers, and the second one is not obviously related to the first. The decoder ignored one address bit, so a write intended for the write-only COMMAND register landed on the read-only IDENT register instead. The bank did the right thing — answered ERR_O, per its own documented policy — and the core took a trap it had no reason to expect, while the side effect the software was waiting for never happened.
Neither symptom names the cause. A missing side effect and a spurious error are what you would see on a logic analyser; one wrong bit in an address decoder is what you would have to deduce.
8. The Integration Audit
Every number below comes from the single run published in Chapter 19.1 §6 — boot, RAM fill, DMA copy, concurrent core traffic, timer interrupt, interrupt service. Nothing was re-stimulated to make a table tidy.
=== SIM J - the integration audit ===
every number below comes from the run above. Nothing
was re-stimulated to make a table look tidy.
SEAM 1 - the core side, per port
port requests transfers answers
instruction 10 10 10
load/store 18 18 18
-> ONE REQUEST, ONE TRANSFER, ONE ANSWER, both ports.
seam violations dropped moved SEL ADDR trap
instruction 0 0 0 0 0
load/store 0 0 0 0 0
stall clocks (core not ready) IF 0 LS 0
wait clocks (bus not ready) IF 21 LS 40
-> counted separately and never summed. They are two
different parties refusing to proceed.
SEAM 2 - the bus side
shared-bus clocks 614
clocks a cycle was open 66
ownership: IF 20 LS 38 DMA 8
ACK terminations 36
ERR terminations 0
transfers seen by both seams 28
clocks the DMA was busy but did not own the bus 10
CONSERVATION: 28 core transfers + 8 DMA transfers
= 36, and the bus terminated 36 times.
-> every transfer any master started was terminated
exactly once, and nothing terminated that nobody
started. The DMA's 4 words are 8 transfers
because a copy is a read and then a write.Three things in that audit are worth naming.
One request, one transfer, one answer, both ports. Ten and eighteen, matched three ways. A seam that invents or destroys work fails here first.
Stall and wait are on separate lines with a note saying why. Twenty-one wait clocks on the fetch port and forty on the data port, against zero stall clocks on both. Adding them would produce a number that describes nothing — Chapter 19.3 §5 measured why.
The conservation check balances. Twenty-eight core transfers plus eight DMA transfers against thirty-six terminations. The DMA's four words are eight transfers because a copy is a read and then a write — and a system in which that arithmetic does not close has either lost a termination or delivered one nobody asked for.
SLAVES
ROM reads 10 refused 0
RAM writes 8
timer expiries 1 reloads 1
GPIO edges 0
CSR commands 0 writes refused 0
CSR scratch 0x5a5a0005
DMA words 4
THE CORE'S VIEW
traps taken 0
interrupts 1
-> the trap count is a seam-1 number and the interrupt
count is neither seam's. B3 describes one of these
mechanisms and names neither.The last line is the module. The trap count is a seam-1 number. The interrupt count is neither seam's. B3 describes one of these mechanisms and names neither.
9. What To Take Away
| the question | who answers it | |
|---|---|---|
| the cycle | is this a legal Wishbone transfer? | B3, completely and unambiguously |
| seam 1 | does the core's request survive becoming one? | you, and the core's README |
| seam 2 | what does the register do when it arrives? | you, and RULE 2.00 says: in writing |
The bus was never the hard part. Eighteen chapters settled it. The two boundaries B3 declines to describe are where the defects in this module live, and all six of them are legal.
Continue learning
Related tutorials
- 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.
- Related topic
The Slave Interface
A Wishbone slave never initiates. It observes a qualified request, interprets a local offset, and ends the transfer with exactly one of three terminations. Everything that makes it reusable comes from what it refuses to know: its own base address, the topology, and which master is asking.
- Related topic
Byte Enables
A select pattern becomes write-enable logic two different ways. Measured on a walking-one write against a register whose four bytes all differ.
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.
