Wishbone · Module 4
DAT_O
Both masters and slaves have a DAT_O, and they mean opposite things. What each must drive, when it must be valid, and why a master may legally leave stale write data on the bus during a read.
Chapter 4.3 settled where. The next two chapters settle what — and the first of them has to clear up a name that causes more wiring errors than any other in the interface.
If an interface owns DAT_O, what must it drive, and when?
1. Two Paths, Not One Bus
Wishbone has two unidirectional data paths, and each is an output at one end and an input at the other.
MASTER SLAVE
────── ─────
DAT_O ───── write data ──────▶ DAT_I
DAT_I ◀──── read data ────── DAT_OWhy two paths rather than one bidirectional bus. Chapter 2.3 §1 gave the general reason: on-chip, a tri-state net costs more than it saves, so the industry standard is separate paths with multiplexing in the fabric. Wishbone follows that, and the consequence is visible in the interconnect — Chapter 3.5 §2 broadcasts the master's DAT_O to every slave and multiplexes the slaves' DAT_O back.
The asymmetry is not cosmetic. The write path has one producer and many possible consumers, which is free. The read path has many producers and one consumer, which requires selection. That is why adding a slave costs almost nothing on the write side and one more multiplexer input per data bit on the read side.
2. The Master's Output: Write Data
RULE 3.60 — "MASTER interfaces MUST qualify the following signals with [STB_O]: [ADR_O], [DAT_O()], [SEL_O()], [WE_O], and [TAGN_O]."
DAT_O is in that list, so the master's obligations are the same as for the address:
It is meaningful only under STB_O. Between transfers it carries residue, and no slave may act on it.
It must be stable while the transfer is presented. If a slave waits, the write data must not move — the structural-stability argument from Chapter 3.3, met by driving the bus from a latched copy.
One thing the rule does not say, and it matters: nothing requires the master to drive meaningful data on a read. The specification qualifies DAT_O with STB_O, not with STB_O && WE_O. Engineering consequence: a slave must not attach significance to DAT_I during a read, and a master is free to leave whatever it likes there. Most masters simply present the last write value, which is exactly what Chapter 3.3's copy engine does.
3. The Slave's Output: Read Data
A slave's DAT_O carries the value at the addressed location, and it has one obligation the master's side does not.
It must be meaningful in the termination cycle. Chapter 3.7 established that read data is valid in the cycle the termination is asserted and is not held afterwards. That is the window the master captures in.
And it must be quiet when this slave is not the one being addressed. This is the obligation people miss, and it is not a specification rule — it is a consequence of how fabrics merge.
4. RTL — Both Output Sides
// ─────────────────────────────────────────────────────────────────────────
// wb_dat_master_out — the MASTER's DAT_O: write data.
//
// PURPOSE. Show the output-side obligation in isolation: the write data is
// latched once, driven from the register, and never moves while the
// transfer is presented (RULE 3.60).
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_dat_master_out #(
parameter int unsigned AW = 30,
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic go_i,
input logic we_i,
input logic [AW-1:0] adr_i,
input logic [DW-1:0] wdat_i,
output logic cyc_o,
output logic stb_o,
output logic we_o,
output logic [AW-1:0] adr_o,
output logic [DW-1:0] dat_o, // MASTER DAT_O = WRITE data
input logic ack_i,
input logic err_i,
input logic rty_i
);
logic active_q, we_q;
logic [AW-1:0] adr_q;
logic [DW-1:0] dat_q;
// Every qualified signal is driven from a REGISTER. That is what makes
// RULE 3.60's stability obligation structural — the client may change
// wdat_i the cycle after go_i and the bus does not move.
assign cyc_o = active_q;
assign stb_o = active_q;
assign we_o = we_q;
assign adr_o = adr_q;
assign dat_o = dat_q;
always_ff @(posedge clk_i) begin
if (rst_i) begin
active_q <= 1'b0; // RULE 3.20
we_q <= 1'b0;
adr_q <= '0;
dat_q <= '0;
end else if (!active_q) begin
if (go_i) begin
active_q <= 1'b1;
we_q <= we_i;
adr_q <= adr_i;
// Latched on every transfer, read or write. RULE 3.60 qualifies
// DAT_O with STB_O, NOT with STB_O && WE_O — so a master is not
// required to present anything meaningful here on a read, and a
// slave must not attach significance to it.
dat_q <= wdat_i;
end
end else if (ack_i || err_i || rty_i) begin
active_q <= 1'b0;
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_dat_slave_out — the SLAVE's DAT_O: read data.
//
// PURPOSE. Show the two obligations on a slave's data output: present the
// addressed value when qualified for a read, and drive ZERO otherwise so a
// merged return path is not corrupted.
//
// The second obligation is a DESIGN CONVENTION, not a Wishbone rule — see
// Section 3. It is the safe default for a reusable slave.
// ─────────────────────────────────────────────────────────────────────────
module wb_dat_slave_out #(
parameter int unsigned OFF_AW = 10,
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic cyc_i,
input logic stb_i,
input logic we_i,
input logic [OFF_AW-1:0] adr_i,
input logic [DW-1:0] dat_i, // SLAVE DAT_I = WRITE data in
output logic [DW-1:0] dat_o, // SLAVE DAT_O = READ data out
output logic ack_o
);
localparam logic [OFF_AW-1:0] W_CTRL = 'd0;
localparam logic [OFF_AW-1:0] W_SCRATCH = 'd1;
localparam logic [OFF_AW-1:0] W_ID = 'd2; // read-only constant
logic [DW-1:0] ctrl_q, scratch_q;
logic xfer;
assign xfer = cyc_i & stb_i; // RULES 3.30 & 3.35
assign ack_o = xfer;
always_ff @(posedge clk_i) begin
if (rst_i) begin
ctrl_q <= '0;
scratch_q <= '0;
end else if (xfer && we_i) begin
unique case (adr_i)
W_CTRL: ctrl_q <= dat_i;
W_SCRATCH: scratch_q <= dat_i;
default: ; // W_ID is read-only
endcase
end
end
// ── The read-data output ─────────────────────────────────────────────
// Three conditions gate it, and each does a distinct job:
// xfer — this slave is the one being addressed (RULES 3.30, 3.35)
// !we_i — this is a read; a write has no read data
// case — the offset exists
//
// The unconditional `dat_o = '0` before the branch is the important line.
// It is the DEFAULT, so on every path where this slave is not producing
// data it produces zero — which is what keeps an OR-based return path
// from being corrupted, and what prevents a latch being inferred.
always_comb begin
dat_o = '0;
if (xfer && !we_i) begin
unique case (adr_i)
W_CTRL: dat_o = ctrl_q;
W_SCRATCH: dat_o = scratch_q;
W_ID: dat_o = DW'(32'h5742_0001); // "WB", revision 1
default: dat_o = '0;
endcase
end
end
endmoduleReading the pair
Purpose. Two interfaces, both driving a signal called DAT_O, carrying opposite information in opposite directions.
Ports that matter. On the master, dat_o is an output carrying write data and dat_i would be read data returning. On the slave the two are exchanged. Nothing in the names tells you which is which — only the module you are looking at does.
Ownership. The master drives write data; the slave drives read data; neither drives the other's.
Combinational logic. The master has none on the data path — dat_o is a register output. The slave's dat_o is a multiplexer with an unconditional zero default.
Sequential logic. The master latches write data on acceptance of a local request. The slave holds two application registers.
Timing. The master's dat_o is valid for as long as stb_o is asserted and does not move. The slave's dat_o is valid in the cycle ack_o is asserted, which for this always-immediate slave is the same cycle as the request.
Qualification. The master qualifies with stb_o per RULE 3.60. The slave qualifies with cyc_i & stb_i per RULES 3.30 and 3.35, and with !we_i, and with the offset being legal.
Reset. Synchronous, active high. The master's qualifiers are negated; the slave's application registers cleared (an educational choice, per Chapter 4.2).
Simplifications. No byte lanes — Chapter 4.7. No ERR_O or RTY_O. No wait states.
Failure modes.
- Master drives
dat_ofromwdat_idirectly. Works against an immediate slave, breaks the first time one waits. - Slave omits the
dat_o = '0default. A latch is inferred and, on an OR-based return path, every other slave's read is corrupted. - Slave omits
!we_i. It drives read data during writes — harmless in a gated fabric, corrupting in an OR-based one. - Master's
DAT_Owired to a slave'sDAT_O. Two drivers on one net; the tools catch it. Wired to the master's ownDAT_Iproduces reads returning the last value written — Section 6.
5. Waveform — Two Outputs, Two Windows
DAT_O on each side, in its own transfer
9 cyclesTwo things to read off it.
The master's DAT_O still carries 0xCAFE during the read. It is qualified by STB_O and therefore technically "meaningful", but on a read it means nothing — RULE 3.60 does not condition it on WE_O, and the slave must not attach significance to it. A slave that wrote a register on a read because the data looked valid is a real bug, and Chapter 4.6 is the signal that prevents it.
The slave's DAT_O is zero everywhere except cycle 6. Not because the value is unknown, but because this slave drives zero whenever it is not producing read data — the merged-return-path obligation from Section 3.
6. Failure Modes and Discriminating Evidence
Symptom: every read returns the value most recently written, regardless of address.
Candidate causes. The master's DAT_O wired to its own DAT_I, directly or through a fabric that treated "the data bus" as one net.
Discriminating evidence. Write a distinctive value, then read a different address that should hold something else. If the write value comes back, the read path is looped to the write path and no slave is involved at all. Confirm by checking whether the addressed slave's DAT_O even changed.
Likely RTL location. The fabric's data wiring, not any endpoint.
Symptom: a read returns a value that shares bits with two different registers.
Candidate causes. An unselected slave driving non-zero DAT_O into an OR-based return path.
Discriminating evidence. Compare the returned value against every slave's DAT_O in the termination cycle. A bitwise OR of two of them is conclusive, and recognisable by eye once seen.
Likely RTL location. The offending slave's read mux — usually a missing unconditional zero default.
Property. P3 in Section 7.
Symptom: a slave's register changes during a read.
Candidate causes. The slave's write path is not gated on WE_I, so the master's stale DAT_O lands in a register.
Discriminating evidence. Perform a read of a writable register and check whether it changed. The value it takes will be the previous write's data, which is a distinctive fingerprint.
Likely RTL location. The slave's write branch condition — Chapter 4.6.
Symptom: writes succeed against fast slaves and corrupt against slow ones.
Candidate causes. The master drives dat_o from its client's live input rather than a latched copy.
Discriminating evidence. Trigger on stb_o asserted with no termination and watch dat_o. Any change is a RULE 3.60 violation.
Likely RTL location. The assign dat_o = … line.
Property. P1.
7. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_dat_o_checker — output-side data properties.
//
// P1 and P2 are SPECIFICATION requirements (RULE 3.60).
// P3 is a DESIGN CONVENTION required by an OR-based return path, not a
// Wishbone rule — see Section 3 and the label on the property.
// ─────────────────────────────────────────────────────────────────────────
module wb_dat_o_checker #(
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
// Master side
input logic m_stb_o,
input logic m_we_o,
input logic [DW-1:0] m_dat_o,
input logic m_ack_i,
input logic m_err_i,
input logic m_rty_i,
// Slave side
input logic s_cyc_i,
input logic s_stb_i,
input logic [DW-1:0] s_dat_o
);
default disable iff (rst_i);
logic terminated;
assign terminated = m_ack_i | m_err_i | m_rty_i;
// P1 — SPECIFICATION (RULE 3.60, as stability). On a WRITE, the data must
// not move while the transfer is outstanding. Restricted to writes
// deliberately: the rule qualifies DAT_O with STB_O, and a master is
// free to leave anything on it during a read, so asserting stability
// on reads would fail correct designs.
property p_wdata_stable_while_presented;
@(posedge clk_i) (m_stb_o && m_we_o && !terminated) |=> $stable(m_dat_o);
endproperty
a_wdata_stable : assert property (p_wdata_stable_while_presented)
else $error("RULE 3.60: master DAT_O moved during an outstanding write");
// P2 — SPECIFICATION (RULE 3.60, as qualification). Not assertable as a
// value constraint — the rule says DAT_O is MEANINGFUL under STB_O,
// not that it must be any particular value otherwise. Recorded here
// as a note rather than a property, because writing an assertion
// that forced DAT_O to zero between transfers would fail every
// conforming master.
// P3 — DESIGN CONVENTION, not a Wishbone rule. Required by a fabric whose
// return path is an OR reduction; unnecessary in one that gates each
// slave's data. Bound to a slave written for the former.
property p_slave_quiet_when_unqualified;
@(posedge clk_i) (!(s_cyc_i && s_stb_i)) |-> (s_dat_o == '0);
endproperty
a_slave_quiet : assert property (p_slave_quiet_when_unqualified)
else $error("convention: slave drove DAT_O = %h while unqualified", s_dat_o);
endmoduleP2 is deliberately not a property, and the comment says why. It is tempting to assert that DAT_O is zero between transfers, and it would fail every conforming master — the rule makes the signal meaningful under the strobe, not zero otherwise. Writing an assertion stronger than the rule is the failure mode Chapter 2.6 warned about, and it is worth leaving a visible note where the temptation is.
Tooling limitation. Icarus has no SVA support; reviewed by inspection only.
8. Common Mistakes
"DAT_O is the data bus."
Wrong mental model: one bidirectional data path with one name.
Concrete bug: the master's write-data output wired to its own read-data input, or a slave's read data driven onto the write-data net.
Observable evidence: reads returning the last value written, independent of address.
Correct model: two unidirectional paths. DAT_O on a master is write data; DAT_O on a slave is read data. Same name, opposite directions, different wires.
"A slave can drive whatever it likes when it is not selected."
Wrong mental model: the fabric ignores unselected slaves.
Concrete bug: on an OR-based return path, an unselected slave's non-zero data ORs into the selected slave's value.
Observable evidence: a read value sharing bits with two different registers.
Correct model: the INTERCON selects but cannot silence. Driving zero when unqualified is a convention the slave must follow because it does not know which kind of fabric it will land in.
"Write data is meaningless on a read, so a slave can use it."
Wrong mental model: whatever is on the master's DAT_O during a read is fair game.
Concrete bug: a slave whose write path is gated only on the transfer and not on WE_I, writing a register during a read with the previous write's data.
Observable evidence: a register that changes when software reads it, taking the value of the last write.
Correct model: RULE 3.60 qualifies DAT_O with STB_O alone, so a master may leave anything there during a read. The slave's obligation is to gate its write path on WE_I.
9. Interview Reasoning
Because it lets a module's port list be written and read without knowing what it will be connected to.
A slave's author declares DAT_O as an output and it is correct in every system the slave is ever instantiated in. If the names were global — "write data" and "read data" — a slave would have an output called write-data-something, which reads backwards, and every module would have to be written with the system in mind.
Where it bites is precisely where it helps. DAT_O on a master is write data; DAT_O on a slave is read data. They are different wires travelling in opposite directions, and an engineer who thinks of "the DAT_O net" connects a master's write data to its own read-data input.
The symptom of that mistake is distinctive: every read returns the last value written, regardless of address, because the read path is looped back to the write path and no slave is involved at all.
The habit that removes the confusion: read the direction aloud rather than the suffix — "data, master output, so write data" — until the translation stops being needed.
10. Understanding Check
11. What's Next
The output side is settled: two unidirectional paths, each an output at one end; the master's write data qualified by the strobe and stable while presented; the slave's read data valid in the termination cycle and zero otherwise.
Every one of those outputs is somebody else's input, and the receiving side has its own rules — in particular, the one that decides when a value may be believed.
When an interface receives
DAT_I, what does that data mean, and when is it safe to use?
Chapter 4.5 — DAT_I answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
DAT_I
A master's DAT_I is returned read data; a slave's is incoming write data. Neither may be believed without the condition that qualifies it — and sampling one cycle late returns the previous transaction's value.
- Related topic
WE_O
One bit decides which data path carries the meaningful value. It is a direction selector and never a write trigger, and a slave that ignores it writes a register on every read with the previous write's data.
- Related topic
Data Transfer
Selection routes a request; it does not move a value. Write data fans out, read data fans in through a multiplexer that every target widens, byte lanes decide which parts of a word participate, and a register narrower than the bus has to be placed on a lane rather than merely connected.
- Related topic
Data Flow
One Wishbone access, followed through every block in both directions: what the master drives, where the address changes form, which signals are broadcast and which are decoded, and how read data and termination find their way back to exactly one requester.
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.
