Wishbone · Module 4
ERR_I
An abnormal termination whose meaning the specification deliberately delegates to the IP core supplier — which makes RULE 2.15's datasheet normative, and an undocumented ERR_O a real integration hazard.
Every slave in Modules 3 and 4 has answered unmapped offsets and illegal directions with a signal that was never explained. Chapter 4.8 went as far as saying silence is the one response that is never acceptable, and left the alternative unexamined.
How does a slave say no, and what is a master obliged to do about it?
1. What an Error Terminates
ERR_O ends the transfer. It is one of the three mutually exclusive terminations under RULE 3.45, generated in response to the qualified transfer under RULE 3.35, exactly like an acknowledge — the difference is entirely in what the master is being told.
What it means in the general case: this transfer cannot be completed as requested, and retrying it unchanged will not help. That last clause is what separates it from Chapter 4.12's retry, and it is the distinction that actually matters at the master.
The conditions that produce one in this module's slaves, all of them design decisions rather than requirements:
| Condition | Chapter |
|---|---|
| offset not implemented in this slave | 3.4 |
| write to a read-only register | 4.6 |
| read of a write-only register | 4.6 |
| byte-lane mask selecting nothing | 4.7 |
| address matching no slave at all | 3.5 |
None of those are specified by Wishbone. Each is a choice that slave made and should have documented, and a different slave could legitimately acknowledge every one of them.
2. The Obligation That Comes With It
An errored transfer must change nothing.
This is not stated as a numbered rule — it follows from what the termination communicates. A slave that reports failure and performs the operation anyway has told the master something untrue, and the master has no way to discover it.
It is the worst of the failure modes in Chapter 4.5 §5 for a specific reason: every other bug leaves the system in a state somebody can inspect and reason about. This one leaves software with a correct-looking error report and a silently modified device.
Structurally, it means the error decision and the action must share one condition. The write_ok term carried through Chapters 4.5 to 4.8 exists for this: err_o and write_ok are computed from the same illegal term, so they cannot disagree.
Read data on an errored transfer. RULE 3.65 lists ERR_O among the signals a slave may qualify DAT_O() with, so a slave may drive something. But there is no defined value and no reason for a master to believe it — which is why Chapter 4.5's master captures on ack_i specifically, not on any termination.
3. The Integration Hazard
4. What a Master Does With One
The specification says this is supplier-defined, so what follows is engineering judgement, presented as such.
Report it upward, always. Whatever else happens, the failure must reach something that can act. An error consumed silently is worse than a hang, because a hang is at least visible.
Do not use the read data. There is no defined value.
Do not retry unchanged. An error means retrying will not help. A master that retries an error either loops forever or converts a clean failure into a hang — and there is a signal for the case where retrying would help, which is Chapter 4.12.
Abandon a multi-transfer cycle, or do not — but decide. A block that errors on its fourth transfer leaves three completed. Continuing, stopping, or unwinding are all defensible; what is not defensible is having no policy, because the caller then cannot know how much of the block happened. The masters in Chapters 4.9 and 4.10 abandon, and say so in a comment.
And document it, because RULE 2.15 requires it.
5. RTL — Producing and Handling an Error
// ─────────────────────────────────────────────────────────────────────────
// wb_err_slave — a slave whose error conditions are enumerated explicitly.
//
// The structure is the point: every error condition is a named term, err_o
// is their OR, and the write path is gated on their NOR. That makes the
// Section 2 obligation — an errored transfer changes nothing — structural
// rather than something a reviewer has to verify case by case.
//
// EVERY condition below is a DESIGN DECISION, not a specification rule.
// Documenting them is RULE 2.15's requirement, which is why they are
// enumerated as named signals rather than folded into one expression.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_err_slave #(
parameter int unsigned OFF_AW = 4,
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 [(DW/8)-1:0] sel_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 err_o,
output logic [DW-1:0] ctrl_o,
output logic [2:0] errcode_o // LOCAL diagnostic, not WB
);
localparam logic [OFF_AW-1:0] W_CTRL = 'd0; // read / write
localparam logic [OFF_AW-1:0] W_STATUS = 'd1; // read-only
localparam logic [OFF_AW-1:0] W_CMD = 'd2; // write-only
logic [DW-1:0] ctrl_q;
assign ctrl_o = ctrl_q;
logic xfer;
assign xfer = cyc_i & stb_i; // RULES 3.30 & 3.35
// ── ERROR CONDITIONS, ONE NAMED TERM EACH ──────────────────────────────
logic e_unmapped, e_ro_write, e_wo_read, e_empty_sel;
always_comb begin
unique case (adr_i)
W_CTRL, W_STATUS, W_CMD: e_unmapped = 1'b0;
default: e_unmapped = 1'b1;
endcase
end
assign e_ro_write = we_i & (adr_i == W_STATUS);
assign e_wo_read = ~we_i & (adr_i == W_CMD);
assign e_empty_sel = (sel_i == '0);
logic any_err;
assign any_err = e_unmapped | e_ro_write | e_wo_read | e_empty_sel;
// RULE 3.35: both terminations are generated from the qualified transfer.
// RULE 3.45: mutually exclusive by construction — ack_o carries ~err_o.
assign err_o = xfer & any_err;
assign ack_o = xfer & ~any_err;
// ── THE SECTION 2 OBLIGATION, MADE STRUCTURAL ──────────────────────────
// write_ok is the NOR of the same terms err_o is the OR of. They cannot
// disagree, so there is no reachable state in which this slave reports a
// failure and performs the operation.
logic write_ok;
assign write_ok = xfer & we_i & ~any_err;
always_ff @(posedge clk_i) begin
if (rst_i) begin
ctrl_q <= '0;
end else if (write_ok && (adr_i == W_CTRL)) begin
for (int unsigned n = 0; n < DW/8; n++) begin
if (sel_i[n]) ctrl_q[n*8 +: 8] <= dat_i[n*8 +: 8];
end
end
end
// ── DIAGNOSTIC CODE ────────────────────────────────────────────────────
// NOT a Wishbone signal. Wishbone's ERR_O carries no reason code — the
// master learns only that the transfer failed. Exposing a reason on a
// side-band output (or in a readable status register) is a common and
// useful extension, and calling it out as non-standard here is the
// point: a reader must not infer that Wishbone provides error codes.
always_comb begin
if (!xfer) errcode_o = 3'd0;
else if (e_unmapped) errcode_o = 3'd1;
else if (e_ro_write) errcode_o = 3'd2;
else if (e_wo_read) errcode_o = 3'd3;
else if (e_empty_sel) errcode_o = 3'd4;
else errcode_o = 3'd0;
end
always_comb begin
dat_o = '0; // RULE 3.65
if (xfer && !we_i && ack_o) begin
unique case (adr_i)
W_CTRL: dat_o = ctrl_q;
W_STATUS: dat_o = {{(DW-8){1'b0}}, 8'hA5};
default: dat_o = '0;
endcase
end
end
endmodule// ─────────────────────────────────────────────────────────────────────────
// wb_err_master — a master with an EXPLICIT, DOCUMENTED error policy.
//
// POLICY (this is what RULE 2.15 would require in the datasheet):
// * ERR_I abandons the current cycle immediately.
// * The failing address is latched and reported.
// * Read data from an errored transfer is NEVER captured.
// * An errored transfer is NEVER retried by this master; the decision
// to retry belongs to the client, which knows whether anything has
// changed that would make a second attempt different.
// * On a multi-transfer block, transfers already completed STAY
// completed and the count of them is reported.
//
// None of the above is required by Wishbone. All of it is required to be
// WRITTEN DOWN by RULE 2.15, which is the reason for this comment block.
// ─────────────────────────────────────────────────────────────────────────
module wb_err_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 failed_o,
output logic [AW-1:0] fail_adr_o,
output logic [CW-1:0] completed_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,
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;
assign cyc_o = active_q;
assign stb_o = active_q;
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;
rdat_o <= '0;
done_o <= 1'b0;
failed_o <= 1'b0;
fail_adr_o <= '0;
completed_o <= '0;
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;
completed_o <= '0;
failed_o <= 1'b0;
end
end else if (err_i) begin
// ── ERROR POLICY, EXECUTED ───────────────────────────────────────
// Abandon. Latch the failing address so the client can report
// something actionable — a bare "it failed" is not diagnosable.
// rdat_o is deliberately NOT written: there is no defined read
// value on an errored transfer (Chapter 4.5).
active_q <= 1'b0;
done_o <= 1'b1;
failed_o <= 1'b1;
fail_adr_o <= adr_q;
end else if (rty_i) begin
// Distinct from an error, and handled distinctly. Chapter 4.12.
active_q <= 1'b0;
done_o <= 1'b1;
end else if (ack_i) begin
rdat_o <= dat_i;
completed_o <= completed_o + CW'(1);
if (left_q == CW'(1)) begin
active_q <= 1'b0;
done_o <= 1'b1;
end else begin
left_q <= left_q - CW'(1);
adr_q <= adr_q + AW'(1);
end
end
end
end
endmoduleReading the pair
Purpose. The slave shows error conditions enumerated so that reporting and acting cannot diverge. The master shows a policy that is explicit enough to write into a datasheet.
Ownership. The slave drives ERR_O; the master consumes ERR_I and drives nothing in response except the negation of its qualifiers.
Combinational logic. Slave: four named error terms, their OR, two exclusive terminations, write_ok as their NOR, the diagnostic code, and a read multiplexer gated on ack_o. Master: qualifiers from active_q.
Sequential logic. Slave: one lane-gated register. Master: the active flag, address, remaining count, and the four reporting outputs.
Timing. Both terminations are combinational from the qualified transfer, so an error is reported in the cycle the transfer is presented. The master abandons on the following edge.
Qualification. err_o and ack_o both contain xfer; write_ok contains xfer and ~any_err. RULE 3.45 exclusivity holds by construction rather than by inspection.
Reset. Synchronous, active high, per RULES 2.30, 3.00 and 3.20.
Simplifications. errcode_o is non-standard and labelled as such. The master reads only, and its policy is one of several defensible ones.
Failure modes. Section 7.
6. Waveform — An Error Mid-Block
ERR_I: abandon, report, change nothing
9 cyclesCycle 2 shows RULE 3.45 holding: ERR_I asserted and ACK_I low. The two are never both asserted, and the slave in Section 5 makes that structural rather than incidental.
rdat does not change in cycle 3. The master captured 0xA5 from the first transfer and left it alone when the error arrived — there is no defined read value on an errored transfer, so there is nothing to capture.
compl stops at 1, which is what makes the failure actionable: the client learns that one transfer of three completed and which address failed. A bare failure flag would leave it unable to tell whether anything happened at all.
7. Failure Modes and Discriminating Evidence
Symptom: a system hangs on an access to an unmapped address, and the slave's ERR_O is asserted.
Candidate causes. The master does not implement ERR_I, or the signal is not connected through the interconnect.
Discriminating evidence. ERR_O asserted at the slave while STB_O stays held at the master. Conclusive, and it distinguishes this from a slave that failed to terminate — which produces an identical master-side waveform. Always probe the slave before concluding it did not answer.
Likely location. The interconnect's error merge, or the master's port list. Not the slave.
Symptom: software reports a write failed, and the register changed anyway.
Candidate causes. The slave's write condition and its error condition are computed independently and disagree.
Discriminating evidence. Write to a read-only register, confirm ERR_O, read back. Changed is conclusive.
Likely RTL location. The write enable. The fix is structural: derive write_ok from the same term err_o is derived from.
Property. P2 in Section 8.
Symptom: a master loops forever on a failing address.
Candidate causes. The master retries on ERR_I, treating it as a transient failure.
Discriminating evidence. The same address re-presented repeatedly with ERR_I each time. The distinction from a legitimate retry loop is which signal terminated it — RTY_I invites another attempt, ERR_I does not.
Likely RTL location. The master's termination branch, where err_i and rty_i share a path.
Symptom: a master acts on read data from a failed transfer.
Candidate causes. The capture is gated on any termination rather than on ACK_I.
Discriminating evidence. Errored read, then inspect the captured register. Changed is conclusive.
Property. P3.
Symptom: two slaves disagree about whether an access is legal.
Candidate causes. Not a bug in either — the conditions producing ERR_O are supplier-defined, so two slaves may legitimately differ. One acknowledges an empty byte-lane mask; another errors it.
Discriminating evidence. Read both datasheets. If either does not document its error conditions, that is a RULE 2.15 violation and the real finding.
8. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_err_checker — error properties.
//
// P1 is SPECIFICATION (RULES 3.35, 3.45). P2 and P3 are DESIGN OBLIGATIONS
// that follow from what the termination communicates rather than from a
// numbered rule — the specification does not state "an errored transfer
// changes nothing", because it does not define what an error MEANS. They
// are asserted here as this system's documented contract, which is exactly
// the material RULE 2.15 requires in the datasheet.
// ─────────────────────────────────────────────────────────────────────────
module wb_err_checker #(
parameter int unsigned DW = 32
) (
input logic clk_i,
input logic rst_i,
input logic cyc_i,
input logic stb_i,
input logic ack_o,
input logic err_o,
input logic rty_o,
input logic [DW-1:0] ctrl_q,
input logic [DW-1:0] m_rdat_q
);
default disable iff (rst_i);
// P1 — SPECIFICATION. RULE 3.35: generated from the qualified transfer.
// RULE 3.45: never more than one termination at a time.
property p_err_legal;
@(posedge clk_i) err_o |-> ((cyc_i && stb_i) && !ack_o && !rty_o);
endproperty
a_err_legal : assert property (p_err_legal)
else $error("ERR_O asserted unqualified or alongside another termination");
// P2 — DESIGN OBLIGATION. An errored transfer changes no state. This is
// the property worth having above all others in this chapter: the
// failure it catches leaves software with a correct error report
// and a silently modified device.
property p_err_changes_nothing;
@(posedge clk_i) $changed(ctrl_q) |-> !$past(err_o);
endproperty
a_err_changes_nothing : assert property (p_err_changes_nothing)
else $error("slave state changed on a transfer terminated with ERR_O");
// P3 — DESIGN OBLIGATION, master side. No read data is captured from an
// errored transfer. There is no defined value to capture.
property p_no_capture_on_err;
@(posedge clk_i) $changed(m_rdat_q) |-> !$past(err_o);
endproperty
a_no_capture_on_err : assert property (p_no_capture_on_err)
else $error("master captured read data from an errored transfer");
// P4 — DESIGN OBLIGATION. This master does not retry an error. Stated as
// a property because the alternative — an error retry loop — is a
// LIVENESS failure that no signal-level rule forbids.
property p_err_ends_the_cycle;
@(posedge clk_i) (cyc_i && stb_i && err_o) |=> !stb_i;
endproperty
a_err_ends_the_cycle : assert property (p_err_ends_the_cycle)
else $error("strobe still asserted the cycle after ERR_O");
endmoduleOnly P1 is a conformance property. P2, P3 and P4 encode a contract the specification deliberately leaves to the supplier — and that is not a weakness in them. Most real bugs violate local intent rather than the standard, and the discipline is to label which kind each property is, as Chapter 4.8 §10 argued.
P4 would be wrong for a different master. A master whose documented policy is to retry errors a bounded number of times would fail it while being perfectly conformant. That is the correct behaviour for a property encoding policy: it should fail when the policy changes, which is what makes it worth having.
Tooling limitation. Icarus has no SVA support; reviewed by inspection only.
9. Common Mistakes
"ERR_O has a standard meaning, so I know what it tells me."
Wrong mental model: the specification defines the error conditions.
Concrete bug: a master written against one slave's error semantics, connected to another with different ones — an access that errored before now succeeds, or vice versa.
Observable evidence: behaviour that changes when a peripheral is swapped for a nominally equivalent one.
Correct model: the ERR_I description says the source of the error is defined by the IP core supplier. Two slaves may legitimately disagree about the same access. RULE 2.15 requires both to document it, and the datasheet is where the answer is.
"If my master doesn't need errors, I can leave ERR_I unconnected."
Wrong mental model: an optional signal is optional independently at each end.
Concrete bug: a conformant slave asserting ERR_O to a master that cannot see it. The system hangs on the first unmapped access.
Observable evidence: a hang whose waveform says "no termination" at the master while the slave shows a termination — the most misleading signature in this module.
Correct model: optionality is a property of an interface, and connecting two interfaces is where the mismatch becomes a bug. RULE 2.15's datasheets exist so this is caught by reading rather than by debugging.
"An error is a failed write, so the value probably went in anyway."
Wrong mental model: the error is advisory.
Concrete bug: a slave whose write path is gated separately from its error decision, so the two can disagree.
Observable evidence: a register that changed despite a reported failure — and software that trusts the report.
Correct model: an errored transfer changes nothing. Make it structural by deriving the write enable from the same term the error is derived from, so the two cannot diverge no matter what is added later.
10. Interview Reasoning
First at the slave's ERR_O, before concluding the slave failed to answer — because that master-side waveform has two very different causes and they look identical.
Cause one: nobody answered. No slave decoded the address, and the interconnect has no default responder. The fix is in the interconnect.
Cause two: a slave answered on ERR_O, and the master cannot see it. The master does not implement ERR_I, or it is not merged through the fabric. The master-side picture is byte-for-byte the same, which is why Chapter 4.8's diagnosis must be extended with this probe.
One observation separates them. Probe ERR_O at every slave in the cycle the strobe is held. Asserted somewhere means cause two.
Why cause two happens between conformant parts. ERR is optional — RULE 3.40's minimum interfaces contain ACK and not ERR. A slave that generates it is conformant; a master that lacks it is conformant; the combination hangs. Neither interface is wrong, and the integration is.
Where it should have been caught. RULE 2.15 requires a slave supporting ERR_O to document the conditions that generate it, and a master supporting ERR_I to document how it reacts. Reading the two datasheets together shows a slave that emits a signal the master does not consume — on paper, before tape-out. That is the rule's entire purpose, and it is why an undocumented ERR_O is a specification violation rather than a tidiness problem.
The two fixes. Implement ERR_I at the master, or convert errors to acknowledges in the interconnect — which trades a hang for silent data loss and must be a deliberate, documented decision rather than a default.
11. Understanding Check
12. What's Next
ERR_I says no, permanently. But some refusals are not permanent — a slave whose FIFO is momentarily full, or whose second port is busy, is not failing the access so much as declining it now.
Retrying an error is a bug. Retrying this is the entire point, and the difference is one signal.
How does a slave say "not now" rather than "no" — and why is retry the one termination that can deadlock a system?
Chapter 4.12 — RTY_I answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- 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.
- Related topic
SoC Communication
Six chapters built the pieces; this one assembles them into a working fabric and traces three real accesses through it. The result works, and reading the nine unwritten rules a third party would need is what makes the case for a published protocol concrete rather than theoretical.
- 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.
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.
