Wishbone · Module 23
Master Design
Wishbone B3 contains no state machine. Four rules force one anyway — and a clock audit that sums to the total shows FAST_PATH cutting 31 clocks to 17 without touching the bus phase at all.
Every module before this one measured Wishbone. This one builds it, and the first thing to say is uncomfortable: the specification you are implementing does not contain the thing you are about to write.
There is no master state machine in Wishbone B3. There is no diagram of one, no list of states, no naming convention for them. Search the document and you will not find the word.
What B3 has instead is a set of rules about signals. This chapter takes four of them and shows that they leave you almost no freedom — that a master FSM is not a design choice so much as the shape those four rules press into RTL. Then it builds the machine, runs it, and accounts for every single clock it spends, because a design you cannot audit is a design you are trusting rather than checking.
1. The Port List Is The Only Part B3 Hands You
RULE 3.40 is unusually direct:
"As a minimum, the MASTER interface MUST include the following signals:
[ACK_I],[CLK_I],[CYC_O],[RST_I], and[STB_O]."
Five signals. Not [ADR_O]. Not [DAT_O()]. Not [WE_O]. Not [ERR_I] or [RTY_I]. A conformant Wishbone master can legally have no address bus at all — a single-register peripheral controller needs none, and B3 is careful not to force one on it.
This matters more than it looks. It tells you which parts of your port list you are choosing, and RULE 2.00 then obliges you to write those choices down:
"Each WISHBONE compatible IP core MUST include a WISHBONE DATASHEET as part of the IP core documentation."
So the header comment on the module below is not decoration. It is the only artefact B3 actually requires you to produce.
| signal | required by RULE 3.40 | in this master | why |
|---|---|---|---|
[CLK_I] | yes | yes | — |
[RST_I] | yes | yes | — |
[CYC_O] | yes | yes | — |
[STB_O] | yes | yes | — |
[ACK_I] | yes | yes | — |
[ADR_O] | no | yes | chosen: this master addresses memory |
[DAT_O()] / [DAT_I()] | no | yes | chosen: it moves data |
[WE_O] | no | yes | chosen: it does both directions |
[SEL_O()] | no | yes | chosen: byte lanes, per Chapter 19.4 |
[ERR_I] / [RTY_I] | no | yes | chosen, under PERMISSION 3.20 |
2. Four Rules, And The Machine They Force
Here is the derivation. Read each rule and ask what it makes impossible.
RULE 3.25 — "MASTER interfaces MUST assert [CYC_O] for the duration of SINGLE READ / WRITE, BLOCK and RMW cycles. [CYC_O] MUST be asserted no later than the rising [CLK_I] edge that qualifies the assertion of [STB_O]."
This forbids a master that raises [STB_O] first and [CYC_O] afterwards. It does not forbid raising them together, and "no later than" is satisfied most cheaply by driving both from the same condition. That is one state, or a set of states, in which both are high.
RULE 3.60 — "MASTER interfaces MUST qualify the following signals with [STB_O]: [ADR_O], [DAT_O()], [SEL_O()], [WE_O], and [TAGN_O]."
This is the load-bearing one. Qualified by [STB_O] means those signals are only meaningful while [STB_O] is asserted — and therefore must not change while it is asserted and unanswered, because the slave would then be answering a question that has since been replaced. This forces registers. A master that drives [ADR_O] straight from whatever its client is asking for right now has no way to stop the client changing its mind mid-phase.
RULE 3.35 — "The cycle termination signals [ACK_O], [ERR_O], and [RTY_O] must be generated in response to the logical AND of [CYC_I] and [STB_I]."
There is exactly one outstanding question at a time. No tags, no IDs, no reordering. This is why the machine has a single WAIT state and not a queue — and it is the deepest difference between Wishbone Classic and AXI, which Chapter 20.2 measured at length.
RULE 3.45 — "the SLAVE MUST NOT assert more than one of the following signals at any time: [ACK_O], [ERR_O] or [RTY_O]."
One-hot terminations. This makes a plain OR safe where a priority encoder would otherwise be needed:
logic term;
assign term = ack_i || err_i || rty_i;That single line is a rule quoted as RTL. If RULE 3.45 did not exist, that OR would be a bug.
The states that fall out
| state | [CYC_O] | [STB_O] | forced by | could it be removed? |
|---|---|---|---|---|
| IDLE | 0 | 0 | RULE 3.30 (slaves must be silent) | no — something must represent "no cycle" |
| REQ | 1 | 1 | RULE 3.25 + 3.60 | no — this is the cycle |
| WAIT | 1 | 1 | RULE 3.35 | on the wires it is identical to REQ |
| DONE | 0 | 0 | nothing | yes — and we will |
Two of these four states are not in the specification at all. WAIT drives exactly the same wires as REQ; the only difference is whether an answer has arrived. DONE drives exactly the same wires as IDLE. Section 6 removes DONE with a parameter and measures what that costs.
3. The Wrong Version, First
Here is the master almost everybody writes the first time. It is shorter, it looks cleaner, and it is broken.
// ─── DO NOT SHIP THIS ───────────────────────────────────────────────
module wb_master_bad (
input logic clk_i, rst_i,
input logic req_valid_i, req_we_i,
input logic [11:0] req_adr_i,
output logic cyc_o, stb_o, we_o,
output logic [11:0] adr_o,
input logic ack_i
);
logic busy_q;
// "why register it? the client already has it"
assign adr_o = req_adr_i; // <-- RULE 3.60 VIOLATION
assign we_o = req_we_i; // <-- RULE 3.60 VIOLATION
assign cyc_o = busy_q;
assign stb_o = busy_q;
always_ff @(posedge clk_i) begin
if (rst_i) busy_q <= 1'b0;
else if (!busy_q) busy_q <= req_valid_i;
else if (ack_i) busy_q <= 1'b0;
end
endmoduleWith a zero-wait-state slave this design is indistinguishable from a correct one. The phase lasts one clock, the client has no opportunity to change req_adr_i, and every test passes.
Add one wait state and it corrupts silently. The client — a CPU pipeline, a DMA engine, an arbiter upstream — advances its own address on the next clock because nothing told it not to. [ADR_O] moves while [STB_O] is still asserted. The slave, which sampled the first address, now completes a transfer against the second one. No signal reports this. Both sides believe they succeeded.
This is precisely what RULE 3.60 exists to prevent, and it is why the real master captures its context into registers on entry to REQ:
// RULE 3.60: the context is held from registers captured on entry to
// REQ. Driving these from req_* directly would let a client change the
// address mid-phase, which Chapter 23.1's negative control does.
assign we_o = we_q;
assign adr_o = adr_q;
assign dat_o = dat_q;
assign sel_o = sel_q;The conformance monitor in Section 7 has a check for exactly this, CTXmoved, and Section 8 is honest about what it found.
4. The Master, Built
[CYC_O] and [STB_O] come straight from the state, which satisfies RULE 3.25 by making them rise on the same edge:
// RULE 3.25: CYC_O for the duration, rising no later than STB_O. Here
// they rise together, which satisfies "no later than".
assign cyc_o = (st_q == S_REQ) || (st_q == S_WAIT);
assign stb_o = (st_q == S_REQ) || (st_q == S_WAIT);The IDLE arm captures everything RULE 3.60 qualifies, in one place, on one edge:
// ── IDLE: nothing on the bus. CYC_O and STB_O are low, so
// RULE 3.30 makes every slave silent. ──
S_IDLE: begin
if (req_valid_i) begin
we_q <= req_we_i; // captured, per RULE 3.60
adr_q <= req_adr_i;
dat_q <= req_dat_i;
sel_q <= req_sel_i;
niss_q <= niss_q + 16'd1;
st_q <= S_REQ;
end
endAnd REQ handles the case that catches designers out — the slave that answers on the very first clock:
// ── REQ: the first clock the request is presented. A slave using
// PERMISSION 3.10 answers on this very clock, which is why REQ
// and WAIT are separate states doing the same thing on the
// wires - the difference is only whether an answer arrived. ──
S_REQ: begin
if (term) begin
rdat_q <= we_q ? '0 : dat_i; // a write returns no read data
rerr_q <= err_i;
rrty_q <= rty_i;
rsp_q <= 1'b1;
ndone_q <= ndone_q + 16'd1;PERMISSION 3.10 is what makes that first-clock answer legal:
"If the SLAVE guarantees it can keep pace with all MASTER interfaces and if the
[ERR_I]and[RTY_I]signals are not used, then the SLAVE's[ACK_O]signal MAY be tied to the logical AND of the SLAVE's[STB_I]and[CYC_I]inputs."
A master that only sampled terminations in WAIT would deadlock against such a slave — it would leave REQ expecting an answer later, and the answer had already been and gone.
5. Watching It Run
Zero wait states, one write. The columns are taken from the simulation, and the right-hand column names what forces each clock:
=== SIM A - the master's FSM, state by state ===
zero wait states. The slave answers combinationally,
which PERMISSION 3.10 permits, so WAIT is never entered.
clk state CYC STB ACK rsp what forces it
0 REQ 1 1 1 0 RULE 3.25: CYC_O rises with STB_O
1 DONE 0 0 0 1 no rule - this clock is the design's
2 IDLE 0 0 0 0 -
issued 1 done 1 WAIT clocks 0 DONE clocks 1Read the second row carefully. "no rule — this clock is the design's." The DONE clock is not required by anything in B3. It exists because this master hands its response to a client through a registered pulse, and that pulse needs a clock to live in. It is the design's cost, not the protocol's, and Section 6 charges it properly.
Now the same master against a slave that needs three wait states:
=== SIM B - the slave, with and without wait states ===
rig waits acks WAIT clocks clocks/transfer
W=0 0 3 0 1.00
W=3 3 2 6 4.00Four clocks per transfer, of which three are WAIT. The machine sat in WAIT holding the entire request still — and RULE 3.60 is the only reason that is safe. The slave sampled an address three clocks ago and is still answering that address, because nothing was permitted to move it.
6. Removing The State That Is Not In The Specification
DONE is the design's clock. So take it away: FAST_PATH makes the machine accept the next request directly out of WAIT, combinationally, the moment a termination arrives.
assign req_ready_o = (st_q == S_IDLE)
|| (FAST_PATH && (st_q == S_WAIT) && term);Eight writes, one wait state, two rigs identical but for that one parameter. The testbench counts which state the machine was in on every single clock, and asserts that the four counts sum to the total — a claim of a saving means nothing if the clocks merely moved somewhere nobody was counting.
=== SIM J - the clock audit ===
rig total IDLE REQ WAIT DONE xfers viol
FAST_PATH=0 31 8 8 8 7 8 0
FAST_PATH=1 17 1 8 8 0 8 0
-> Both censuses sum exactly. 31 = 8+8+8+7
and 17 = 1+8+8+0. NO CLOCK IS UNACCOUNTED
FOR - which is the only way to claim a saving is
real rather than moved somewhere unmeasured.REQ and WAIT are identical in both rigs: 8 and 8. FAST_PATH does not touch the bus phase at all — the slave sees precisely the same waveform. What vanishes is 7 DONE clocks and 7 of the 8 IDLE clocks, because the machine no longer returns to idle between transfers. 31 clocks becomes 17 for the same 8 transfers, and zero protocol violations either way.
That is a 45% reduction, and it would be dishonest to stop there.
FAST_PATHis not free.req_ready_onow depends combinationally onack_i, so the slave's termination reaches the client's handshake logic in the same clock.
B3 §4.1 names that path, in a passage every one-clock optimisation in this curriculum has had to quote:
"...this results in an asynchronous loop from the MASTER, through the INTERCONN to the SLAVE, and then from the SLAVE through the INTERCONN back to the MASTER... In large System-on-Chip devices this routing delay between MASTER and SLAVE is the dominant timing factor."
The clock audit measures clocks. It cannot measure the period those clocks take. A design that halves the clock count and fails timing has not improved anything, and only synthesis can tell you which happened.
7. Proving The Master Is Correct
wb_conformance watches five rules on every rig in this module:
| check | rule | what it catches |
|---|---|---|
STBnoCYC | RULE 3.25 | [STB_O] asserted outside a cycle |
TERMnoREQ | RULE 3.35 | a termination answering nothing |
MULTI | RULE 3.45 | two terminations at once |
CTXmoved | RULE 3.60 | the request changed mid-phase |
TERMheld | RULE 3.50 | a termination outliving its strobe |
Against a correct master and a correct slave:
And the master/slave pair, monitored throughout:
violations 0 over 3 phasesClean — and a clean result from a checker that has never been seen to fail is worth nothing. So the same monitor was pointed at deliberately broken slaves. That result is Section 8 of Chapter 23.2, and the short version is that two of the five checks could not be made to fire at all until the testbench was allowed to break the rules itself, because a conformant master never produces the conditions they look for.
8. The Datasheet RULE 2.00 Demands
RULE 2.15 requires the datasheet to state how the master reacts to [ERR_I] and [RTY_I], its port size, its granularity, and the cycle types it supports. For this master:
| item | value | authority |
|---|---|---|
| port size | 32-bit | RULE 2.15 requires it stated |
| granularity | 8-bit, via [SEL_O()] | RULE 2.15 |
| cycle types | SINGLE READ, SINGLE WRITE | RULE 2.15 |
response to [ERR_I] | captured and passed to the client; the master does not retry | PERMISSION 3.20 — a local policy |
response to [RTY_I] | captured and passed to the client; the master does not retry | PERMISSION 3.20 — a local policy |
[TAGN_O] | none | — |
PERMISSION 3.20 is explicit that this is yours to decide:
"MASTER and SLAVE interfaces MAY be designed to support the
[ERR_I]and[ERR_O]signals... This specification does not dictate what the MASTER does in response to[ERR_I]."
A master that retries on [RTY_I] is equally conformant. A master that does not say which it does is not, because RULE 2.00 makes the datasheet part of the deliverable.
9. What This Chapter Did Not Build
Honest scope, so the next chapters are not oversold:
- No burst or
[CTI_O()]support. This is a Classic master. PERMISSION 4.05 makes registered feedback optional and Chapter 22.5 measured it separately. - No timeout. The master waits forever for a termination, because B3 specifies no timeout of any kind. Chapter 12.6 owns that subject, and Section 8 of Chapter 23.4 shows a slave exploiting the absence.
- No pipelining. RULE 3.35 permits exactly one outstanding phase; Chapter 20.2 compared that against AXI's outstanding-transaction model.
- No arbitration. A master drives its port and nothing else; Chapter 23.6 is where ownership appears.
Next: Chapter 23.2 — Slave Design builds the other side of these same four rules, and finds that the slave's obligations are stricter: a master may be slow, but a slave must both assert and negate its termination in response to the strobe.
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
Need for Standardized Interconnects
An address map answers where a register lives. It says nothing about which wires carry the request, when they are valid, how the target reports completion, or what happens on an error. Three peripherals with three private interfaces produce three adapters, three verification efforts and three ways to be wrong — which is the argument for standardising the interface rather than the map.
- Related topic
FPGA Design Challenges
Six peripherals and two masters on one FPGA is where on-chip integration stops being theory. The decode that must be exhaustive and one-hot, the read multiplexer that grows with every target and carries the critical path, the latency and reset conventions that refuse to agree, and the point at which the fabric rather than the peripherals starts failing timing.
- Related topic
Masters and Slaves
Master and slave are transaction roles, not a statement about importance or hierarchy. The role determines exactly which information each side owns: the initiator supplies address, direction and write data; the target supplies read data, completion and any error. Getting that ownership wrong is the source of an entire family of integration bugs.
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.
