Wishbone · Module 4
ACK_I
The only mandatory termination. What a slave promises by asserting it, how wait states work without a wait signal, and why RULE 3.55 requires a master to keep working when a slave holds it asserted.
Every master output is now covered, and all of them are qualified. A transfer is presented and held, and until the slave answers, nothing moves.
How does a slave say "done", and what exactly has it promised when it does?
1. What an Acknowledge Promises
ACK_O means the transfer completed normally, and what that entails depends on direction.
On a read: the slave has placed valid data on its DAT_O, and RULE 3.65 ties the two together — "SLAVE interfaces MUST qualify the following signals with [ACK_O], [ERR_O] or [RTY_O]: [DAT_O()]." The acknowledge is what makes the read data meaningful, which is exactly the capture window Chapter 4.5 built its master around.
On a write: the slave has accepted the data. It does not promise the data has reached its final destination — a slave with an internal write buffer may acknowledge on acceptance — only that responsibility has transferred.
What it does not promise, in either direction:
Not that the operation was semantically correct. A write to a register whose value is illegal is acknowledged; the slave accepted the transfer. Reporting an illegal value is an application-level concern, and conflating it with ERR_O is a design decision to make deliberately, not by accident.
Not that anything is observable yet. A write to a control register may take effect several cycles later inside the slave. The acknowledge is about the bus transaction, not about the device's internal state machine.
Not anything at all about other transfers. Each acknowledge terminates exactly one transfer.
2. Where Acknowledge Comes From
RULE 3.35 requires it to be generated in response to the AND of CYC_I and STB_I. That phrasing rules out two tempting shortcuts.
A slave may not tie ACK_O high. It would be asserted when no transfer is presented, which is not a response to anything. Chapter 3.2 contrasted this with valid/ready, where a permanently-ready sink is both legal and common — Wishbone Classic has no equivalent, because its termination is a response, not an advertised capability.
A slave may not acknowledge on STB_I alone. That is the RULE 3.30 violation Chapter 4.8 §9 traced through a two-master system.
What a never-busy slave does instead is make the acknowledge a function of the qualified transfer:
assign ack_o = xfer & ~err_o;Asserted exactly when a transfer is presented to it, negated otherwise. That is a response, generated combinationally, and it is what every slave in Modules 3 and 4 does.
RULE 3.50 adds the other end. Terminations are asserted and negated in response to STB_I's assertion and negation — so when the strobe drops, the acknowledge drops. A slave that latches ACK_O and holds it after the strobe has gone is answering a question nobody is asking.
3. Wait States, Without the Timing
A slave that cannot answer immediately simply withholds its acknowledge. The master's transfer stays presented, the slave takes the cycles it needs, and the acknowledge arrives when it is ready.
That is the entire mechanism, and it is worth stating plainly because it is smaller than people expect: there is no separate wait signal, no stall line, no credit. Silence is the wait.
Two consequences follow immediately.
Latency is not fixed and not negotiable. A master cannot know how long a transfer will take, and must not assume. Chapter 4.1 made the same point about the clock: synchronous does not mean fixed-latency.
Silence is indistinguishable from a broken slave. Chapter 4.8 §6 built a hang detector for exactly this reason — the protocol supplies no timeout, so a slave taking 10,000 cycles and a slave that will never answer look identical.
The precise cycle-by-cycle timing of a waited transfer belongs to Module 5 — how the signals line up, what the master does on each edge, what the slave must hold. Module 6 and Module 7 then work through read and write cycles in full; neither has shipped yet, which is why they stay bold. This chapter stops at what the signal means.
4. RULE 3.55 — The Rule Most Masters Are Written Without
5. RTL — Producing and Consuming an Acknowledge
// ─────────────────────────────────────────────────────────────────────────
// wb_ack_slave — a slave with a CONFIGURABLE number of wait states, so the
// acknowledge is produced both combinationally and after a delay.
//
// RULE 3.35: ack_o is generated in response to the AND of cyc_i and stb_i.
// RULE 3.50: it is negated when stb_i is negated — here structurally, since
// every path into ack_o contains xfer.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_slave #(
parameter int unsigned OFF_AW = 4,
parameter int unsigned DW = 32,
parameter int unsigned WAITS = 0 // 0 = never busy
) (
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,
output logic [DW-1:0] dat_o,
output logic ack_o,
output logic [DW-1:0] ctrl_o
);
localparam logic [OFF_AW-1:0] W_CTRL = 'd0;
logic [DW-1:0] ctrl_q;
assign ctrl_o = ctrl_q;
logic xfer;
assign xfer = cyc_i & stb_i; // RULES 3.30 & 3.35
// ── WAIT-STATE COUNTER ─────────────────────────────────────────────────
// Counts only while a transfer is presented, and resets the moment it is
// not. That reset is what satisfies RULE 3.50 structurally: when stb_i
// goes away, the counter goes back to zero and ack_o cannot be asserted.
logic [7:0] waited_q;
always_ff @(posedge clk_i) begin
if (rst_i) waited_q <= '0;
else if (!xfer) waited_q <= '0;
else if (!ack_o) waited_q <= waited_q + 8'd1;
end
// ── THE ACKNOWLEDGE ────────────────────────────────────────────────────
// With WAITS = 0 this reduces to `ack_o = xfer` — asserted in the same
// cycle the transfer is presented. That is the Classic single-cycle form,
// and Chapter 4.1 Section 2 explains why it is conformant despite the
// CLK_I description's "all outputs are registered" sentence.
assign ack_o = xfer & (waited_q >= 8'(WAITS));
// ── NOTE ON RULE 3.55 ──────────────────────────────────────────────────
// With WAITS = 0 and a master holding stb_i across consecutive transfers,
// ack_o stays HIGH continuously. This slave is conformant; a master that
// waits for ack_i to fall is not. See Section 4.
always_ff @(posedge clk_i) begin
if (rst_i) ctrl_q <= '0;
else if (xfer && we_i && ack_o && (adr_i == W_CTRL)) ctrl_q <= dat_i;
end
always_comb begin
dat_o = '0;
// RULE 3.65: the slave qualifies its DAT_O with its termination.
if (xfer && !we_i && ack_o) begin
dat_o = (adr_i == W_CTRL) ? ctrl_q : {{(DW-8){1'b0}}, 8'hA5};
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_ack_master — a block master written to satisfy RULE 3.55.
//
// It holds STB_O across consecutive transfers and counts ACKNOWLEDGES by
// sampling the LEVEL each cycle, never by looking for an edge. Against a
// never-busy slave ack_i is continuously high and this still counts three
// transfers as three.
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_master #(
parameter int unsigned AW = 30,
parameter int unsigned DW = 32,
parameter int unsigned CW = 8
) (
input logic clk_i,
input logic rst_i,
input logic go_i,
input logic [AW-1:0] base_i,
input logic [CW-1:0] count_i,
output logic done_o,
output logic [CW-1:0] acked_o, // transfers completed
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,
input logic [DW-1:0] dat_i,
input logic ack_i,
input logic err_i,
input logic rty_i
);
logic active_q;
logic [AW-1:0] adr_q;
logic [CW-1:0] left_q;
logic [DW-1:0] last_q;
assign cyc_o = active_q; // RULE 3.25
assign stb_o = active_q; // held across the block
assign we_o = 1'b0;
assign adr_o = adr_q;
assign dat_o = '0;
always_ff @(posedge clk_i) begin
if (rst_i) begin
active_q <= 1'b0; // RULE 3.20
adr_q <= '0;
left_q <= '0;
last_q <= '0;
acked_o <= '0;
done_o <= 1'b0;
end else begin
done_o <= 1'b0;
if (!active_q) begin
if (go_i && (count_i != '0)) begin
active_q <= 1'b1;
adr_q <= base_i;
left_q <= count_i;
acked_o <= '0;
end
end else begin
// ── RULE 3.55 COMPLIANCE, IN ONE LINE ────────────────────────────
// ack_i is read as a LEVEL, in the cycle the transfer is presented.
// Nothing here asks whether ack_i changed, so a slave holding it
// asserted across all three transfers is counted correctly.
//
// The bug this avoids: if ($rose(ack_i)) ... which would see one
// acknowledge instead of three and never finish the block.
if (ack_i) begin
last_q <= dat_i; // Chapter 4.5's capture
acked_o <= acked_o + CW'(1);
if (left_q == CW'(1)) begin
active_q <= 1'b0; // last transfer: drop CYC/STB
done_o <= 1'b1;
end else begin
left_q <= left_q - CW'(1);
adr_q <= adr_q + AW'(1);
end
end else if (err_i || rty_i) begin
active_q <= 1'b0;
done_o <= 1'b1;
end
end
end
end
endmoduleReading the pair
Purpose. The slave shows an acknowledge produced as a response, with and without wait states. The master shows one consumed as a level, which is what RULE 3.55 requires.
Ownership. The slave drives ACK_O and DAT_O; the master drives the qualifiers and the address.
Combinational logic. Slave: xfer, the acknowledge, and a read multiplexer gated on ack_o per RULE 3.65. Master: its qualifiers from active_q.
Sequential logic. Slave: the wait counter and one register. Master: the active flag, address, remaining count, captured data, acknowledge count.
Timing. With WAITS = 0 the slave acknowledges in the cycle the transfer is presented. With WAITS = 2 the counter reaches 2 on the third cycle of the presented transfer and the acknowledge asserts then. The master advances its address on the same edge it counts the acknowledge, so the next transfer is presented in the following cycle with STB_O never dropping.
Qualification. Every slave output contains xfer. The read path additionally contains ack_o, which is RULE 3.65 made structural.
Reset. Synchronous, active high, per RULES 2.30, 3.00 and 3.20.
Simplifications. The master reads only and abandons on ERR_I or RTY_I — a policy choice, examined in the next two chapters. The wait counter is eight bits, which bounds WAITS at 255.
Failure modes. Section 7.
6. Waveform — A Held Acknowledge Across Three Transfers
ACK_I: continuously asserted, three transfers
9 cyclesThere is exactly one rising edge on ACK_I in this figure, and three transfers completed. That is the whole of RULE 3.55 in one picture.
The slave is doing nothing unusual. ack_o = xfer & ~err_o, and xfer is continuously true because the master holds both qualifiers across the block. The acknowledge has no reason to fall.
A master using $rose(ack_i) would count one, wait forever for a second edge, and hang with STB_O and ACK_I both high — a waveform that looks like the transfer already succeeded, which is what makes the bug expensive.
7. Failure Modes and Discriminating Evidence
Symptom: a block transfer hangs after the first transfer, with STB_O and ACK_I both asserted.
Candidate causes. The master is edge-detecting ACK_I against a never-busy slave — a RULE 3.55 violation in the master.
Discriminating evidence. ACK_I continuously asserted while the master makes no progress is conclusive. The distinguishing feature against an ordinary hang is that the acknowledge is high, not low: the slave is answering, and the master is not listening.
Likely RTL location. The master's completion condition — $rose(ack_i), an edge-detect flop, or a wait-for-negation state.
Property. P4 in Section 8.
Symptom: a block completes with fewer transfers than requested.
Candidate causes. The same edge-detection, in a master whose counter is driven by edges but whose termination condition is driven by something else.
Discriminating evidence. Count the cycles in which CYC_I & STB_I & ACK_I were all true and compare with the master's count. A mismatch localises the bug to the master immediately.
Symptom: a slave acknowledges when no transfer is presented.
Candidate causes. ACK_O tied high, or derived from something other than the qualified transfer.
Discriminating evidence. Check CYC_I and STB_I in the cycle ACK_O is asserted. Either low is a RULE 3.35 violation.
Likely RTL location. The ack_o assignment.
Property. P1.
Symptom: read data is correct sometimes and zero other times, with no pattern in the address.
Candidate causes. The slave drives read data outside its termination, or the master captures outside it. The two are distinguishable.
Discriminating evidence. Look at the slave's DAT_O in the acknowledged cycle. Correct there but wrong at the master means the master's capture window is wrong (Chapter 4.5). Wrong there too means the slave is violating RULE 3.65.
Property. P3.
Symptom: a slave holds ACK_O asserted after the strobe drops.
Candidate causes. A registered acknowledge with no path back to zero when STB_I goes away — a RULE 3.50 violation.
Discriminating evidence. ACK_O high with STB_I low. Conclusive, and in a shared fabric it corrupts the next master's transfer, which is where the symptom will appear.
Property. P2.
8. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_ack_checker — acknowledge properties, slave side and master side.
//
// P1, P2, P3 are SPECIFICATION-derived (RULES 3.35, 3.50, 3.65).
// P4 is SPECIFICATION-derived from RULE 3.55, but is stated as a LIVENESS
// property about the MASTER rather than a constraint on any wire — which
// is why it needs a white-box progress signal.
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_checker #(
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 ack_o,
input logic [DW-1:0] dat_o,
input logic m_progress // white-box: master advanced a transfer
);
default disable iff (rst_i);
// P1 — RULE 3.35. The acknowledge is a RESPONSE: it may only be asserted
// when the transfer that provokes it is present. Catches a tied-high
// ACK_O and one derived from STB_I alone.
property p_ack_only_when_qualified;
@(posedge clk_i) ack_o |-> (cyc_i && stb_i);
endproperty
a_ack_only_when_qualified : assert property (p_ack_only_when_qualified)
else $error("RULE 3.35: ACK_O asserted without CYC_I & STB_I");
// P2 — RULE 3.50. Terminations are negated in response to the negation
// of STB_I. A registered ACK_O with no clearing path fails here.
property p_ack_drops_with_stb;
@(posedge clk_i) !stb_i |-> !ack_o;
endproperty
a_ack_drops_with_stb : assert property (p_ack_drops_with_stb)
else $error("RULE 3.50: ACK_O still asserted after STB_I negated");
// P3 — RULE 3.65. The slave qualifies its DAT_O with its termination.
// Stated in the contrapositive: no termination, no read data. A
// slave driving stale data between transfers fails here, which is
// also what keeps a merged return path clean (Chapter 4.4).
property p_read_data_qualified;
@(posedge clk_i) (!ack_o && !we_i) |-> (dat_o == '0);
endproperty
a_read_data_qualified : assert property (p_read_data_qualified)
else $error("RULE 3.65: slave drove read data without a termination");
// P4 — RULE 3.55, as LIVENESS. If a transfer is presented and the slave
// is acknowledging it, the master MUST make progress. This is the
// only way to catch edge-detection: the bus itself looks perfectly
// legal, and what is broken is that nothing happens.
property p_master_progresses_on_held_ack;
@(posedge clk_i) (cyc_i && stb_i && ack_o) |-> m_progress;
endproperty
a_master_progresses_on_held_ack :
assert property (p_master_progresses_on_held_ack)
else $error("RULE 3.55: master did not advance on an asserted ACK_I");
endmoduleP4 is the one worth studying. Every other property here constrains a wire, and a passive bus monitor can check it. P4 constrains an outcome — and the bus during the RULE 3.55 failure is entirely legal: qualifiers asserted, acknowledge asserted, all rules satisfied. What is wrong is that the master is not moving, and "not moving" is not a signal.
This is the same boundary Chapter 4.9 §7 drew around atomicity: conformance is checkable from the wires, and behaviour is not. RULE 3.55 is unusual among the B3 rules in that it constrains a master's internal design rather than its outputs, which is exactly why it needs a property of this shape.
Tooling limitation. Icarus has no SVA support; reviewed by inspection only.
9. Common Mistakes
"Wait for ACK_I to go high, then wait for it to go low before the next transfer."
Wrong mental model: the acknowledge is a pulse marking each completion.
Concrete bug: edge detection on ACK_I, which RULE 3.55 exists to forbid.
Observable evidence: a block that hangs after one transfer with STB_O and ACK_I both asserted — a waveform that looks like success.
Correct model: sample ACK_I as a level in the cycle the transfer is presented. A never-busy slave holds it asserted across an entire block, legitimately, and the master must count transfers rather than edges.
"A slave that is always ready can just tie ACK_O high."
Wrong mental model: importing valid/ready's always-ready sink, where this is standard.
Concrete bug: an acknowledge asserted with no transfer presented — RULE 3.35 violated, and in a shared fabric it terminates other slaves' transfers.
Observable evidence: transfers completing that were never addressed to this slave, and read data merging from two sources.
Correct model: Wishbone termination is a response. A never-busy slave writes ack_o = xfer & ~err_o, which is asserted exactly when there is something to answer.
"ACK_O means the operation succeeded."
Wrong mental model: acknowledge is a status code.
Concrete bug: a slave that acknowledges a write it did not perform, or errors a write it did — either way the termination stops describing what happened.
Observable evidence: software that believes a configuration took effect when the register is unchanged.
Correct model: ACK_O means the bus transfer terminated normally. It says nothing about whether the value was sensible or whether the device has finished acting on it. A transfer the slave refuses must be terminated with ERR_O and must change nothing — which is Chapter 4.11.
10. Interview Reasoning
The master is edge-detecting ACK_I, and the slave is never busy. That combination is a RULE 3.55 violation in the master.
Why ACK_I never falls. A never-busy slave computes its acknowledge as a function of the qualified transfer — ack_o = xfer & ~err_o. The master holds CYC_O and STB_O across the whole block, so xfer is continuously true and the acknowledge has no reason to go low. The slave is fully conformant.
What the master does wrong. It waits for a rising edge, or for a negation before presenting the next transfer. Neither is coming. It stalls, holding the strobe, and the acknowledge stays high in response — a stable, legal-looking bus with nothing happening on it.
Why the waveform misleads. ACK_I high normally means success. Seeing it asserted, an engineer's first instinct is that the transfer completed and the problem is downstream, so the search starts in the wrong place. The tell is that STB_O is still asserted — a completed transfer would have released it.
RULE 3.55 exists for exactly this, and its phrasing is worth noting: it constrains the master's design, not any signal it drives. That is rare among the B3 rules, and it is why the property that catches it needs a white-box progress signal rather than bus wires.
The fix. Sample ack_i as a level inside the state that represents an outstanding transfer, and count transfers rather than edges. It is a one-line change, and the same line makes wait states work correctly for free.
11. Understanding Check
12. What's Next
ACK_I is the normal ending, and it is the only one an interface must have. But a transfer can fail to be legal in the first place — an address no slave implements, a write to a read-only register — and every slave in Modules 3 and 4 has been answering those with a signal never explained.
How does a slave say no, and what is a master obliged to do about it?
Chapter 4.11 — ERR_I answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
ACK — Acknowledge
The slave's half of the handshake: a response rather than an advertised readiness, sampled as a level, with RULE 3.55 constraining the master's internal design rather than any wire it drives.
- Related topic
Control Signals
Address and data are payload; they say what values are involved and nothing about what should happen to them. Control information is what makes a bus interpretable: a qualifier that says a request is real, a direction, lane enables, a completion and an error — each derived from a failure that occurs without it.
- 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.
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.
