Wishbone · Module 5
Transaction Completion
One clock edge ends a Wishbone transfer, and four things must happen at it in the right relationship: capture the read data, record the result, release the bus, and report exactly once.
Chapter 5.5 closed the boundary where a client request becomes a bus transaction. This chapter closes the other one.
What exactly happens at the edge where the slave terminates the outstanding transfer?
1. Before, At, and After the Edge
Chapter 5.1 introduced edge-by-edge narration. Here it is applied to the one edge that matters most, for a read that terminates with ACK.
Before the edge — the slave has driven its answer and the master has not yet seen it.
- Master drives:
CYC_O,STB_O,ADR_O,WE_O,SEL_O, all stable since acceptance (RULE 3.60). - Slave drives:
ACK_Oasserted, andDAT_Ocarrying valid read data (RULE 3.65). - Master's state:
active_qset. Nothing has been captured.
At the edge — both sides sample.
- The master samples
ack_iasserted. Its transfer is complete. - The master samples
dat_i. This is the only edge at which that value is meaningful. - The slave samples its own inputs and sees the transfer still presented.
After the edge — the master's state has moved.
active_qcleared, soCYC_OandSTB_Onegate.rdat_onow holds the captured data.done_oasserted for one cycle.- The slave, seeing
STB_Inegate, dropsACK_Oand stops drivingDAT_O— RULE 3.50 and OBSERVATION 3.10.
Between this edge and the next — the read data on the bus is already gone. A master that deferred its capture has nothing to capture.
2. Why the Capture Cannot Be Deferred
This is worth stating sharply because the bug it prevents is the most common in Wishbone.
RULE 3.65 — "SLAVE interfaces MUST qualify the following signals with [ACK_O], [ERR_O] or [RTY_O]: [DAT_O()]."
A slave's read data is qualified by its own termination. Not by the strobe, not by the address — by the termination. So it is meaningful in exactly the cycles where a termination is asserted, and a conformant slave drives '0 otherwise, which Chapter 4.4 showed also keeps a merged return path clean.
The master therefore has exactly one opportunity, and the symptom of missing it is distinctive: every read returns the previous read's value. A perfect, consistent off-by-one across every read points at the capture edge and nothing else — and it looks like a software bug, which is why it costs time.
3. Reporting Exactly Once
The fourth obligation is the one the specification says nothing about, because it lives on the client side.
A bus transaction produces one termination. The master must turn that into one local completion.
Reporting twice double-counts: a client tracking outstanding requests leaks, a DMA descriptor advances twice, a semaphore is released twice.
Reporting zero times hangs the client, which is indistinguishable from a bus hang at the level the client can see.
The structural risk is a done_o that is a level rather than a pulse. If done_o is asserted while the master is idle, a client sampling it every cycle sees one completion per idle cycle. wb_single_master defaults done_o <= 1'b0 at the top of its always_ff and sets it only in the completion branch, which makes the pulse structural rather than a convention.
The second risk is a completion generated from the wrong condition — from ack_i alone, for instance, rather than from active_q && ack_i. That reports a completion for a termination the master did not provoke, which is property P3 in Chapter 5.1.
4. RTL — The Completion Path
// ─────────────────────────────────────────────────────────────────────────
// wb_completion_master — the termination edge, made explicit.
//
// PURPOSE. Chapter 5.5's wb_single_master handles completion correctly but
// compactly. This one separates the four obligations of Section 1 into
// named, individually reviewable steps, and distinguishes the three
// termination classes at the client interface instead of merging them.
//
// It is the Module 5 reference master. Everything Module 5 has established
// is present: latched metadata (5.5), level-sampled termination (5.4),
// PERMISSION 3.40 qualifiers (5.1), and a one-pulse local completion.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00, 3.20).
// ─────────────────────────────────────────────────────────────────────────
module wb_completion_master #(
parameter int unsigned AW = 30,
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
// ── local client side ───────────────────────────────────────────────
input logic req_i,
input logic req_we_i,
input logic [AW-1:0] req_adr_i,
input logic [DW-1:0] req_dat_i,
input logic [DW/8-1:0] req_sel_i,
output logic acc_o,
output logic busy_o,
output logic done_o, // ONE pulse per accepted request
output logic [1:0] status_o, // 0=ok 1=error 2=retry
output logic [DW-1:0] rdat_o,
// ── 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-1:0] dat_o,
output logic [DW/8-1:0] sel_o,
input logic [DW-1:0] dat_i,
input logic ack_i,
input logic err_i,
input logic rty_i
);
localparam logic [1:0] ST_OK = 2'd0, ST_ERR = 2'd1, ST_RTY = 2'd2;
logic active_q;
logic we_q;
logic [AW-1:0] adr_q;
logic [DW-1:0] dat_q;
logic [DW/8-1:0] sel_q;
assign acc_o = req_i & ~active_q;
assign busy_o = active_q;
assign cyc_o = active_q; // PERMISSION 3.40
assign stb_o = active_q;
assign we_o = we_q; // all from the private copy
assign adr_o = adr_q; // (Chapter 5.5)
assign dat_o = dat_q;
assign sel_o = sel_q;
// ── THE TERMINATION, DECODED ONCE ─────────────────────────────────────
// RULE 3.45 makes these mutually exclusive at the slave, so at most one
// is asserted. Decoding them in one place means the completion branch
// cannot disagree with itself about which one arrived.
logic terminated;
assign terminated = ack_i | err_i | rty_i;
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;
sel_q <= '0;
done_o <= 1'b0;
status_o <= ST_OK;
rdat_o <= '0;
end else begin
// ── OBLIGATION 4a: done_o defaults LOW every cycle ───────────────
// This default is what makes done_o a PULSE structurally. A design
// that sets done_o in the completion branch and clears it elsewhere
// depends on the "elsewhere" being reachable; this one cannot fail
// that way.
done_o <= 1'b0;
if (acc_o) begin
active_q <= 1'b1;
we_q <= req_we_i;
adr_q <= req_adr_i;
dat_q <= req_dat_i;
sel_q <= req_sel_i;
end else if (active_q && terminated) begin
// ══ THE TERMINATION EDGE ═════════════════════════════════════════
// All four obligations of Section 1, in one place.
// ── 1. CAPTURE READ DATA ────────────────────────────────────────
// Three conditions, each excluding a different wrong cycle:
// active_q : the slave is still driving the return path
// ack_i : the termination SUCCEEDED (no defined value on
// ERR_I; nothing happened on RTY_I)
// !we_q : this was a read
// RULE 3.65 gives exactly one cycle in which dat_i is meaningful,
// and this is it. Deferring by one cycle yields the previous
// read's value on every read.
if (ack_i && !we_q) rdat_o <= dat_i;
// ── 2. RECORD WHICH TERMINATION ─────────────────────────────────
// Kept DISTINCT rather than merged into a single error flag.
// ERR and RTY oblige a client to do different things: an error
// must not be retried, a retry should be (Chapters 4.11, 4.12).
// Collapsing them here would discard the one bit of information
// the third termination signal exists to carry.
if (ack_i) status_o <= ST_OK;
else if (err_i) status_o <= ST_ERR;
else status_o <= ST_RTY;
// ── 3. RELEASE THE BUS ──────────────────────────────────────────
// Clearing active_q negates CYC_O and STB_O together, which is
// correct for a single-transfer cycle (Chapter 5.2). The slave
// then drops ACK_O in response, per RULE 3.50.
active_q <= 1'b0;
// ── 4. REPORT ONCE ──────────────────────────────────────────────
// Guarded by active_q, so a termination the master did not
// provoke cannot generate a completion.
done_o <= 1'b1;
end
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_latecapture_master — THE OFF-BY-ONE, isolated for simulation.
//
// NOT A REFERENCE DESIGN. Identical to wb_completion_master except that
// the read-data capture is moved OUTSIDE the active_q guard, so it runs in
// the cycle AFTER the termination. By then the master has negated STB_O,
// the slave has stopped driving DAT_O per RULE 3.65, and the return path
// has reverted to zero.
//
// Everything on the bus remains fully conformant. Only the captured value
// is wrong, and it is wrong in the characteristic way: every read returns
// the PREVIOUS read's value.
// ─────────────────────────────────────────────────────────────────────────
module wb_latecapture_master #(
parameter int unsigned AW = 30,
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic req_i,
input logic req_we_i,
input logic [AW-1:0] req_adr_i,
input logic [DW-1:0] req_dat_i,
input logic [DW/8-1:0] req_sel_i,
output logic acc_o,
output logic busy_o,
output logic done_o,
output logic [DW-1:0] rdat_o,
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,
output logic [DW/8-1:0] sel_o,
input logic [DW-1:0] dat_i,
input logic ack_i,
input logic err_i,
input logic rty_i
);
logic active_q, we_q, cap_q;
logic [AW-1:0] adr_q;
logic [DW-1:0] dat_q;
logic [DW/8-1:0] sel_q;
assign acc_o = req_i & ~active_q;
assign busy_o = active_q;
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;
assign sel_o = sel_q;
always_ff @(posedge clk_i) begin
if (rst_i) begin
active_q <= 1'b0; we_q <= 1'b0; cap_q <= 1'b0;
adr_q <= '0; dat_q <= '0; sel_q <= '0;
done_o <= 1'b0; rdat_o <= '0;
end else begin
done_o <= 1'b0;
cap_q <= 1'b0;
// ── THE BUG ─────────────────────────────────────────────────────
// cap_q was set at the termination edge, so this runs ONE CYCLE
// LATE. dat_i has already reverted — RULE 3.65 means the slave
// stopped driving it when STB_O went away.
if (cap_q) rdat_o <= dat_i;
if (acc_o) begin
active_q <= 1'b1; we_q <= req_we_i; adr_q <= req_adr_i;
dat_q <= req_dat_i; sel_q <= req_sel_i;
end else if (active_q && (ack_i || err_i || rty_i)) begin
active_q <= 1'b0;
done_o <= 1'b1;
if (ack_i && !we_q) cap_q <= 1'b1; // defer the capture
end
end
end
endmoduleReading the pair
Purpose. The first master performs all four completion obligations at the right edge. The second defers one of them by a single cycle.
Interface. status_o replaces Chapter 5.5's merged err_o, keeping the three termination classes distinct at the client boundary.
State. Both hold the outstanding flag and the metadata copy. The broken master adds cap_q, and that register is the bug.
Combinational behaviour. acc_o, the qualifiers, the bus outputs from the private copy, and terminated.
Sequential behaviour. Capture on acceptance; the four completion steps on termination.
Request start. At the acc_o edge, per Chapter 5.5.
Waiting. Bus outputs register-held; nothing moves.
Termination. Level-sampled while active_q is set, per Chapter 5.4.
Read data. Captured at the termination edge under all three conditions. In the broken master, one cycle later, from a bus that has already reverted.
Write data. dat_q driven for the whole transfer, legal under RULE 3.60.
Reset. Synchronous, active high; active_q <= 0 negates both qualifiers.
Failure modes. Section 6.
Simplifications. One outstanding transaction. status_o records the class but implements no retry policy — that decision belongs to the client, and Module 11 owns retry properly.
5. Waveform — The Edge, and the Cycle After It
The termination edge: one cycle of valid read data
7 cyclesCycle 4 is one cycle wide and is the whole window. ACK_I and valid DAT_I coincide because RULE 3.65 ties them together.
Cycle 5 is why deferring fails. The master has negated STB_O; the slave has responded by dropping ACK_O and reverting DAT_O to zero, per RULE 3.50 and OBSERVATION 3.10. There is nothing left to capture.
In a busier system the late capture is worse than zero — it picks up whatever the next transfer puts on the return path, which is the classic "every read returns the previous read's value" signature.
6. Failure Modes and Discriminating Evidence
Symptom: every read returns the previous read's value.
Candidate causes. The capture runs one cycle after the termination.
Discriminating evidence. Put ACK_I, DAT_I and the master's capture strobe on one waveform. If the capture edge is one after the acknowledge edge, that is conclusive — and the perfect off-by-one across every read, with no dependence on address, is itself close to diagnostic.
Likely RTL location. The capture condition — typically if (ack_i) placed where active_q has already cleared.
Property. P1 in Section 7.
Symptom: reads return zero regardless of what the slave holds.
Candidate causes. The same deferred capture in a quiet system, where the return path reverts to zero rather than to another transfer's data.
Discriminating evidence. Check the slave's DAT_O in the acknowledged cycle. Correct there and zero at the master isolates the capture edge; zero at both is a RULE 3.65 violation in the slave.
Symptom: a failed access reports success, with plausible data.
Candidate causes. The capture is gated on terminated rather than on ack_i, so ERR_I or RTY_I also captures.
Discriminating evidence. Issue a read to an unmapped address and inspect status_o and rdat_o. A non-OK status with a changed rdat_o is conclusive — on an error there is no defined read value to have changed.
Property. P2.
Symptom: a client's outstanding-request counter drifts.
Candidate causes. done_o asserted for more than one cycle, or generated from a condition that can recur.
Discriminating evidence. Count done_o assertions against acc_o assertions over a run. Any inequality is conclusive, and the direction says which way it fails.
Likely RTL location. A done_o that is a level rather than a pulse, or one lacking a default assignment.
Property. P3.
Symptom: a completion is reported with no request outstanding.
Candidate causes. done_o derived from ack_i without the active_q guard, so a termination belonging to another master — or bus noise in a broken fabric — generates a spurious completion.
Discriminating evidence. done_o asserted while busy_o was low in the preceding cycle.
Property. P4.
Symptom: an error and a retry are handled identically, and a transient failure becomes permanent.
Candidate causes. The master merges ERR_I and RTY_I into one flag at the client boundary.
Discriminating evidence. Not a waveform question — read the master's client interface. A single error bit where the bus carries three distinct terminations has discarded information RULE 3.45 went to the trouble of keeping exclusive.
Correct approach. status_o's three-way encoding. Chapter 4.12 showed why retrying an error loops forever and not retrying a retry fails needlessly.
7. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_completion_checker — termination-edge properties.
//
// P1 and P2 are DESIGN OBLIGATIONS derived from RULE 3.65: the
// specification says a slave qualifies DAT_O with its termination, from
// which it follows that a master must capture there — but the
// specification does not dictate master capture timing, so these are
// house policy built on a specification fact. P3 and P4 are LOCAL POLICY
// encoding the client contract.
// ─────────────────────────────────────────────────────────────────────────
module wb_completion_checker #(
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic cyc_o,
input logic stb_o,
input logic ack_i,
input logic err_i,
input logic rty_i,
input logic we_o,
input logic [DW-1:0] rdat_q, // white-box: captured read data
input logic acc_o, // white-box: acceptance
input logic busy_o,
input logic done_o
);
default disable iff (rst_i);
// P1 — DESIGN OBLIGATION (from RULE 3.65). The captured read register
// changes only at the edge of a successful READ termination.
// Catches BOTH the off-by-one and an unconditional capture.
//
// $past is required because rdat_q changes AT the edge while the
// conditions authorising it were true BEFORE it — the shift
// Chapter 4.5 Section 10 warned about getting backwards.
property p_capture_at_termination_only;
@(posedge clk_i) $changed(rdat_q)
|-> $past(cyc_o && stb_o && ack_i && !we_o);
endproperty
a_capture_at_termination_only :
assert property (p_capture_at_termination_only)
else $error("read data captured outside a successful read termination");
// P2 — DESIGN OBLIGATION. A failed termination captures nothing. There
// is no defined read value on ERR_I and nothing happened on RTY_I.
property p_no_capture_on_failure;
@(posedge clk_i) (cyc_o && stb_o && (err_i || rty_i)) |=> $stable(rdat_q);
endproperty
a_no_capture_on_failure : assert property (p_no_capture_on_failure)
else $error("read data captured from a failed termination");
// P3 — LOCAL POLICY. done_o is a ONE-CYCLE PULSE and follows a real
// termination of an outstanding transfer. Catches a level-style
// done_o and a completion generated from the wrong condition.
property p_done_follows_termination;
@(posedge clk_i) done_o
|-> $past(busy_o && cyc_o && stb_o && (ack_i || err_i || rty_i));
endproperty
a_done_follows_termination : assert property (p_done_follows_termination)
else $error("completion pulse without a matching bus termination");
// P4 — LOCAL POLICY. Exactly one completion per acceptance, enforced as
// an alternation: an acceptance is always followed by a completion
// before the next acceptance. Catches both duplicated and dropped
// completions, which P3 alone would not.
property p_one_done_per_accept;
@(posedge clk_i) acc_o |=> (!acc_o throughout (done_o[->1]));
endproperty
a_one_done_per_accept : assert property (p_one_done_per_accept)
else $error("a second acceptance occurred before the completion");
endmoduleP1 is the property this chapter exists for, and note that it is not a conformance property despite resting on RULE 3.65. The specification constrains the slave — it says when DAT_O is qualified. What a master does with that window is the master's design, so P1 is house policy built on a specification fact. That distinction is the kind Chapter 5.8 makes systematic.
P4 uses a goto repetition (done_o[->1]) to express alternation rather than trying to count. Counting completions against acceptances in a property is possible but fragile; alternation states the intent directly.
Tooling limitation. Icarus Verilog has no SVA support and cannot execute any of these. They are reviewed by inspection; both masters are elaborated and the off-by-one is simulated in Section 8.
8. Simulation — Read Capture, Measured
Both masters performed three reads of distinct known values from wb_stb_wait_slave with three wait states, then a fourth read, with the captured value recorded after each completion.
read slave holds correct late
1 0x11 0x11 0x0
2 0x22 0x22 0x0
3 0x33 0x33 0x0
late-capture master final value: 0x0The late-capture master returned zero on every read, because in this quiet two-node system the return path reverts to zero once the strobe drops — the slave obeying RULE 3.65 exactly as it should.
Note that its register never changed at all across three reads of three different values. It is not lagging by one; it is sampling a bus that has already gone idle.
In a busier fabric the same bug returns the previous read's value instead, which is harder to spot because the values look plausible. Zero is the friendlier symptom, and a design that sees uniform zeros from every read should suspect the capture edge before suspecting the slave.
9. Common Mistakes
"ACK means the data is available; I can read it next cycle."
Wrong mental model: the read data persists around the acknowledge.
Concrete bug: capture deferred by one cycle.
Observable evidence: every read returning zero, or every read returning the previous read's value — a perfect off-by-one with no address dependence.
Correct model: RULE 3.65 qualifies the slave's DAT_O with its termination. One cycle, then it is gone — and OBSERVATION 3.10 says the slave negates automatically when the strobe drops.
"A termination is a termination; capture on any of them."
Wrong mental model: the transfer ended, so take the data.
Concrete bug: capturing on ERR_I or RTY_I.
Observable evidence: a master reporting a failure while its data register changed — meaningless data that a client may act on.
Correct model: read data accompanies a successful termination. RULE 3.45 keeps the three exclusive precisely so a master can branch on which arrived.
"One error flag is enough at the client interface."
Wrong mental model: the client only needs success or failure.
Concrete bug: ERR_I and RTY_I merged. A transient condition becomes a permanent failure, or a permanent one is retried forever.
Observable evidence: spurious failures under load, or a master looping on an unmapped address.
Correct model: they differ in exactly one respect — whether retrying could succeed — and that is the information the third wire exists to carry. status_o keeps it.
10. Interview Reasoning
In the master's read-data capture, one cycle after the termination instead of at it.
The mechanism. RULE 3.65 qualifies a slave's DAT_O with its own termination, so the data is meaningful in exactly the cycle the acknowledge is asserted. At that edge the master clears its outstanding flag and negates STB_O. The slave, per RULE 3.50 and OBSERVATION 3.10, drops ACK_O and stops driving read data. A capture in the following cycle samples a return path that has already reverted.
Why the previous value specifically. The capture register holds whatever it got last time. If the fabric reverts to zero, every read returns zero. If another transfer is in flight, it picks that up. The consistent case — previous read's value — happens when the register simply is not rewritten, so the client keeps seeing the last successful capture.
What makes it diagnostic. The off-by-one is perfect and address-independent. Every read, every address, always the previous value. Bugs in a slave or in decode vary with address; this one does not. That pattern alone should point at the capture edge.
The one waveform that confirms it. ACK_I, DAT_I, and the master's capture strobe on the same axis. If the capture is one edge after the acknowledge, done.
Why it survives review. if (ack_i) rdat_o <= dat_i; is correct inside the state representing an outstanding transfer and wrong outside it. It is the same line of code in both places, so reading the line tells you nothing — you have to read where it sits.
The related bug worth checking simultaneously. If the capture is gated on terminated rather than ack_i, an errored read also captures. Issue a read to an unmapped address and see whether the data register moved; on an error there is no defined value for it to have moved to.
11. Understanding Check
12. What's Next
Initiation and completion are now both precise: a named acceptance edge with a full capture, and a termination edge with four obligations in the right relationship.
Everything so far has described which edge things happen on. Nothing has yet described what happens between edges — where the combinational paths run, why a slave that answers immediately can be protocol-perfect and still fail timing closure, and what the specification itself says about that tradeoff.
How do all the handshake signals relate around clock edges, and what does that cost in silicon?
Chapter 5.7 — Timing Relationships answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
Bus Transactions
A transaction is the unit of bus work: one beginning, one ending, and an interval in between during which the request must not move. Making waiting expressible is what lets a slow target share a bus with a fast one, and it is what turns an initiator from a wire into a state machine with real failure modes.
- Related topic
The Wishbone Mental Model
Wishbone is two levels, not one: a master opens a bus cycle and presents transfers inside it, and an addressed slave terminates each transfer with exactly one of three signals. That two-level structure is why describing Wishbone as valid/ready with renamed signals is wrong rather than merely imprecise.
- Related topic
The Slave Interface
A Wishbone slave never initiates. It observes a qualified request, interprets a local offset, and ends the transfer with exactly one of three terminations. Everything that makes it reusable comes from what it refuses to know: its own base address, the topology, and which master is asking.
- Related topic
Transaction Lifecycle
One Wishbone transaction from a master's decision to act through the slave's termination and back to the caller: what is fixed by the protocol, what every implementation may vary, and what a real simulation of the assembled system shows at each step.
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.
