Wishbone · Module 29
Open Hardware Projects
A hand-written two-bit mux, a generated arbiter-and-decoder, and a formally-verified B4 pipelined crossbar with a starvation timeout — compared factually, with no winner declared.
Three projects. Three interconnects. One protocol family, and almost nothing else in common.
This chapter is not a directory and not a ranking. The objective is a single observation, made three times with evidence:
The specification constrains far less than people assume, and the space it leaves is where the engineering happens.
1. What We Inspected
| project | repository | commit | date | licence | status |
|---|---|---|---|---|---|
| SERV / servant | github.com/olofk/serv | f200eb2e | 2026-08-25 | ISC; Apache-2.0 on servile | active, not archived |
| LiteX | github.com/enjoy-digital/litex | dce79bf9 | 2026-09-21 | BSD-2-Clause | active, not archived |
WB2AXIP / wbxbar | github.com/ZipCPU/wb2axip | 2e8d3bc2 | 2026-06-02 | Apache-2.0 | active, not archived |
All three inspected 2026-09-22, and all three are the canonical upstream repositories — not forks or mirrors, checked against the GitHub API on the same date. The first two are the deep cases of 29.1 and 29.2; this chapter adds the third and sets them side by side.
2. The Third Project States Its Own Profile
wbxbar.v opens by saying what it is, which saves a great deal of guessing:
// Purpose: A Configurable wishbone cross-bar interconnect, conforming
// to the WB-B4 pipeline specification, as described on the
// ZipCPU blog.ORIGINAL SOURCE EXCERPT — ZipCPU/wb2axip, rtl/wbxbar.v, commit 2e8d3bc2.
And its port list proves it, because it carries signals B3 Classic does not have:
output wire [NM-1:0] o_mstall,ORIGINAL SOURCE EXCERPT — same file.
input wire [NS-1:0] i_sstall, i_sack,ORIGINAL SOURCE EXCERPT — same file.
STALL does not exist in B3 Classic. In Classic, a slave that needs time withholds its termination and the phase simply stays open — that is the entire mechanism, and Module 9 is built on it. A STALL wire is a different contract: it says whether a new request is accepted this clock, independently of when any earlier one is answered.
This is the boundary that matters most when reading unfamiliar source. Everything Modules 5 through 9 taught about "the phase is open until its termination arrives" is a statement about B3 Classic. Carry it into a pipelined interface and you will conclude, confidently and wrongly, that an acknowledgement belongs to whatever is currently on the address lines.
The one consequence worth measuring:
=== LAB F - PROFILE BOUNDARY ===
eight operations, a three-clock target, one parameter
profile issued acked max outstanding clocks
B3 Classic 8 8 1 41
pipelined 8 8 4 20OUR MEASUREMENT, from a VLSI Mentor reconstruction — not from running wbxbar.
In Classic, max outstanding is 1 by construction. Not by choice, not by implementation quality: a phase is a request held until its termination, so there is nowhere for a second one to be. In the pipelined model the depth is whatever somebody chose. wbxbar's own header says:
// LGMAXBURST can be set to control the maximum number of outstanding
// transactions. An LGMAXBURST of 6 will allow 63 outstanding
// transactions.ORIGINAL SOURCE EXCERPT — same file.
Do not read the clock counts as a performance comparison. They are two different protocols answering the same eight requests. The numbers differ because the contracts differ.
3. Three Answers To The Starvation Question
Chapter 28.4 asked what you would do about a master that waits forever, and noted that B3 mandates no arbitration algorithm at all. Here is what three projects actually did.
SERV/servant does not arbitrate in any general sense. Its arbiter merges two ports belonging to one CPU, with a fixed winner:
assign o_wb_mem_adr = i_wb_cpu_ibus_stb ? i_wb_cpu_ibus_adr : i_wb_cpu_dbus_adr;ORIGINAL SOURCE EXCERPT — olofk/serv, servile/servile_arbiter.v, commit f200eb2e.
Starvation is not a question this design has, because there is only one requester with two mouths and the file states they are never both open.
LiteX uses round-robin, with the grant advancing either freely or per transaction:
# connect bus requests to round-robin selector
reqs = [m.cyc for m in masters]
self.comb += self.rr.request.eq(Cat(*reqs))ORIGINAL SOURCE EXCERPT — enjoy-digital/litex, litex/soc/interconnect/wishbone.py, commit dce79bf9.
Round-robin bounds the service interval as a local policy, not as a protocol property.
wbxbar does something neither of the others does — it makes starvation an error:
// OPT_STARVATION_TIMEOUT, if set, applies the OPT_TIMEOUT counter to
// how long a particular master waits for arbitration. If the master is
// "starved", a bus error will be returned.ORIGINAL SOURCE EXCERPT — ZipCPU/wb2axip, rtl/wbxbar.v, commit 2e8d3bc2.
That is a genuinely different position. LiteX prevents starvation structurally; wbxbar permits it, detects it, and converts it into something the master can act on. Both are defensible. Neither is required by Wishbone, and a system that needs one and installs the other has a requirements problem, not a protocol problem.
Note also how differently the two timeouts answer. LiteX's watchdog synthesises an ACK with all-ones data. wbxbar's returns a bus error and aborts the cycle:
// OPT_TIMEOUT, if set to a non-zero value, is a number of clock periods
// to wait for a slave to respond. Should the timeout expire and the
// slave not respond, a bus error will be returned and the slave will
// be issued a bus abort signal (CYC will be dropped).ORIGINAL SOURCE EXCERPT — same file.
Same problem, opposite answers, and the reason is architectural: wbxbar can afford ERR because its masters are expected to handle it, and LiteX's ACK is the conservative choice for a framework that must work with masters which may not.
4. The Comparison
| SERV / servant | LiteX | wbxbar | |
|---|---|---|---|
| inspected revision | f200eb2e | dce79bf9 | 2e8d3bc2 |
| profile | Classic-style, no STALL | Classic-style, no STALL | WB-B4 pipelined (stated) |
| masters | one CPU, two ports | N, parameterised | NM, default 4 |
| slaves | RAM + one aggregate port | M, parameterised | NS, default 8 |
| topology | fixed mux + I/D merge | shared or crossbar | crossbar |
| decode | ADR[31:30], two bits | per-slave function, CYC-gated | SLAVE_ADDR / SLAVE_MASK arrays |
| arbitration | fixed, instruction wins | round-robin, two modes | per-slave, with grant tracking |
| response steering | live selector, licensed by a comment | live grant, or per-transaction | per-master grant state |
ERR | NOT PRESENT | present, ORed across slaves | present, and generated by timeouts |
RTY | NOT PRESENT | NOT FOUND in the inspected file | NOT VERIFIED |
| timeout | NOT PRESENT | watchdog → synthesised ACK | OPT_TIMEOUT → bus error + abort |
| starvation | not applicable | prevented by round-robin | OPT_STARVATION_TIMEOUT → error |
| registered outputs | no | register=True on the decoder | OPT_DBLBUFFER |
| verification evidence | testbenches exist; NOT inspected | NOT INSPECTED | formal properties in-file |
| outstanding transfers | 1 | 1 | up to 63 at LGMAXBURST=6 |
No column of that table is a score. Read it as three engineering positions, each coherent with the system around it — and each a trade-off somebody made deliberately:
| project | what it costs | what it buys |
|---|---|---|
| SERV / servant | no way to signal an error, no room in the map | an interconnect of two combinational modules |
| LiteX | one shared interface everything queues on | any master and slave count, from one declaration |
wbxbar | pipelined state, outstanding-transfer tracking, a wider port list | depth, per-slave concurrency, and failures that report themselves |
5. The Verification Row Is Not Decoration
wbxbar carries its properties in the same file as the logic:
`ifdef FORMALORIGINAL SOURCE EXCERPT — same file.
and among them, an invariant that should look familiar:
assert((grant[N][NS-1:0] & grant[iN][NS-1:0])==0);ORIGINAL SOURCE EXCERPT — same file.
Two masters may not hold the same slave's grant simultaneously — a one-hot ownership property, asserted rather than assumed. Chapter 26.2 argued that an assertion nobody has seen fail is an assertion nobody has tested; a formally-proved one is a stronger statement than either.
SOURCE FACT: the file contains FORMAL-guarded assertions including grant exclusivity.
NOT VERIFIED: we did not run the proofs. We are reporting that the properties exist and what one of them says.
6. The Crossbar Question, Measured
wbxbar is a crossbar. LiteX offers one. The tempting sentence is "a crossbar is faster", and it is not a sentence the structure supports on its own.
A crossbar removes the single shared interface that transfers would otherwise queue on. Whether that is worth anything depends entirely on whether the traffic is going to different places:
THE MEASURED CONCLUSION
With disjoint destinations the crossbar had two transfers
selected on 4 clocks and the shared interface on 0,
finishing in 21 clocks against 25.
With both masters aimed at ONE slave the crossbar had
0 concurrent clocks and finished in 25, against 25.OUR MEASUREMENT, from a VLSI Mentor reconstruction of the inspected LiteX classes.
21 against 25 with disjoint destinations. 25 against 25 with a shared one. The architectural statement is the topology permits two transfers to proceed independently. The performance statement needs a workload, and with the wrong workload the answer is zero.
That is the honest form of the claim, and it is also the useful one: it tells you what to measure before choosing.
7. What Not To Generalize
Do not transfer timing assumptions across profiles. wbxbar is B4 pipelined and says so. Reading it with Classic habits produces wrong conclusions about which acknowledgement belongs to which request.
Do not read the table as a maturity ranking. SERV's missing ERR is not a gap; it is an absent requirement. wbxbar's starvation timeout is not over-engineering; it is a different system's need.
Do not infer deployment from any of this. Three maintained open-source repositories is exactly what we verified. Stars, age and conference appearances are not deployment evidence, and we are not claiming any.
Do not assume the defaults are what anybody ships. OPT_TIMEOUT defaults to 0 — disabled — in the inspected source. Every one of these knobs has a default and a reason somebody might change it.
Do not treat "no RTY" as three identical findings. For SERV it is a verified absence in the inspected files. For LiteX it is NOT FOUND in the one file we read, which is a weaker statement about a 1,147-line module inside a much larger framework. For wbxbar it is NOT VERIFIED, because we did not look. Those are three different epistemic positions and collapsing them into one row would be the exact failure this chapter is trying to avoid.
8. What To Carry Forward
- Establish the profile before reading the timing. If the port list has
STALL, your Classic intuitions are about a different contract. - B3 leaves arbitration, timeout, error policy and topology to you — and three projects took three different routes through that space.
- A timeout that answers
ACKand a timeout that answersERRsolve the same problem for different masters. Which is right depends on what your master can handle. - "Faster" needs a workload. 21 versus 25, or 25 versus 25, from the same two topologies.
- Properties in the file are evidence. Grant exclusivity asserted beats grant exclusivity assumed.
Chapter 29.4 goes back to the smallest systems and asks what they are actually good for.
Continue learning
Related tutorials
- Related topic
Crossbar Concepts
Two slaves acknowledging two different masters on the same clock — and the same crossbar, given the same destination twice, serialising exactly like a shared bus. Contention moves; it does not disappear.
- 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.
- Related topic
Interconnect RTL
Shared bus and crossbar from one module, one parameter apart. The crossbar won on traffic where no transfer overlapped any other — the limit was ownership granularity, never slave bandwidth.
- Related topic
FPGA SoCs
A two-bit address decode, an arbiter whose safety rests on a comment, and one wire called CYC at one end and STB at the other — reconstructed from the servant source at a recorded commit.
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.
