Wishbone · Module 19
LiteX
What a SoC generator settles that B3 leaves open — word addressing as a default, a separate narrow CSR bus with a named Wishbone bridge, and a CPU base class whose fields are an inventory of unspecified things.
Chapter 19.1 established that two boundaries in a RISC-V Wishbone system are specified by nobody. A SoC generator is a codebase that has already decided all of them, for every core it supports, and written the decisions down. That makes it unusually good reading.
What does LiteX settle that Wishbone B3 leaves open — and what does it still leave to you?
1. LiteX Is Not a Bus. It Is a Generator.
LiteX assembles SoCs from Python descriptions. Wishbone is one of the buses it can emit. The useful observation is not that LiteX "uses Wishbone" — it is that a generator must answer every open question at generation time, because there is no human in the loop to answer it later.
Three of its answers are worth reading against B3 directly.
2. Word Addressing, Made Explicit
From litex/soc/interconnect/wishbone.py, the Wishbone Interface defaults are data_width=32, adr_width=30, with sel_width = data_width//8.
Thirty address bits for a 32-bit data path is the word-addressing convention stated as a default. Two bits are gone, permanently, before any address map is written. They did not disappear — they moved into sel.
This is Chapter 12.1's convention, and it is the single most common source of confusion when a byte-addressing core meets a Wishbone slave. The teaching SoC in this module makes the same choice and confines the division to exactly two modules:
// ── BYTE ADDRESSES ABOVE, WORD ADDRESSES BELOW ──────────────────────────
// The core issues 32-bit BYTE addresses. The bus is WORD-addressed with
// AW = 12, which is Chapter 12.1's convention and, as it happens, LiteX's
// default too - its Wishbone Interface declares adr_width=30 for a 32-bit
// data width. The adapters divide by four. The boundary is in exactly two
// modules and nowhere else.
//
// byte 0x0000..0x03FF -> word 0x000..0x0FF boot ROM
// byte 0x1000..0x13FF -> word 0x400..0x4FF RAM
// byte 0x2000..0x200F -> word 0x800..0x803 timer
// byte 0x2800..0x280F -> word 0xA00..0xA03 GPIO
// byte 0x3000..0x300F -> word 0xC00..0xC03 CSR bankThe comment is doing real work there. If the byte-to-word boundary lives in more than one place, the two places will eventually disagree, and the symptom will be a peripheral that responds to the wrong address rather than anything that looks like an addressing bug.
3. A Separate Bus For Registers, With an Explicit Bridge
This is the decision that surprises people, and it is the one worth the most.
LiteX does not hang every peripheral register directly off Wishbone. It has litex/soc/interconnect/csr_bus.py, whose own docstring describes it as:
"The CSR Bus is a lightweight and low-bandwidth bus design for accessing Configuration and Status Registers (CSRs)."
Its signals are adr, re, we, dat_w, dat_r — not CYC, STB, ACK, SEL. And litex/soc/interconnect/wishbone.py contains class Wishbone2CSR(LiteXModule): an explicit, named bridge between the two.
Neither row is more correct. The upper one spends a bridge to avoid carrying SEL, ERR and cycle framing into every eight-bit configuration register; the lower one spends that apparatus to keep every register access visible as a Wishbone cycle, which is what makes Chapter 19.5 measurable.
This module's teaching SoC makes the opposite choice, deliberately: its register banks are Wishbone slaves directly, so that every register access in Chapter 19.5 is a Wishbone cycle you can watch. That is a teaching choice, not a better one, and stating which is which is the point of the exercise.
4. The Name Collision, Settled Once
"CSR" means two unrelated things in a RISC-V Wishbone system, and they are both in scope for this module.
| a RISC-V CSR | a peripheral CSR | |
|---|---|---|
| reached by | a csrrw-class instruction | a load or store |
| lives | inside the core | on the bus |
| appears on any bus | never | yes |
| specified by | the RISC-V privileged ISA | the peripheral's datasheet |
| example | mtvec, mstatus | a timer's RELOAD register |
Everything in Module 19 called CSR is the second kind. The first kind never leaves the core and therefore never reaches a seam. The register bank built for this module states the collision in its own header rather than assuming the reader will infer it:
// ── THE NAME COLLISION, STATED ONCE ─────────────────────────────────────
// "CSR" here means a memory-mapped peripheral register bank, which is how
// SoC generators use the term - LiteX has a whole csr_bus.py whose own
// docstring calls it "a lightweight and low-bandwidth bus design for
// accessing Configuration and Status Registers (CSRs)", and it is a
// SEPARATE BUS from Wishbone with its own adr/re/we/dat_w/dat_r signals,
// bridged by an explicit Wishbone2CSR module.
//
// A RISC-V Control and Status Register is a DIFFERENT THING with the same
// name: it is reached by instruction, lives inside the core, and never
// appears on any bus. Neither this module nor anything else in Module 19
// is about those.5. The CPU Base Class Is a List of Unspecified Things
litex/soc/cores/cpu/__init__.py defines a CPU base class. Its fields are, in effect, an inventory of everything neither RISC-V nor Wishbone covers:
| field | what it is | who specifies it |
|---|---|---|
periph_buses / memory_buses | the core has more than one bus port, and they are different lists | the core's author |
interrupts = {} | a named map of interrupt lines | nobody — see 19.4 |
mem_map = {"csr": 0x82000000} | where register space lands | the integrator |
reset_address_check | whether the core will boot from where you put the ROM | the core's author |
Read that list again as a specification gap report. Every one of those fields exists because two conformant specifications left the question open and somebody had to fill it in per core. periph_buses and memory_buses being separate lists is Chapter 19.1 §5's measurement written as a data structure: a core is more than one master.
6. What a Generator Cannot Decide For You
A generator settles the plumbing. It does not settle what a register does.
SIM H exercises four access policies in one 32-bit-wide register bank — the policies a generator emits code for but does not choose:
=== 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 PASSEvery row there is local register policy. The bus performed an ordinary WRITE cycle in each case. What differed was the slave's reaction: storage, refusal, a counter, or nothing at all. Chapter 19.5 takes each of these apart.
Note row five. A write to the read-only IDENT register was answered ERR_O, and the bank counted it. That is a legal Wishbone termination carrying a purely local meaning — and per RULE 2.15 item 4, B3's only requirement about it is that the datasheet say when it happens.
7. Partial Address Decoding, Which B3 Endorses
The bank above decodes only the two address bits it needs. That is not laziness; B3 lists it as a feature:
"each SLAVE decodes only the range of addresses that it requires... The remaining address bits are decoded by the interconnection system."
// ── PARTIAL ADDRESS DECODING ────────────────────────────────────────────
// This slave decodes only the two offset bits it needs. B3 lists that as a
// feature - "each SLAVE decodes only the range of addresses that it
// requires... The remaining address bits are decoded by the interconnection
// system." CSR_ALIAS is this module decoding one bit too few.And it is the origin of one of this module's six seeded defects. CSR_ALIAS is the same decoder with one bit too few — a slave that decodes adr[0] but not adr[1], so every register becomes two registers. Chapter 19.5 §7 measures what that actually breaks, and the answer is more than one thing, which is what makes it hard to find.
8. The Lesson, Stated Generally
| B3 settles | a generator settles | you still settle |
|---|---|---|
| cycle framing, termination, signal stability | address width convention, bridges, bus topology, which peripheral sits where | what each register does when written |
| what a slave may assert and when | which core connects to which bus port | what your core does with ERR_I |
that SEL_O is qualified by STB_O | sel_width = data_width//8 | which lanes a half-word at offset 2 asserts |
The third column is the one that does not get smaller when you adopt a generator. It is Chapter 19.4 and Chapter 19.5.
Continue learning
Related tutorials
- 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
Address Decoding
A bus address answers two questions, not one. Measured across a three-target SoC, including the boundary where one window ends and the next begins.
- Related topic
Address Maps
An address map is a contract with five signatories and only one of them is checked by a compiler. Measured at the four addresses per region where decode bugs live.
- Related topic
Open-Source SoC Examples
Three verified open-source Wishbone interconnects that made different architectural choices, read for what they reveal about topology — and a complete route and provenance audit closing the module.
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.
