Wishbone · Module 2
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.
Module 1 closed by arguing that independently written blocks need a published interface contract. It did not say what such a contract contains.
Module 2 builds that vocabulary — the concepts every on-chip bus shares, before any one bus's version of them. It uses generic names throughout, and says so every time, because the whole point of Module 2 is that these ideas are not Wishbone's.
The first of them is the most basic and the most often stated carelessly.
Which side of a bus connection starts an operation, and which side answers — and what, precisely, does each side get to decide?
1. The Roles, and What They Are Not
A master — equivalently an initiator — is the side that starts a transfer. It decides that an access should happen, when it should happen, what address it targets, and whether it is a read or a write.
A slave — equivalently a target — is the side that answers. It never starts anything. It waits to be addressed, and when it is, it supplies whatever the transfer requires from its side and indicates that the transfer is finished.
A note on terminology, kept short because it is not the lesson. This curriculum uses master and slave where the Wishbone specification does, because reading the specification and the ecosystem's RTL requires those words. Much of the industry now writes initiator and target, or manager and subordinate — Arm's AMBA specifications made that change, and APB's manager/subordinate model uses the newer terms for the same idea. The concepts are identical. This chapter uses both vocabularies interchangeably on purpose, so that neither one becomes the only one you can read.
Three things the role does not mean, each of which is a real misconception:
It is not a statement about importance or complexity. A DMA engine is an initiator and a 512 KiB SRAM controller is a target; the SRAM is by far the larger and more complex block. Initiating is a job, not a rank.
It is not a property of a block. A block is a master on a particular interface. Chapter 1.5's DMA engine is an initiator on the memory bus and simultaneously a target on the register bus the CPU configures it through. Same block, two interfaces, opposite roles, and both are active at once.
It is not a hierarchy. Nothing about being a master implies authority over a slave, or that a slave is "inside" a master. They are two ends of one interface, and the interface has no notion of above and below.
2. Ownership — the Part That Matters
Here is the definition worth carrying out of this chapter. For each piece of information in a transfer, exactly one side decides its value, and the role determines which.
| Information | Owner | Why that side and not the other |
|---|---|---|
| Address | initiator | Only the initiator knows what it wants to access |
| Direction (read or write) | initiator | The target cannot infer intent from an address |
| Write data | initiator | It is the value being delivered |
| Byte granularity | initiator | Which bytes matter is a property of the request |
| Request qualification | initiator | Only the initiator knows when its request is real |
| Read data | target | The value exists only inside the target |
| Completion | target | Only the target knows when it has finished |
| Error | target | Only the target can judge whether the access was legal |
Read the split, not the rows. Everything describing what is being asked for flows one way. Everything describing what happened flows the other. No item is owned by both, and none is unowned.
Two consequences follow immediately, and both are load-bearing for the rest of Module 2.
Each signal has exactly one driver. If two blocks can drive read data, the bus has two owners for one fact and the result is whatever the electrical or logical merge produces. Chapter 1.5 showed an AND-OR return path producing the bitwise OR of two targets' values — a number belonging to neither. That is an ownership violation before it is an electrical problem.
Ownership tells you where to look when something is wrong. A transfer that returns wrong read data is a target-side or return-path problem; the initiator did not produce that value and cannot have corrupted it. A transfer that reaches the wrong target is an initiator-side or decode problem; the target only ever sees what it was handed. One glance at the ownership table cuts a debug search in half, and Section 6 makes that concrete.
3. The Teaching Interface
Module 2 needs a concrete interface to reason about. Here it is, defined once and reused in every chapter.
// ─────────────────────────────────────────────────────────────────────────
// THE MODULE 2 TEACHING INTERFACE.
//
// These are GENERIC educational signal names. They are NOT Wishbone signal
// names and they are not any other bus's signal names either. Wishbone's
// actual signals — their names, directions and rules — are Module 4's
// subject, and meeting them before the concepts are in place is exactly the
// mistake Module 1 exists to prevent.
//
// Every signal below is owned by exactly one side, per Section 2.
// ─────────────────────────────────────────────────────────────────────────
// Driven by the INITIATOR (master):
// valid 1 = a request is being presented this cycle
// write 1 = write, 0 = read
// addr [31:0] byte address of the access
// wdata [31:0] data to be written
// byte_en [3:0] which byte lanes of wdata participate
// Driven by the TARGET (slave):
// ready 1 = this request is complete on this clock edge
// rdata [31:0] read data, valid in the cycle `ready` is high
// error 1 = the access completed unsuccessfullyThe one temporal rule Module 2 assumes, stated here and relied on throughout:
A transfer is accepted on a rising clock edge where
validandreadyare both high. Until that edge, the initiator holdsvalid,write,addr,wdataandbyte_enstable.
That is a teaching contract, not a standard. It is deliberately the simplest rule that makes the rest of the module expressible, and Chapter 2.5 is where its consequences are worked out properly. Real buses differ in the details — how many transfers may be outstanding, whether a request may be withdrawn, what happens on a reset mid-transfer — and those differences are exactly what a protocol specification exists to pin down.
4. RTL 1 — A Target That Answers
The simplest useful target: a GPIO block that owns three registers.
// ─────────────────────────────────────────────────────────────────────────
// gpio_target — a slave on the Module 2 teaching interface.
//
// PURPOSE. Demonstrate what a target owns and what it must never assume.
// It answers requests addressed to it; it never starts one; it drives only
// `ready`, `rdata` and `error`, and reads everything else.
//
// The address it sees is a LOCAL OFFSET, not a system address. The block has
// no idea where it lives — the separation Chapter 1.2 argued for, now a port.
// ─────────────────────────────────────────────────────────────────────────
module gpio_target #(
parameter int unsigned AW = 8, // local offset width
parameter int unsigned DW = 32,
parameter int unsigned PINS = 8
) (
input logic clk,
input logic rst_n, // asynchronous assert, active low
// ── Initiator-driven (this block only reads these) ───────────────────
input logic sel, // this target is the decoded one
input logic valid,
input logic write,
input logic [AW-1:0] addr, // LOCAL offset
input logic [DW-1:0] wdata,
input logic [3:0] byte_en,
// ── Target-driven (this block owns these) ────────────────────────────
output logic ready,
output logic [DW-1:0] rdata,
output logic error,
// ── The hardware this block exists to control ────────────────────────
output logic [PINS-1:0] pin_out,
input logic [PINS-1:0] pin_in
);
localparam logic [AW-1:0] REG_DIR = 'h00; // 1 = output
localparam logic [AW-1:0] REG_OUT = 'h04; // value driven when enabled
localparam logic [AW-1:0] REG_IN = 'h08; // read-only, samples the pins
logic [PINS-1:0] dir_q, out_q, pin_sync_q, pin_meta_q;
// An access is happening to THIS target only when it is selected AND the
// initiator says its request is real. Either alone is not enough, and
// Section 6 is the bug that results from using only one of them.
logic access;
assign access = sel & valid;
logic addr_legal;
always_comb begin
unique case (addr)
REG_DIR, REG_OUT, REG_IN: addr_legal = 1'b1;
default: addr_legal = 1'b0;
endcase
end
// ── Combinational: what this cycle decides ───────────────────────────
// This block is never busy, so it completes in the cycle it is asked.
// `ready` is gated by `access` because a target must never complete a
// transfer it was not given — see assertion P2 in Section 5.
assign ready = access;
assign error = access & ~addr_legal;
always_comb begin
rdata = '0; // default: no latch
if (access && !write && addr_legal) begin
unique case (addr)
REG_DIR: rdata = {{(DW-PINS){1'b0}}, dir_q};
REG_OUT: rdata = {{(DW-PINS){1'b0}}, out_q};
REG_IN: rdata = {{(DW-PINS){1'b0}}, pin_sync_q};
default: rdata = '0;
endcase
end
end
// ── Sequential: what survives the cycle ──────────────────────────────
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dir_q <= '0;
out_q <= '0;
end else if (access && write && addr_legal && byte_en[0]) begin
// Only lane 0 carries these 8-bit registers. Honouring byte_en is what
// lets a byte store to a neighbouring register leave these alone.
unique case (addr)
REG_DIR: dir_q <= wdata[PINS-1:0];
REG_OUT: out_q <= wdata[PINS-1:0];
default: ; // REG_IN is read-only
endcase
end
end
// Two-flop synchroniser: pin_in is asynchronous to clk.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pin_meta_q <= '0;
pin_sync_q <= '0;
end else begin
pin_meta_q <= pin_in;
pin_sync_q <= pin_meta_q;
end
end
assign pin_out = out_q & dir_q;
endmoduleReading this module
Purpose. It is the smallest block that exhibits every target-side behaviour: it answers only when selected, it owns exactly three outputs, and it distinguishes a legal access from an illegal one.
Interface contract. sel says the decoder chose you; valid says the initiator means it. Both are required. addr is a local offset — the block never sees a system address, which is what lets it be instantiated twice at two different bases.
Combinational behaviour. Three decisions are made in the current cycle: whether this is an access at all, whether the offset is legal, and — on a read — which register's value to present. rdata is assigned '0 before the conditional, so every path assigns it and no latch can be inferred.
Sequential behaviour. Exactly two registers survive across cycles, plus the synchroniser. The write branch requires access && write && addr_legal && byte_en[0], which is four independent conditions, each of which is a real requirement rather than defensive clutter.
Timing. On any rising edge where access is high, the transfer is complete — ready is already high combinationally, and a write has landed. There are no wait states. That is a deliberate simplification, and Chapter 2.5 removes it.
Deliberate simplifications. No wait states, no interrupt output, no byte-granular access to a wider register, and a single-cycle ready that is a combinational function of the inputs. That last one is worth flagging: a combinational path from valid to ready becomes a loop the moment an initiator makes valid depend on ready — a hazard Chapter 1.3 §6 raised and that real protocols address with explicit rules about which side may depend on the other.
How it could fail. Drop sel from access and the block answers every transfer in the system. Drop valid and it answers on stale bus values between transfers. Drop addr_legal from the write branch and a write to an unimplemented offset silently lands in whichever register the unique case happens to fall through to. Forget byte_en and a byte-granular store corrupts a neighbour.
What to verify. Section 5.
5. Verification — Properties That Encode Ownership
Ownership is a structural claim, so it is assertable.
// ─────────────────────────────────────────────────────────────────────────
// Ownership properties for a target on the Module 2 teaching interface.
// These check the ARCHITECTURAL claims of this chapter. They are not any
// bus's protocol rules — Wishbone's own properties are Module 26's subject.
// ─────────────────────────────────────────────────────────────────────────
module target_role_checker #(
parameter int unsigned DW = 32
) (
input logic clk,
input logic rst_n,
input logic sel,
input logic valid,
input logic ready,
input logic error,
input logic [DW-1:0] rdata
);
default disable iff (!rst_n);
// P1 — a target never completes a transfer it was not given. This is the
// ownership rule stated as a property: `ready` belongs to the target,
// but the target may only assert it inside an access aimed at it.
property p_ready_requires_access;
@(posedge clk) ready |-> (sel && valid);
endproperty
a_ready_requires_access : assert property (p_ready_requires_access)
else $error("target asserted ready with sel=%b valid=%b", sel, valid);
// P2 — the same rule for the error output. Split from P1 so a failure
// names which output escaped, rather than reporting "something did".
property p_error_requires_access;
@(posedge clk) error |-> (sel && valid);
endproperty
a_error_requires_access : assert property (p_error_requires_access)
else $error("target asserted error outside an access");
// P3 — an unselected target drives no read data. On a shared return path
// built as an OR reduction, a non-zero rdata from an unselected
// target corrupts the selected one's value. This catches it at the
// source instead of at the multiplexer.
property p_quiet_when_unselected;
@(posedge clk) (!sel) |-> (rdata == '0);
endproperty
a_quiet_when_unselected : assert property (p_quiet_when_unselected)
else $error("unselected target is driving rdata = %h", rdata);
endmoduleWhy these three. Each corresponds to a failure that actually happens and that produces no error anywhere in a working system. P1 and P2 catch a target that answers transfers belonging to someone else. P3 catches the quieter version — a target that stays out of the completion path but still contributes bits to a shared return path.
What is deliberately not asserted. Nothing about how long the target may take, because this chapter's target is never busy and the general rule is Chapter 2.5's. Nothing about the initiator's obligations, which need the transaction model first.
6. Failure Modes and the Evidence That Separates Them
Role and ownership violations have a characteristic property: they produce plausible behaviour rather than errors. These are worth knowing by symptom.
Symptom: writing one peripheral also changes another.
Candidate causes. Two targets are selected for one address — an ownership violation in the decoder. Or one target ignores sel and answers everything. Or two regions overlap.
Discriminating evidence. Probe both targets' sel inputs on the offending access. If both assert, the fault is decode and Chapter 2.2 owns it. If only one asserts and the other target still changed state, that target is not gating on sel — the fault is inside it, and assertion P1 fires at the exact cycle.
Likely RTL location. Either the decoder's region comparison, or the target's access term.
Symptom: a read returns a value that looks like two values merged.
Candidate causes. An unselected target driving non-zero read data into an OR-based return path.
Discriminating evidence. Compare the returned value against each target's rdata in the same cycle. A bitwise OR of two of them is conclusive, and it is a pattern you can recognise by eye once you have seen it. Assertion P3 catches it at the source.
Likely RTL location. The unselected target's read mux — usually a missing sel in its default assignment.
Symptom: transfers succeed but data is occasionally from the previous access.
Candidate causes. The initiator changed addr or wdata before the accepting edge, violating the stability rule in Section 3. Or it sampled rdata outside the cycle ready was high.
Discriminating evidence. Put valid, ready and addr on one waveform. If addr changes while valid is high and ready is low, the initiator broke the contract — the target is blameless. If both were stable, look at when the initiator captured rdata.
Likely RTL location. The initiator's request register, not the target.
Symptom: the bus works with one target and breaks when a second is added.
Candidate causes. Almost always a target that was answering unconditionally and got away with it while it was the only one.
Discriminating evidence. Add P1 to every target and run the existing tests. The one that fires was broken the whole time and was invisible because nothing contradicted it.
The general strategy: use the ownership table to pick a side before picking a block. Read data and completion are the target's; address, direction and write data are the initiator's. A wrong value in one of those columns is a bug on that column's side, and that single question halves the search before any waveform is opened.
7. Common Mistakes
"The master controls the slave."
Wrong mental model: master implies authority, so a slave does what it is told.
Concrete failure: an engineer expects a write to complete the action, and designs an initiator that assumes the peripheral has finished the physical work when the transfer ends. The transfer completed; the UART is still shifting bits.
Observable evidence: software that overwrites a transmit register while the previous byte is still going out, and loses data at high rates only.
Correct model: the roles describe who starts and who answers. A completed transfer means the register was written, not the hardware has finished acting on it.
"A block is either a master or a slave."
Wrong mental model: the role is a property of the block.
Concrete failure: a DMA engine designed with a single port, on the assumption it must pick one role — so its configuration registers end up reachable only through a side channel, or not at all.
Observable evidence: an integration where the CPU has no way to configure the DMA engine it is supposed to control.
Correct model: the role belongs to the interface. Figure 1's DMA engine is a target on its register port and a master on its memory port, simultaneously.
"A target can answer whenever it recognises its own address."
Wrong mental model: sel is redundant because the target could decode the address itself.
Concrete failure: the target answers on bus values left over between transfers, and answers transfers aimed at other targets whose addresses happen to alias.
Observable evidence: spurious completions with no request, and — on a shared return path — corruption of other targets' reads. Assertion P1 fires immediately.
Correct model: the decoder owns target selection, and the target owns its local offset. Splitting it that way is what lets the same block be instantiated twice at different bases.
"Read data just needs to be correct eventually."
Wrong mental model: the target puts the value on the bus and the initiator picks it up.
Concrete failure: a target that drives valid read data one cycle and drops it, against an initiator that captures a cycle later. The value is right in a waveform and wrong in a register.
Observable evidence: reads that return the previous access's data, consistently, which looks like an off-by-one in software.
Correct model: read data has a defined validity window — here, the cycle ready is high — and both sides must agree on it. That agreement is precisely what a protocol specification fixes.
8. Interview Reasoning
It means that on a particular interface, that block starts transfers: it decides an access should happen, when, to what address, and in which direction.
What it does not mean, and each of these is a real confusion:
- It is not importance or complexity. A DMA engine initiates against a memory controller that is far larger and more complex.
- It is not a property of the block. A DMA engine is a target on its register port and a master on its memory port, at the same time. The role belongs to the interface.
- It is not authority. A completed write means the register was written, not that the peripheral has finished acting on it.
The part a strong answer reaches: the role's real content is ownership. The initiator owns address, direction, write data and request qualification; the target owns read data, completion and error. That assignment is what makes each signal single-driver, and it is what tells you which side to investigate when a transfer goes wrong.
9. Understanding Check
10. What's Next
This chapter fixed the roles and, more usefully, the ownership that follows from them. The initiator supplies address, direction, write data and qualification; the target supplies read data, completion and error; every signal has one driver; and that table is a debugging tool before it is a definition.
It also assumed something without examining it. gpio_target has a sel input, and the chapter simply said the decoder chose you. Nothing so far says how.
Given a 32-bit address and a system of several targets, what decides which one is selected — and what makes that decision correct rather than merely working?
Chapter 2.2 — Address Space answers it at the bus level: how a region is expressed in hardware, why range comparison and mask comparison are different engineering choices, how a global address becomes a local offset, and what makes a decode exhaustive and mutually exclusive rather than accidentally either. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
Shared Resources
Two initiators wired to one target is not a wiring problem with a wiring solution. A single-port target has one address input and one completion output, so access must be serialised — and the rule that matters most is not who goes first but that ownership cannot change while a transaction is in flight.
- Related topic
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.
- 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.
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.
