Wishbone Bus
Wishbone is the open, royalty-free, single-clock synchronous on-chip interconnect maintained by OpenCores and originally drafted by Wade Peterson of Silicore Corporation. Its B4 revision, published in 2010, formalizes a master and slave interface built around a tiny handshake (CYC, STB, ACK), a byte-granular address and data path, and two cycle modes (classic and pipelined) (Wishbone B4 specification, OpenCores 2010). Wishbone is placed explicitly into the public domain and is the de facto interconnect of every serious open-source RISC-V and OpenRISC soft core, sitting where ARM’s AMBA AXI sits in commercial closed-source SoCs (OpenRISC, Wikipedia; AMBA AXI, Wikipedia).
Why Wishbone Exists
Before standard on-chip buses, every integrated processor design had its own way of wiring CPUs to memories and peripherals. Each new IP core (intellectual property core, i.e. a reusable RTL block such as a UART or memory controller) needed custom glue logic to fit any new chip. The Wishbone authors set out to “foster design reuse by alleviating System-on-Chip integration problems” by defining a “common interface between IP cores” so that cores from different suppliers could be wired together with no glue logic at all (wbspec_b4, Chapter 1). The revision history at OpenCores is short: Revision B.3 in 2002 introduced the pipelined-mode signalling, and Revision B.4 (the current standard) was published on 2010-06-22 with refinements to the pipeline traffic mode and clearer documentation (OpenCores Wishbone HOWTO). The bus is intentionally minimalist. It does not specify electrical levels, it does not mandate a particular topology, and it does not even prescribe a clock frequency. It only specifies the set of signals a master uses to ask for a transfer, the set a slave uses to acknowledge, and the rules that govern when each signal is allowed to change.
Wade Peterson chose the name during a Thanksgiving Day dinner; the forked, Y-shaped pair of separate input and output paths reminded him of a wishbone (wbspec_b4, footnote 1). The current revision, B4, is “not copyrighted, and has been placed into the public domain” and “may be used for the design and production of System-on-Chip (SoC) components without royalties or other financial obligations to OpenCores” (wbspec_b4 page 3). That clause is why every open-source hardware project that needs a bus reaches for Wishbone first.
Mental Model
flowchart LR subgraph MASTER M_OUT["ADR_O · DAT_O · WE_O · SEL_O · CYC_O · STB_O · TGx_O"] M_IN["DAT_I · ACK_I · ERR_I · RTY_I · STALL_I"] end subgraph SYSCON CLK["CLK_O"] RST["RST_O"] end subgraph SLAVE S_IN["ADR_I · DAT_I · WE_I · SEL_I · CYC_I · STB_I · TGx_I"] S_OUT["DAT_O · ACK_O · ERR_O · RTY_O · STALL_O"] end M_OUT --> S_IN S_OUT --> M_IN CLK --> MASTER CLK --> SLAVE RST --> MASTER RST --> SLAVE
A Wishbone master and a Wishbone slave wired point-to-point. What it shows: every signal is unidirectional; the master’s outputs are the slave’s inputs, the slave’s outputs are the master’s inputs, and a separate SYSCON module distributes clock and reset to both. The insight to take: because every signal is named with _O or _I rather than a tri-state direction, two interfaces can be wired with nothing but plain wires; no tri-state arbiters or external bus controllers are needed for the point-to-point case. Shared-bus and crossbar topologies are built by adding a small INTERCON module of muxes around this base interface (wbspec_b4 sections 2.2 and 8.2).
The conceptual core of Wishbone is the handshake. A master raises CYC_O to declare that a bus cycle is in progress and raises STB_O to indicate that the address, data, and control lines hold valid information for one transfer. The selected slave, in response to every STB_I, asserts exactly one of three termination signals: ACK_O for a successful transfer, ERR_O if the access caused an error (e.g. a bad address or a parity fault), or RTY_O if the slave is temporarily unable to serve and would like the master to try again. The master may then drop STB_O and either issue another transfer (still inside the same cycle) or drop CYC_O to release the bus.
Mechanical Walk-through
The signal set
The B4 specification splits signals into three groups: SYSCON signals (clock and reset), signals common to master and slave (data path, address, tags), and direction-specific control signals. The full master signal set described in section 2.2.3 is (wbspec_b4 pages 28-29):
- CYC_O: cycle output. Asserted “for the duration of all bus cycles.” When the master needs more than one back-to-back transfer (a BLOCK cycle, for instance), CYC_O stays high across all of them. In shared-bus systems, CYC_O is the request line the master raises to ask the arbiter for ownership of the bus.
- STB_O: strobe output. “Indicates a valid data transfer cycle.” Every assertion of STB_O is paired with exactly one response from the slave.
- WE_O: write enable output. Negated for reads, asserted for writes.
- ADR_O[n..0]: address output array. Width is implementation-defined; the lower bits are determined by port granularity. On a 32-bit data port with byte granularity, ADR_O[n..2] is the word address and the two least significant bits are conventionally zero.
- DAT_O[m..0]: data output array, up to 64 bits wide.
- SEL_O[k..0]: select output array. Each bit indicates that the corresponding byte lane of DAT_O (on writes) or the expected byte lane of DAT_I (on reads) is valid. On a 32-bit port with 8-bit granularity, SEL_O[3..0] selects which of the four byte lanes participate in the transfer.
- ACK_I, ERR_I, RTY_I: termination inputs from the slave.
- DAT_I[m..0]: read data from the slave.
- STALL_I: pipeline stall input (used only in pipelined mode; see below).
- LOCK_O: optional, asserted to indicate “the current bus cycle is uninterruptible” so no other master can preempt across the locked sequence (wbspec_b4 page 28).
- TGA_O, TGD_O, TGC_O: user-defined tags attached respectively to address, data, and cycle. Tags are how a designer extends Wishbone without inventing new signals; their timing is fixed by the spec but their meaning is local to a given IP core.
The slave-side signals mirror these (suffix _I becomes _O and vice versa). The naming convention “is used to clearly identify the direction of the signal as it relates to the entity to which it is attached,” so wiring a master to a slave is mechanical: connect every *_O on one side to the matching *_I on the other (wbspec_b4 section 2.2.2).
The classic handshake
In Classic mode (Chapter 3 of B4), each transfer takes a minimum of one clock for a zero-wait-state slave but typically more. The master presents the address, data, WE, and SEL on a rising clock edge with CYC_O and STB_O asserted. The slave sees this and either acknowledges combinationally on the same cycle, drops back ACK_O the next cycle, or asserts ACK_O after N clocks of wait states. The master qualifies the response only when both STB_O and ACK_I are simultaneously high; this AND of the request strobe with the acknowledge is the canonical Wishbone handshake. After the handshake, the master can either keep CYC_O up and start the next transfer or drop CYC_O to end the cycle.
This protocol is, as the ZipCPU writeup observes, “a minimum of three clocks per transaction” in its pure classic form: one clock to present, one for the slave to acknowledge, one to settle (ZipCPU on formal Wishbone, 2017). For a simple peripheral like a UART that is perfectly fine, but for a CPU fetching instructions from memory the round-trip latency cripples throughput.
Pipelined mode and the STALL signal
The Wishbone B3 revision added a pipelined mode (“registered feedback”) that Chapter 4 of B4 keeps as a first-class option. The idea is to decouple request issuance from response. The master issues a new STB_O every clock without waiting for the previous ACK_I. The slave drains the requests into a small internal FIFO and returns one ACK_I per cycle once the data is ready. To stall the request stream when its FIFO fills up, the slave asserts STALL_O; the master must hold the current address and data and not advance to the next request until STALL_I drops (wbspec_b4 sections 2.2.3 and 4). With pipelined mode, “N transactions in N+1 clocks” become possible, restoring near-single-cycle throughput while preserving the same handshake semantics (ZipCPU on Wishbone pipelining, 2018).
Byte lanes and endianness
Wishbone supports data ports of 8, 16, 32, or 64 bits and grants the system designer freedom over which byte lanes are valid in a partial-word access. The SEL_O array carries this information directly. A 32-bit master writing only byte 0 on a 32-bit port presents the byte as DAT_O[7..0] with SEL_O = 4’b0001; the slave inspects SEL_O to decide which byte lane to update. The spec defines this independently of endianness: the master and slave are free to be big-endian, little-endian, or mixed, as long as they document their conventions in the mandatory Wishbone Datasheet (wbspec_b4 sections 3.5.4 and 8.5).
The “Wishbone Datasheet” is itself part of the standard. Section 2.1.1 requires that every Wishbone-compliant IP core ship with a datasheet listing the supported cycle types, port size, granularity, endianness, and any tags. This is the documentation contract: integrate by reading two short tables, not by reverse-engineering RTL.
Termination signals: ACK, ERR, RTY
The three termination signals exist for three different reasons. ACK_I is the normal-success case. ERR_I is a hard error; the IP core supplier defines what counts as one (a typical use is a slave decoding an address it does not implement, or detecting a parity fault). RTY_I says “I am temporarily busy, please try again,” which is useful for slaves that share resources, e.g. a memory controller whose port is currently dedicated to refresh (wbspec_b4 page 29). Many small slaves wire ERR_O and RTY_O to ground, and the masters that need only success/no-success ignore them; the spec permits this.
Reset, BLOCK, and RMW
A SYSCON block in the system asserts RST_O for at least one clock to initialize all state machines. All masters and slaves must respond to RST_I; section 3.1.1 requires that no other bus signals be processed during reset. BLOCK cycles (section 3.3) are simply long classic cycles: CYC_O stays asserted while many STB_O / ACK_I pairs happen back-to-back, avoiding the need to re-arbitrate for the bus between transfers. RMW (read-modify-write) cycles (section 3.4) hold CYC_O across one read and one write, which is the Wishbone way to implement an atomic test-and-set without the AXI-style separate ARID/AWID channels.
Configuration / Code
Here is a minimal Verilog skeleton for a Wishbone B4 classic slave that implements a 32-bit memory-mapped register at a base address. The point of the listing is to show how few lines are needed to be compliant.
module wb_reg #(
parameter ADDR_W = 4
)(
input clk_i,
input rst_i,
// Wishbone slave port
input cyc_i,
input stb_i,
input we_i,
input [ADDR_W-1:0] adr_i,
input [31:0] dat_i,
input [3:0] sel_i,
output reg [31:0] dat_o,
output reg ack_o
);
reg [31:0] reg_q;
// Synchronous slave: ack the very next clock after a strobe
always @(posedge clk_i) begin
if (rst_i) begin
reg_q <= 32'h0;
ack_o <= 1'b0;
dat_o <= 32'h0;
end else begin
// Default: deassert ack
ack_o <= 1'b0;
// Strobe AND cycle = a real request to this slave
if (cyc_i && stb_i && !ack_o) begin
if (we_i) begin
// Byte-lane writes via SEL
if (sel_i[0]) reg_q[ 7: 0] <= dat_i[ 7: 0];
if (sel_i[1]) reg_q[15: 8] <= dat_i[15: 8];
if (sel_i[2]) reg_q[23:16] <= dat_i[23:16];
if (sel_i[3]) reg_q[31:24] <= dat_i[31:24];
end else begin
dat_o <= reg_q;
end
ack_o <= 1'b1;
end
end
end
endmoduleLine-by-line: the clk_i and rst_i come from SYSCON. The slave inspects cyc_i && stb_i together because the master must hold both high for a real request; this is the canonical guard against spurious activity on the strobe alone. The !ack_o term prevents a second acknowledge on the same transfer; without it the slave would happily ack every cycle that the master left STB_O high. SEL_I per-lane gating is mandatory for byte-granular partial writes; ignoring SEL would silently overwrite the entire word on a byte access. ack_o falls back to zero on the cycle after the response, which is the minimum classic-cycle response time of one clock.
A pipelined slave version would add a stall_o output. Until its internal queue fills, stall_o stays low and the slave accepts a new request every clock; while STALL_O is high, the master must keep its request lines unchanged (wbspec_b4 sections 2.2.4 and 4).
Failure Modes and Common Misunderstandings
The most common Wishbone mistake is misreading the handshake: asserting ACK_O whenever STB_I is high, without checking that CYC_I is also high. The spec is explicit that STB_O is qualified by CYC_O; a slave that drops the CYC qualification will respond to floating wires on a deselected bus. The second classic mistake is to forget SEL_I on writes and stomp the whole word on a byte access; many software bugs that look like wild-pointer corruption are actually missing SEL handling in a Wishbone peripheral.
A more subtle pitfall is mixing classic and pipelined participants. A pipelined master connected to a classic slave needs a small adapter, because the slave’s lack of STALL_O means it cannot tell the master to hold; the master must throttle itself. The B4 spec dedicates Chapter 5 to these interface cases and recommends that “if a pipelined master needs to connect to a standard slave, the master should hold STB high until ACK is received” and treat the slave as if it always stalled to one outstanding request (wbspec_b4 sections 5.1 and 5.2).
A third pitfall: assuming Wishbone arbitrates the bus for you. It does not. In a shared-bus topology, an external arbiter must observe each master’s CYC_O and grant the bus by routing the winning master’s outputs onto the shared lines. The spec deliberately leaves the arbitration policy (round-robin, priority, weighted) to the system integrator.
Alternatives and When to Choose Them
The dominant commercial alternative is ARM’s AMBA AXI family. AXI4, released in 2010, defines five independent channels (read address, read data, write address, write data, write response), each with its own VALID/READY handshake (AMBA AXI, Wikipedia). The split-channel model lets a single master issue multiple outstanding read transactions and tag them with IDs, so AXI naturally supports out-of-order completion; that is essential for cache-coherent multi-core SoCs but enormously heavier than Wishbone for a single-master design. AXI4-Lite is a deliberately stripped-down variant suitable for simple peripherals, and it is the closest AMBA equivalent to a classic Wishbone interface. Xilinx and the rest of the commercial FPGA world have largely adopted AXI as their default; Altera/Intel has the Avalon bus, which sits in roughly the same simplicity tier as Wishbone.
The trade-off is straightforward: pick AXI when you need cache coherency, multiple outstanding transactions, or seamless integration with vendor IP catalogs. Pick Wishbone when you are doing a small open-source SoC, want a single-clock single-master design that you can fully understand from a 128-page spec, and want to drop in cores from OpenCores without paying anyone. The Wishbone authors deliberately note that the bus is “synchronous design assures portability, simplicity and ease of use” and is “independent of hardware technology (FPGA, ASIC, etc.)” (wbspec_b4 page 9).
There are smaller niche options too. The TileLink protocol from SiFive, originally inside the Chisel/Rocket Chip ecosystem, sits between Wishbone and AXI in complexity and adds cache coherence primitives. The Pulp project uses a custom protocol called OBI (Open Bus Interface) for small RISC-V cores. For a hobby project, none of these match Wishbone’s combination of simplicity, public-domain status, and a thirty-year library of compatible cores at OpenCores.
Production Notes
Wishbone is the bus underneath the original OpenRISC OR1200 processor and its family of derivatives (mor1kx, marocchino), all of which “Run upstream Linux” and use Wishbone for the bus connecting CPU to memory and peripherals (OpenRISC, Wikipedia). OR1200’s bus signals are an almost literal instantiation of the B3 master interface. The OpenCores uart16550 IP core, still the most widely reused open-source UART, is “WISHBONE SoC bus Rev. B compliant,” and is reused in dozens of FPGA SoC projects (including LiteX-generated systems and Linux-on-RISC-V demos). Dan Gisselquist’s ZipCPU goes further and uses Wishbone pipelined mode exclusively, building its formal verification framework around the bus contract; the wbuart32 UART and its accompanying tutorial are widely cited as the canonical “how to build a small Wishbone peripheral” reference (ZipCPU 2017).
In our own definitely-not-esp32 SoC the bus is Wishbone classic for v1.0: one master (the RV32IMC core), several slaves (boot ROM, RAM, UART, CLINT, PLIC), with an INTERCON module that does naturally aligned full address decoding and routes ACK/DAT back to the master via a one-hot mux. Pipelined mode is a stretch goal for the cache phase: when a hit can return data in one cycle, the round-trip latency of classic Wishbone bottlenecks the IPC, and STALL_O lets the cache miss handler stall the rest of the pipeline without losing the request.