Skip to content
VLSI Mentor

Wishbone · Module 3

The Master Interface

A Wishbone master owns the address, the direction, the write data, the byte selects and both qualifiers, and must hold them until a termination arrives. What it is deliberately not allowed to know matters as much as what it drives — a master that knows the address map or a slave's latency has been coupled to one system.

Chapter 3.2 gave the model and a master that performs a single transfer. This chapter is about responsibility, with a master that does real work.

The distinction matters because a signal list is not a job description. Module 4 details each signal; what a master is for is decided here.

What must a Wishbone master know, decide, and hold responsibility for — and what is it deliberately not allowed to know?

1. What a Master Owns

DrivesThe decision it is making
CYC_OI am taking the interconnection and holding it until this cycle ends
STB_OThis cycle carries a transfer — the request signals are meaningful now
ADR_OThis is the location
WE_OThis is the direction
SEL_OThese byte lanes participate
DAT_OThis is the value being delivered, on a write
ObservesWhat it must do about it
ACK_ISucceeded — capture DAT_I on a read, then close or continue
ERR_IFailed — stop, and report upward
RTY_INot now — the transfer did not happen and may be re-issued
DAT_IRead data, meaningful in the termination cycle

RULE 3.60 is the obligation underneath the first table: the master must qualify ADR_O, DAT_O, SEL_O, WE_O and the tag outputs with STB_O. Those signals are meaningful only while the strobe is asserted — which makes the master responsible for their stability across the whole time a transfer is outstanding.

A Wishbone master has three groups of responsibility. It decides and drives the request: the cycle and strobe qualifiers, the address, the direction, the byte selects and the write data. It must hold all of that stable for as long as the transfer is outstanding, because the specification makes those signals meaningful only while the strobe is asserted. And it observes the slave's termination, acting differently on each of three possibilities: acknowledge means success and read data may be captured, error means the transfer failed, retry means it did not happen and may be re-issued. Separately, four things are deliberately outside the master's knowledge: the system address map, the slave's latency, the slave's internal implementation, and how the interconnection is built.Decidesaddr, dir, lanes, dataHolds it stableuntil terminationObservesterminationthree outcomes, threeactionsWishbone masterportthe specified interfaceNot the address mapit is handed an addressNot slave latencyit waits, however longNot slave internalsregisters are thecontractNot the topologyp2p or crossbar,identical12
Figure 1 — what a master decides, what it must hold, and the four things it is not allowed to know.

2. The Four Things a Master Must Not Know

The right-hand side of Figure 1 is what gets designed wrong, because each is convenient to know.

Not the address map. A master receives an address from whatever drives it — a CPU's load/store unit, a descriptor, a configuration register. It does not know which slave that address reaches. Chapter 2.2 put decode in the INTERCON precisely so neither endpoint carries the map.

Not a slave's latency. Chapter 1.5 priced this: a master that knows latencies acquires one special case per slave and cannot absorb a register slice added for timing closure. In Wishbone Classic the master waits for a termination however long it takes, and there is no alternative — Classic has no separate stall signal.

Not a slave's internals. The software-visible register contract is the whole interface.

Not the topology. Chapter 3.1 §5: the master interface is identical across point-to-point, shared bus, crossbar and data flow. Only the INTERCON changes.

Engineering interpretation, not a rule: the specification does not forbid a master from knowing these things. It defines an interface that makes them unnecessary. The discipline is the designer's, and every violation is a coupling discovered the day the system changes.

3. RTL — A Copy Engine Master

