Skip to content
VLSI Mentor

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.

signalrequired by RULE 3.40in this masterwhy
[CLK_I]yesyes
[RST_I]yesyes
[CYC_O]yesyes
[STB_O]yesyes
[ACK_I]yesyes
[ADR_O]noyeschosen: this master addresses memory
[DAT_O()] / [DAT_I()]noyeschosen: it moves data
[WE_O]noyeschosen: it does both directions
[SEL_O()]noyeschosen: byte lanes, per Chapter 19.4
[ERR_I] / [RTY_I]noyeschosen, 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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 bycould it be removed?
IDLE00RULE 3.30 (slaves must be silent)no — something must represent "no cycle"
REQ11RULE 3.25 + 3.60no — this is the cycle
WAIT11RULE 3.35on the wires it is identical to REQ
DONE00nothingyes — 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.

The four-state Wishbone master FSM. IDLE drives CYC_O and STB_O low and waits for a client request; on a valid request it captures the address, write-enable, write data and byte selects into registers and moves to REQ. REQ drives CYC_O and STB_O high with the captured context held stable; if a termination arrives on this same clock the transfer completes immediately, otherwise the machine moves to WAIT. WAIT drives exactly the same wires as REQ and stays there for as many clocks as the slave needs, holding the entire request context unchanged as RULE 3.60 requires. When any of ACK_I, ERR_I or RTY_I arrives the machine captures the response and moves to DONE. DONE drives CYC_O and STB_O low for one clock while it presents the response to the client, then returns to IDLE.IDLEREQWAITDONEreq_valid — capture contextreq_valid — capture contextreq_valid —capture…no termination yetno termination yetACK/ERR/RTY this clockACK/ERR/RTYthis clockstill unansweredstill unansweredACK/ERR/RTYACK/ERR/RTYresponse deliveredresponse delivered

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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─── 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
endmodule

With 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        // ── 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
        end

And REQ handles the case that catches designers out — the slave that answers on the very first clock:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        // ── 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === 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 1

Read 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.

A zero-wait-state Wishbone write from the master FSM. On clock 0 the machine is in REQ with CYC_O and STB_O both asserted and the captured address and write data driven; the slave answers combinationally on the same clock so ACK_I is already high. On clock 1 the machine is in DONE with CYC_O and STB_O negated and the response pulse presented to the client. On clocks 2 and 3 the machine is IDLE with the bus quiet.REQ: CYC and STB rise togetherREQ: CYC and STB risetogetherDONE: the design's clock, not B3'sDONE: the design's clock,not B3'sCLK_IstateREQREQDONEDONEIDLEIDLEIDLEIDLECYC_OSTB_OADR_O020020XXXXXXACK_Irspt0t1t2t3t4t5t6t7

Now the same master against a slave that needs three wait states:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === 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.00

Four 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === 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_PATH is not free. req_ready_o now depends combinationally on ack_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:

checkrulewhat it catches
STBnoCYCRULE 3.25[STB_O] asserted outside a cycle
TERMnoREQRULE 3.35a termination answering nothing
MULTIRULE 3.45two terminations at once
CTXmovedRULE 3.60the request changed mid-phase
TERMheldRULE 3.50a termination outliving its strobe

Against a correct master and a correct slave:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    And the master/slave pair, monitored throughout:
      violations 0 over 3 phases

Clean — 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:

itemvalueauthority
port size32-bitRULE 2.15 requires it stated
granularity8-bit, via [SEL_O()]RULE 2.15
cycle typesSINGLE READ, SINGLE WRITERULE 2.15
response to [ERR_I]captured and passed to the client; the master does not retryPERMISSION 3.20 — a local policy
response to [RTY_I]captured and passed to the client; the master does not retryPERMISSION 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

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.