A master that performs one transfer is not worth studying. This one is programmed with a source, a destination and a length, and moves words between them.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_copy_master — a Wishbone Classic MASTER that copies a block of words
// from a source address to a destination address.
//
// PURPOSE. Exhibit every master responsibility from Section 1 on a block of
// work large enough that the state is real: request ownership, stability
// while outstanding, termination handling for all three outcomes, read-data
// capture, and a decision about what happens next.
//
// SCOPE. One transfer per bus cycle — CYC_O and STB_O rise and fall
// together. A BLOCK cycle would hold CYC_O across several transfers, which
// is Module 8's subject. ERR_I aborts and reports; RTY_I re-issues the same
// transfer. The detailed semantics of both are Modules 10 and 11.
//
// Reset is SYNCHRONOUS, ACTIVE HIGH — RULE 2.30 and RULE 3.00. RULE 3.20
// requires STB_O and CYC_O negated at the rising edge following assertion
// of RST_I, which forcing S_IDLE achieves.
// ─────────────────────────────────────────────────────────────────────────
module wb_copy_master #(
  parameter int unsigned AW = 32,
  parameter int unsigned DW = 32,
  parameter int unsigned CW = 16          // transfer-count width
) (
  input  logic            clk_i,
  input  logic            rst_i,

  // ── Local control side ───────────────────────────────────────────────
  input  logic            start_i,
  input  logic [AW-1:0]   src_i,
  input  logic [AW-1:0]   dst_i,
  input  logic [CW-1:0]   count_i,        // number of words
  output logic            busy_o,
  output logic            done_o,         // one-cycle pulse on completion
  output logic            error_o,        // sticky until the next start

  // ── Wishbone MASTER interface ────────────────────────────────────────
  output logic            cyc_o,
  output logic            stb_o,
  output logic            we_o,
  output logic [AW-1:0]   adr_o,
  output logic [DW/8-1:0] sel_o,
  output logic [DW-1:0]   dat_o,
  input  logic [DW-1:0]   dat_i,
  input  logic            ack_i,
  input  logic            err_i,
  input  logic            rty_i
);
  typedef enum logic [1:0] {
    S_IDLE,     // no cycle open
    S_READ,     // cycle open, read transfer outstanding
    S_WRITE,    // cycle open, write transfer outstanding
    S_DONE      // reporting completion for one cycle
  } state_e;

  state_e        state_q;
  logic [AW-1:0] src_q, dst_q;
  logic [CW-1:0] remaining_q;
  logic [DW-1:0] hold_q;                 // the word in flight
  logic          error_q;

  // ── Terminations decoded once. RULE 3.45 makes them mutually exclusive
  //    at the slave, so exactly one can be true in a cycle. Decoding them
  //    in one place keeps every use consistent — and keeps the three
  //    OUTCOMES distinct, which is the bug Section 5 is about.
  logic term_ok, term_err, term_rty;
  assign term_ok  = ack_i;
  assign term_err = err_i;
  assign term_rty = rty_i;

  // ── Combinational outputs. Both qualifiers come from the state; every
  //    other request signal comes from a REGISTER — never from the local
  //    control inputs. That makes RULE 3.60's stability obligation
  //    structural rather than a promise the client must keep.
  assign cyc_o = (state_q == S_READ) || (state_q == S_WRITE);
  assign stb_o = cyc_o;                  // one transfer per cycle, see SCOPE
  assign we_o  = (state_q == S_WRITE);
  assign adr_o = (state_q == S_WRITE) ? dst_q : src_q;
  assign dat_o = hold_q;
  assign sel_o = '1;                     // whole-word transfers only

  assign busy_o  = (state_q != S_IDLE);
  assign error_o = error_q;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      state_q     <= S_IDLE;             // negates cyc_o and stb_o: RULE 3.20
      src_q       <= '0;
      dst_q       <= '0;
      remaining_q <= '0;
      hold_q      <= '0;
      error_q     <= 1'b0;
      done_o      <= 1'b0;
    end else begin
      done_o <= 1'b0;

      unique case (state_q)
        S_IDLE: begin
          if (start_i && (count_i != '0)) begin
            src_q       <= src_i;
            dst_q       <= dst_i;
            remaining_q <= count_i;
            error_q     <= 1'b0;
            state_q     <= S_READ;
          end
        end

        S_READ: begin
          if (term_ok) begin
            // Read data is meaningful in the TERMINATION cycle. Capturing
            // it here and nowhere else is Chapter 2.3's validity-window
            // rule applied to Wishbone.
            hold_q  <= dat_i;
            state_q <= S_WRITE;
          end else if (term_err) begin
            error_q <= 1'b1;
            state_q <= S_DONE;
          end
          // term_rty: stay in S_READ. The cycle stays open and the same
          // transfer is presented again. Whether an unbounded retry loop
          // is acceptable is Module 11's question.
        end

        S_WRITE: begin
          if (term_ok) begin
            // Pointers advance only on a SUCCESSFUL write — a word is not
            // copied until it has been written. See the §8 exercise.
            src_q       <= src_q + AW'(DW/8);
            dst_q       <= dst_q + AW'(DW/8);
            remaining_q <= remaining_q - CW'(1);
            // Written as if/else rather than a ternary: a conditional
            // expression whose branches are enum literals does not adopt
            // the enum type automatically, and some tools reject the
            // assignment without an explicit cast.
            if (remaining_q == CW'(1)) state_q <= S_DONE;
            else                       state_q <= S_READ;
          end else if (term_err) begin
            error_q <= 1'b1;
            state_q <= S_DONE;
          end
        end

        S_DONE: begin
          done_o  <= 1'b1;
          state_q <= S_IDLE;
        end
      endcase
    end
  end
endmodule

Reading this module

Purpose. Move count_i words from src_i to dst_i, one read and one write per word, reporting completion and any failure to its local client.

Interface contract. start_i is honoured only while busy_o is low. On the Wishbone side, a cycle is open exactly while the state is S_READ or S_WRITE.

Ownership, visible in the code. Every Wishbone output is either a function of the state or a register output. Not one is driven from src_i, dst_i or count_i. The client may change its inputs the cycle after start_i; the bus does not move. That is Chapter 2.5's structural-stability argument, and it satisfies RULE 3.60 by construction rather than by discipline.

Combinational decisions. Six output assignments and a three-way termination decode. The interesting one is adr_o: a single multiplexer selects source or destination by state, so one address port serves both halves of the copy.

Sequential behaviour. Six registers. src_q/dst_q advance by the bus width in bytes on each successful write; remaining_q counts down; hold_q carries the word between read and write and is the only place a copied value exists; error_q is sticky until the next start_i.

Cycle by cycle, copying two words against an immediate slave:

CycleStatecyc_o/stb_owe_oadr_oTerminationEffect
0IDLE0start_i; src/dst/count latched
1READ10srcACK_Ihold_q ← dat_i
2WRITE11dstACK_Ipointers advance, remaining_q → 1
3READ10src+4ACK_Ihold_q ← dat_i
4WRITE11dst+4ACK_Iremaining_q → 0, go to DONE
5DONE0done_o pulses

Assumptions and simplifications. One transfer per cycle; whole-word transfers only, so sel_o is all ones; no block cycles; RTY_I re-issues indefinitely; no timeout; word-aligned addresses assumed rather than checked; source and destination assumed not to overlap.

How it could fail.

  • Drive adr_o from src_i instead of src_q. Works against an always-immediate slave; breaks the first time one waits.
  • Capture dat_i outside the ACK_I cycle. The copied word is whatever followed on the bus.
  • Treat RTY_I as success. The transfer never happened and stale data is copied.
  • Treat RTY_I as an error. The copy aborts on an explicitly recoverable condition.
  • Advance pointers on the read rather than the write. Every word lands one slot too far.
  • Omit error_q's clear on start_i. One failure makes every later copy report an error.

Scaling. One transfer per cycle leaves the bus idle between the read's termination and the write's presentation; a block cycle would hold CYC_O across both (Module 8). And one word in flight bounds throughput by round-trip latency rather than bandwidth — pipelining is B4's mode and a later subject.

4. Verification — Master-Side Invariants

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_master_checker — invariants a conforming Wishbone Classic MASTER must
// satisfy, at the level Module 3 has established.
//
// NOT a conformance suite. Module 26 owns Wishbone verification, and the
// detailed edge relationships these would need are Module 5's.
// ─────────────────────────────────────────────────────────────────────────
module wb_master_checker #(
  parameter int unsigned AW = 32,
  parameter int unsigned DW = 32
) (
  input logic            clk_i,
  input logic            rst_i,
  input logic            cyc_o,
  input logic            stb_o,
  input logic            we_o,
  input logic [AW-1:0]   adr_o,
  input logic [DW/8-1:0] sel_o,
  input logic            ack_i,
  input logic            err_i,
  input logic            rty_i
);
  // RST_I is active HIGH, so the disable condition is rst_i itself.
  default disable iff (rst_i);

  logic terminated;
  assign terminated = ack_i | err_i | rty_i;

  // P1 — RULE 3.25 requires CYC_O asserted whenever STB_O is. Stated as the
  //      cheap implication; the full "no later / no earlier" edge
  //      relationship needs Module 5's timing material.
  property p_stb_implies_cyc;
    @(posedge clk_i) stb_o |-> cyc_o;
  endproperty
  a_stb_implies_cyc : assert property (p_stb_implies_cyc)
    else $error("STB_O asserted without CYC_O");

  // P2 — RULE 3.60 expressed as stability: while a transfer is presented
  //      and not yet terminated, the qualified signals must not move. This
  //      separates a master driven from registers from one driven from its
  //      client's live inputs.
  property p_request_stable;
    @(posedge clk_i) (stb_o && !terminated)
      |=> ($stable(adr_o) && $stable(we_o) && $stable(sel_o));
  endproperty
  a_request_stable : assert property (p_request_stable)
    else $error("a qualified request signal moved before termination");

  // P3 — a master does not withdraw a transfer before it is terminated.
  property p_no_withdrawal;
    @(posedge clk_i) (stb_o && !terminated) |=> stb_o;
  endproperty
  a_no_withdrawal : assert property (p_no_withdrawal)
    else $error("STB_O withdrawn before termination");

  // P4 — the master must never observe more than one termination at a time.
  //      RULE 3.45 places the obligation on the SLAVE; asserting it here
  //      catches an INTERCON that merged two selected slaves' responses —
  //      a fault for which neither endpoint is responsible.
  property p_one_termination;
    @(posedge clk_i) $onehot0({ack_i, err_i, rty_i});
  endproperty
  a_one_termination : assert property (p_one_termination)
    else $error("master saw multiple terminations: ack=%b err=%b rty=%b",
                ack_i, err_i, rty_i);
endmodule

Why P4 is on the master rather than the slave. RULE 3.45 constrains a slave, and a slave-side assertion catches a misbehaving slave. P4 catches something no slave is responsible for: an INTERCON that OR-merges the terminations of two simultaneously selected slaves. Each slave is individually conforming; the master still sees two. That fault is invisible at every endpoint and visible exactly here.

Tooling limitation, stated plainly. Icarus Verilog has no SVA support — a two-line property/assert property probe fails to parse — so this checker and every other in Module 3 was reviewed by inspection only. No tool available in this environment has executed them. The synthesisable RTL has no such gap.

5. Failure Modes and Discriminating Evidence

Symptom: the copy engine works against one peripheral and corrupts data against another.

Candidate causes. The slower peripheral is the first that ever made the master wait, exposing a stability violation. Or read data captured outside the termination cycle.

Discriminating evidence. Trigger on stb_o && !ack_i && !err_i && !rty_i and watch adr_o and we_o. Any change during that window is a stability violation — property P2 — and the fault is in the master's output assignments, not the peripheral. If the request is stable, put ack_i and the hold_q load on one waveform and check the capture edge.

Likely RTL location. The assign adr_o = … line, or hold_q's condition.

Symptom: the copy completes and the destination contains stale data.

Candidate causes. RTY_I treated as success, or an ERR_I on the write that was ignored.

Discriminating evidence. Count terminations by type across the copy. Any RTY_I at all, combined with a state machine that advanced, is conclusive — the transfer did not happen. This is precisely why the three terminations are decoded separately rather than as terminated / not terminated.

Likely RTL location. The S_READ/S_WRITE termination branches.

Symptom: the engine hangs mid-copy.

Candidate causes. A slave that never terminates; an unmapped address with no default slave; or an unbounded RTY_I loop.

Discriminating evidence. Probe cyc_o, stb_o and all three termination inputs. All three low with stb_o high means nothing is answering — then check whether any slave's strobe is asserted, which separates decode from slave. rty_i pulsing repeatedly means the retry loop is live and the master is behaving correctly against a slave that is refusing: a system problem, not a master bug.

Likely RTL location. Decode first, then the slave, then the slave's retry condition.

Symptom: every copy after the first reports an error.

Candidate causes. error_q not cleared on start_i.

Discriminating evidence. Run a failing copy then a known-good one. An error reported with no err_i asserted during the second is conclusive.

Likely RTL location. The S_IDLE start branch.

6. Common Mistakes

"A master should know how long each slave takes, so it can be efficient."

Wrong mental model: latency knowledge is an optimisation.

Concrete bug: a per-slave latency table that is silently wrong the moment a register slice is inserted for timing closure, or a peripheral is revised.

Observable evidence: reads returning the previous transfer's data after a change with no functional content.

Correct model: the master waits for a termination. In Classic there is no alternative — the absence of a termination is the wait, and there is no stall signal to interpret.

"RTY_I is a kind of error."

Wrong mental model: anything that is not an acknowledge is a failure.

Concrete bug: a copy engine that aborts on a recoverable condition — or, oppositely, treats retry as success and moves a word that was never read.

Observable evidence: spurious failures under load, or silently stale destination data.

Correct model: three terminations, three meanings. Success, failure, and did not happen, may be re-issued. Module 11 owns retry; what a master owes it here is a distinct branch.

"The master can drive its outputs from whatever produced the request."

Wrong mental model: the request exists at the moment it is issued, so the source signals suffice.

Concrete bug: Wishbone outputs assigned from the client's live inputs. Against an immediate slave the transfer lasts one cycle and nothing moves; against a waiting slave the address changes mid-transfer.

Observable evidence: correct with fast peripherals, corrupt with slow ones — and the corruption tracks the peripheral's speed.

Correct model: latch the request and drive the bus from registers. RULE 3.60 makes the request signals meaningful under STB_O; a master that cannot guarantee their stability has not met that obligation.

"A master must know the address map to reach the right peripheral."

Wrong mental model: the master picks the slave.

Concrete bug: decode logic inside the master, which works in one SoC and needs editing for the next.

Observable evidence: a master that cannot be reused without modification.

Correct model: the master drives an address; the INTERCON turns it into a selection. Neither endpoint holds the map — Chapter 2.2's argument, and the reason INTERCON is a separate module.

7. Interview Reasoning

Responsible for the entire request, and accountable for it until the slave ends it. It decides that a transfer should happen, drives the address, direction, byte selects and write data, opens the cycle and presents the transfer — and holds all of it stable until a termination arrives. RULE 3.60 makes those signals meaningful only under the strobe, so stability is the master's obligation.

It must act differently on each of three terminations: acknowledge means success and read data may be captured in that cycle; error means failure; retry means the transfer did not happen and may be re-issued.

Four things it must not know: the address map — it drives an address, the INTERCON decides who answers; a slave's latency — it waits, and in Classic waiting is the only mechanism because there is no stall signal; a slave's internals beyond the register contract; and the topology, because the master interface is identical from point-to-point to crossbar.

The framing that shows judgement: the specification does not forbid a master from knowing those things. It defines an interface that makes them unnecessary, and every one a designer builds in is a coupling to one system that surfaces the day the system changes.

8. Understanding Check

9. What's Next

The master's side is settled: it owns the request, holds it until termination, branches three ways on the outcome, and is deliberately ignorant of the map, the latency, the internals and the topology.

Half the interface remains, and it is not symmetric. The master initiates; the slave never does.

What must a Wishbone slave observe, decide, and return — and how does it stay reusable while doing it?

Chapter 3.4 — The Slave Interface builds a real peripheral against those obligations. The full path is on the Wishbone curriculum index.

Continue learning

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.