Skip to content
VLSI Mentor

Wishbone · Module 10

The ERR Signal

ACK and ERR are two classes of the same event: the transfer ended. Two structurally identical accesses, both terminating in two clocks, only one of which succeeded.

Module 9 ended on a question it could not answer. A slave may insert any number of wait states, so a transfer that is merely slow and one that will never finish look identical on the bus at every finite moment. Chapter 9.5's hung trace violated nothing.

What happens when a transfer cannot complete successfully at all?

1. The Taxonomy, From the Specification

The signal description for STB_O states the classification directly, and it is worth reading as a sentence about completeness rather than about any one signal:

"The SLAVE asserts either ACK_I, ERR_I or RTY_I in response to every assertion of STB_O."

Three classes, one per presented transfer. Not "an acknowledge, plus some error flags" — three alternatives for the same event.

RULE 3.35 requires all three to be generated from the AND of CYC_I and STB_I, exactly alike. RULE 3.45 makes them exclusive:

RULE 3.45 — if a SLAVE supports ERR_O or RTY_O, it MUST NOT assert more than one of ACK_O / ERR_O / RTY_O at any time.

Note the conditional. The rule binds a slave that supports the optional terminations. A slave with only ACK_O satisfies it trivially, because it has nothing to conflict with.

ACKERRRTYTransferOperation
success100endedsucceeded
failure010endedfailed
deferral001endednot attempted — Module 11
waiting000outstandingstill running
>1 assertedRULE 3.45 violationChapter 10.2 measures one

RTY appears here to close the taxonomy and for no other reason. When and how a cycle is retried is supplier-defined and belongs to Module 11, which has not shipped. This module's master accepts RTY_I only so that it does not hang on one.

And both optional terminations are genuinely optional. PERMISSION 3.20 says master and slave interfaces may be designed to support ERR_I / ERR_O; PERMISSION 3.25 says the same for retry. A conformant Wishbone interface need support neither.

2. What ERR Means — and What the Specification Refuses to Say

The signal description is two sentences and the second one is the more important:

ERR_I"indicates an abnormal cycle termination. The source of the error, and the response generated by the MASTER, is defined by the IP core supplier."

Read the first three words: abnormal cycle termination. ERR ends the transfer. It is not a flag attached to a pending operation, and it is not a request to wait longer. The handshake is over.

Then read what the second sentence hands away. Two things that a reader might reasonably expect a bus specification to define:

Why the access failed — supplier-defined. Wishbone does not enumerate error conditions. There is no list of things that must produce an error and no encoding that says which one occurred.

What the master does about it — supplier-defined. No required recovery, no mandated abort, no retry semantics.

This is not a gap in the specification. It is a boundary. Wishbone is an interface specification for cores that must compose into systems nobody has designed yet, and the set of things that can go wrong is a property of the target, not of the bus.

The rule that stops this becoming meaningless

If both halves are supplier-defined, a bare ERR_O would communicate almost nothing. RULE 2.15 closes that, by making the supplier's definitions normative by reference:

RULE 2.15 — the WISHBONE DATASHEET MUST include, among its items: if a MASTER supports the optional ERR_I, how it reacts; if a SLAVE supports ERR_O, the conditions generating it.

So an undocumented ERR_O is a specification violation — not a documentation shortfall. The conditions are the supplier's to choose and the supplier's to publish, and a slave that errors on conditions it never wrote down is non-conformant however clean its waveforms are.

That is why every error policy in this module is stated in the RTL header, and why Section 3's slave carries its policy as a comment block rather than as a remark in the prose.

3. RTL — A Client Contract Worth Writing Down

The commonest error-handling bug is not in the bus logic. It is a master whose client interface says done and leaves "and did it work?" to be inferred.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// wb_error_aware_master — a single-outstanding master whose CLIENT CONTRACT
// separates "the transfer ended" from "the operation succeeded".
//
// THE CLIENT CONTRACT, stated here because leaving it implicit is the bug
// this chapter exists to prevent:
//
//   done_o   pulses for exactly ONE clock per accepted request, and means
//            THE TRANSFER TERMINATED. It does NOT mean the operation
//            succeeded. Every accepted request produces exactly one
//            done_o, whatever the termination class was.
//
//   ok_o     valid only in the clock done_o is asserted. High means the
//            termination was ACK_I: the operation completed successfully.
//
//   err_o    valid only in the clock done_o is asserted. High means the
//            termination was ERR_I: the operation failed.
//
//   rdat_o   valid ONLY when done_o && ok_o && the request was a read.
//            On an ERR termination its contents are not a read result and
//            the client must not consume them.
//
// ok_o and err_o are mutually exclusive by construction and exactly one is
// asserted with each done_o, so a client cannot accidentally read "done"
// as "succeeded" without also ignoring a signal that is right next to it.
//
// WHY THE CONTRACT IS SPELLED OUT. The ERR_I signal description says the
// source of the error, and the response generated by the MASTER, "is
// defined by the IP core supplier" — so none of this is protocol law. It
// is this core's documented behaviour, and RULE 2.15 requires a master
// that supports ERR_I to describe how it reacts. This comment is that
// description.
//
// RTY_I is accepted as a termination class for completeness of the
// taxonomy and is reported as a failure by this master. Retry handling is
// Module 11's subject; a master that does nothing intelligent with RTY_I
// must at least not hang on it.
// ─────────────────────────────────────────────────────────────────────────
module wb_error_aware_master #(
  parameter int unsigned AW = 30,
  parameter int unsigned DW = 32
) (
  input  logic            clk_i,
  input  logic            rst_i,

  // ── 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            busy_o,
  output logic            done_o,      // terminated (any class)
  output logic            ok_o,        // ... and it was ACK
  output logic            err_o,       // ... and it was ERR
  output logic            rty_o,       // ... and it was RTY (Module 11)
  output logic [DW-1:0]   rdat_o,      // valid only with done_o && ok_o

  // ── Wishbone side ──
  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
);
  typedef enum logic { S_IDLE, S_XFER } state_e;
  state_e state_q;

  // Request metadata, latched at acceptance. Chapter 9.3's rule: the
  // transfer's identity is fixed at the presenting edge, and the master
  // must have nothing left that can move while it waits.
  logic [AW-1:0]   adr_q;
  logic [DW-1:0]   dat_q;
  logic [DW/8-1:0] sel_q;
  logic            we_q;

  logic terminated;

  assign cyc_o = (state_q == S_XFER);
  assign stb_o = (state_q == S_XFER);
  assign adr_o = adr_q;
  assign dat_o = dat_q;
  assign sel_o = sel_q;
  assign we_o  = we_q;
  assign busy_o = (state_q != S_IDLE);

  // Any of the three classes ends the transfer. This expression is correct
  // for "did it terminate" and would be WRONG for "did it succeed" — the
  // distinction Chapter 10.2 measures with a broken master.
  assign terminated = cyc_o && stb_o && (ack_i || err_i || rty_i);

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      state_q <= S_IDLE;
      adr_q <= '0; dat_q <= '0; sel_q <= '0; we_q <= 1'b0;
      done_o <= 1'b0; ok_o <= 1'b0; err_o <= 1'b0; rty_o <= 1'b0;
      rdat_o <= '0;
    end else begin
      done_o <= 1'b0;
      ok_o   <= 1'b0;
      err_o  <= 1'b0;
      rty_o  <= 1'b0;

      case (state_q)
        S_IDLE: if (req_i) begin
          adr_q   <= req_adr_i;
          dat_q   <= req_dat_i;
          sel_q   <= req_sel_i;
          we_q    <= req_we_i;
          state_q <= S_XFER;
        end

        S_XFER: if (terminated) begin
          // ── CLASSIFY. ACK is checked first and the others are mutually
          //    exclusive with it, so a slave that violates RULE 3.45 by
          //    asserting two at once cannot make this master report two
          //    outcomes. It reports the more optimistic one, which is why
          //    Chapter 10.2 puts the exclusivity check on the BUS rather
          //    than relying on the master to notice.
          if (ack_i) begin
            ok_o <= 1'b1;
            // Read data is captured ONLY on a successful termination.
            // RULE 3.65 lets a slave qualify DAT_O() with ERR_O too, so
            // something may well be present on an error — but there is no
            // defined meaning for it, and this master does not capture it.
            if (!we_q) rdat_o <= dat_i;
          end else if (err_i) begin
            err_o <= 1'b1;
          end else begin
            rty_o <= 1'b1;
          end

          done_o  <= 1'b1;        // exactly one per accepted request
          state_q <= S_IDLE;      // and the bus is released either way
        end

        default: state_q <= S_IDLE;
      endcase
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// wb_reg_slave_err — the running peripheral, with an EXPLICIT error policy.
//
// THIS SLAVE'S ERROR POLICY. Every line of it is a design decision, not a
// Wishbone requirement. RULE 2.15 requires a slave that supports ERR_O to
// document the conditions that generate it, and this is that document:
//
//   mapped offset, legal operation      -> ACK_O
//   mapped offset, write to a read-only
//     register                          -> ERR_O        (policy)
//   offset not implemented in this
//     slave (word 7)                    -> ERR_O        (policy)
//
// A DIFFERENT SLAVE COULD LEGITIMATELY ACKNOWLEDGE ALL THREE. Wishbone
// does not say which conditions must produce an error; the ERR_I signal
// description says the source of the error "is defined by the IP core
// supplier". What the specification does require is that whatever policy
// is chosen is written down — which is what makes RULE 2.15 load-bearing
// rather than paperwork.
//
// THE OBLIGATION THAT COMES WITH AN ERROR, and the reason err_o and the
// write enable are computed from one term: an errored transfer must change
// nothing. A slave that reports failure and performs the operation anyway
// has told the master something untrue.
//
// Running register map, unchanged from Modules 6-9 except for the ID:
//   0  STATUS   RO        4  ID       RO (0x5742_1001)
//   1  COUNT    RO, free-running      7  (unmapped — kept unmapped since 6.1)
//   2  CONTROL  RW       11  SENSOR   RO, slow (9.2)
// ─────────────────────────────────────────────────────────────────────────
module wb_reg_slave_err #(
  parameter int unsigned OFF_AW      = 4,
  parameter int unsigned DW          = 32,
  parameter int unsigned WAIT_CYCLES = 0    // Module 9 semantics: N waits
) (
  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,
  input  logic [DW/8-1:0]     sel_i,
  output logic [DW-1:0]       dat_o,
  output logic                ack_o,
  output logic                err_o,

  // observation only — not part of the Wishbone interface
  output logic [DW-1:0]       ctrl_o,
  output int unsigned         writes_o
);
  localparam logic [OFF_AW-1:0] O_STATUS = 4'd0;   // RO
  localparam logic [OFF_AW-1:0] O_COUNT  = 4'd1;   // RO, free-running
  localparam logic [OFF_AW-1:0] O_CTRL   = 4'd2;   // RW
  localparam logic [OFF_AW-1:0] O_ID     = 4'd4;   // RO
  localparam logic [DW-1:0] ID_VALUE = 32'h5742_1001;

  logic [DW-1:0] ctrl_q, count_q;
  logic [7:0]    waited_q;
  logic          xfer, mapped, read_only, illegal, ready;

  // RULE 3.35: the termination is generated from the AND of CYC_I and STB_I.
  assign xfer = cyc_i && stb_i;

  assign mapped    = (adr_i == O_STATUS) || (adr_i == O_COUNT)
                  || (adr_i == O_CTRL)   || (adr_i == O_ID);
  assign read_only = (adr_i == O_STATUS) || (adr_i == O_COUNT)
                  || (adr_i == O_ID);

  // ── THE POLICY, in one term. Both the error and the write suppression
  //    are computed from it, so they cannot disagree.
  assign illegal = !mapped || (we_i && read_only);

  assign ready = (waited_q >= 8'(WAIT_CYCLES));

  // RULE 3.45: never more than one termination at a time. Here that is
  // structural — the two expressions are complements of each other under
  // the same qualification, so they cannot both be true.
  assign ack_o = xfer && ready && !illegal;
  assign err_o = xfer && ready &&  illegal;

  // RULE 3.65 qualifies DAT_O() with the termination. On an error this
  // slave drives zero: a defined value, deliberately not a plausible one.
  always_comb begin
    dat_o = '0;
    if (xfer && !we_i && !illegal) begin
      case (adr_i)
        O_STATUS: dat_o = 32'h0000_0001;
        O_COUNT:  dat_o = count_q;
        O_CTRL:   dat_o = ctrl_q;
        O_ID:     dat_o = ID_VALUE;
        default:  dat_o = '0;
      endcase
    end
  end

  assign ctrl_o = ctrl_q;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      ctrl_q <= '0; count_q <= '0; waited_q <= '0; writes_o <= '0;
    end else begin
      count_q <= count_q + 1;

      if (!xfer)       waited_q <= '0;
      else if (!ready) waited_q <= waited_q + 8'd1;
      else             waited_q <= '0;

      // COMMIT. Gated on the SAME `illegal` term that drives err_o, so an
      // errored write cannot modify state. Module 9's rule also applies:
      // gated on `ready`, so one held write is one commit.
      if (xfer && ready && !illegal && we_i && (adr_i == O_CTRL)) begin
        ctrl_q   <= dat_i;
        writes_o <= writes_o + 1;
      end
    end
  end
endmodule

Reading the pair

The master's contract is the artifact. done_o means the transfer terminated; ok_o and err_o say which class did it. One done_o per accepted request, always, whatever happened.

Why done_o deliberately does not mean success. A client that only watches done_o gets a signal that fires on every completed request, which is what it needs for flow control — when may I issue the next one. Folding success into the same bit would make flow control and error handling the same decision, and Chapter 10.2 measures what that costs.

ok_o and err_o are mutually exclusive by construction, not by convention: they are set in different arms of one if. A client cannot see both, and it cannot see neither while done_o is asserted.

The capture rule is one line and it is the whole read-data story. if (!we_q) rdat_o <= dat_i; sits inside the ack_i arm. RULE 3.65 lets a slave qualify DAT_O() with ERR_O as well as ACK_O, so something may genuinely be present on the bus at an errored termination — but there is no defined meaning for it, and this master does not take it.

Which produces a detail worth noticing in Section 5's measurement. After the errored access, rdat_o still holds the value from the previous successful read. It is stale, plausible, and wrong — the same shape as the stale-value hazard Chapter 6.1 measured. The contract is what protects the client, not the register's contents.

The slave's policy is a comment block, and that is deliberate. RULE 2.15 requires a slave supporting ERR_O to document the conditions that generate it. The header is that document, and the three conditions it lists — unmapped local offset, write to a read-only register, legal access — are this slave's decisions. A different peripheral could acknowledge all three and remain perfectly conformant.

illegal drives both the error and the write suppression. That is the structural form of "an errored transfer must change nothing": err_o and the commit condition are computed from one term, so they cannot disagree. 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.

ack_o and err_o cannot both assert. They are complements of illegal under the same qualification, so RULE 3.45 holds by construction rather than by inspection. Chapter 10.2 builds one that does not.

Timing. The slave carries Module 9's WAIT_CYCLES parameter with the same semantics — N means N wait clocks and N+1 presented clocks — so an error can be made to arrive late as easily as an acknowledge. Error and success are the same shape in time.

Reset. Active high, synchronous, consistent with the module.

Simplifications. SEL_O is ignored (Module 13 owns byte lanes). One outstanding transfer. No address decoding — Chapter 10.3 adds the minimum required to distinguish a local invalid offset from a globally unmapped one, and Module 12 owns decoding properly.

4. Waveform — Same Shape, Different Class

Two terminations, one structure

10 cycles
Ten clock cycles showing two accesses running in parallel. Both assert cycle and strobe together at cycle two and hold them through cycle three, a single wait clock each. At cycle three the first access receives an acknowledge and the second receives an error. At cycle four both masters report done for exactly one clock, the first also asserting its ok output and the second asserting its err output, and both have released the bus. The two accesses are identical in shape and differ only in which termination signal was asserted.both presented; one wait clockboth presented; one waitclockterminating edge: ACK vs ERRterminating edge: ACK vsERRboth done; only one okboth done; only one okCLK_ICYC+STBA: ACK_IA: okB: ERR_IB: errdonet0t1t2t3t4t5t6t7t8t9
Figure 1 — two accesses started on the same clock against identical slaves: a legal read of word 4, and a read of word 7 which this slave does not implement. Traced from the simulation in Section 5.

The CYC+STB row is one trace, not two. Both accesses are presented at cycle 2, both wait one clock, and both are still presented at cycle 3. Up to the terminating edge the two are indistinguishable.

Cycle 3 is the terminating edge for both. ACK_I on one, ERR_I on the other. Same edge, same qualification, same number of clocks — the difference is entirely which wire carried the answer.

Cycle 4 is where the client learns. done_o is asserted for both, because both transfers ended. ok on one and err on the other, because only one succeeded.

And both released the bus at cycle 4. CYC+STB is low. An error is a completion, so there is nothing left to hold — a master that kept presenting after an ERR_I would be waiting for an answer it had already been given.

What a trace cannot tell you from this figure alone is why word 7 failed. The bus says the transfer failed; it does not say the offset was unimplemented rather than, say, temporarily unreachable. That is error provenance, and Chapter 10.5 is about recovering it.

5. Simulation — SIM A

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM A - one request path, two termination classes ===
    slave WAIT_CYCLES=1, so each transfer is presented for 2 clocks

    access                    class   held  done  ok  err  rdat
    read  word 4  (ID)         ACK     2     1    1   0   0x57421001
    write word 0  (STATUS RO)  ERR     2     1    0   1   0x57421001

    totals    presented clocks 4
              ACK terminations 1
              ERR terminations 1
              client done      2
              client ok        1
              client err       1
              slave commits    0
              CONTROL register 0x00000000

Read the two access rows first. Both were held for 2 clocks — the slave's WAIT_CYCLES = 1 applies to errors exactly as it applies to acknowledges. Both produced done = 1. Only one produced ok = 1.

client done = 2 against client ok = 1. Two requests were accepted, two terminated, one succeeded. Those are three different counts and a client that tracks only the first cannot distinguish the other two.

slave commits = 0 and CONTROL register = 0x00000000. The errored access was a write, and it changed nothing — the illegal term suppressed the commit and raised the error from one expression.

Now the rdat column, which is the detail worth carrying forward. After the errored write it still reads 0x57421001the ID value captured by the previous successful read. The master did not capture on the error, so the register holds what it held before.

That value is stale, plausible, and wrong, and nothing about it looks suspicious. It is the contract that protects the client here, not the datardat_o is valid only when done_o && ok_o, and a client that checks done_o alone would read a real ID value as the result of a failed write.

An error is not slower than a success, and it is not faster. Both accesses cost the same two clocks. Failure has no characteristic timing signature — which is exactly why Chapter 10.4 cannot infer failure from elapsed time.

6. Failure Modes and Discriminating Evidence

Symptom: software reports a successful operation that visibly did not happen.

Candidate causes. The client contract collapses "terminated" into "succeeded".

Discriminating evidence. Compare the client's success count against the bus's ACK count. If completions exceed acknowledges, the master is reporting errors as successes. Chapter 10.2 measures exactly this.

Likely RTL location. The termination expression — ack_i || err_i used where ack_i was meant.

Symptom: a read returns a plausible but wrong value after a failed access.

Candidate causes. The client consumed rdat_o without checking ok_o, and the register holds the previous read's value.

Discriminating evidence. The returned value matches an earlier read rather than anything at the requested address. That is the signature of a stale capture register, not of a slave returning garbage.

Correct model: read data is qualified by the class of the termination, not merely by its occurrence.

Symptom: a master hangs on a slave that reports errors correctly.

Candidate causes. The master does not implement ERR_I, so the termination arrives on a wire nobody is watching.

Discriminating evidence. ERR_O asserted at the slave while the master still presents. Both interfaces can be individually conformant — ERR support is optional on both sides under PERMISSION 3.20 — and the specification records the hazard directly:

OBSERVATION 3.35 — if the SLAVE supports ERR_O or RTY_O, but the MASTER does not support these signals, deadlock may occur.

Where the fault sits: integration, and RULE 2.15 exists so that two datasheets read against each other would have shown it. Chapter 4.11 §3 works this case in full.

Symptom: a slave errors on a condition nobody expected.

Candidate causes. The slave's error policy was never written down, so the integrator's assumptions and the designer's differ.

Discriminating evidence. The datasheet. RULE 2.15 requires the generating conditions to be documented; an undocumented ERR_O is a conformance failure, and that framing usually resolves the argument faster than a waveform does.

7. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Properties for the error-aware master's CLIENT CONTRACT. Note which is
// which: the bus-level obligations are the specification's, and everything
// about done_o / ok_o / err_o is this core's own promise. Wishbone has no
// opinion about a master's client interface at all.
//
// NOTE ON EXECUTION: Icarus Verilog does not support SVA. These were
// reviewed by inspection and are NOT claimed to have been executed. The
// numbers in Section 5 come from procedural checks, which Icarus does run.
// ─────────────────────────────────────────────────────────────────────────
module wb_error_master_props #(
  parameter int unsigned DW = 32
) (
  input logic          clk_i, rst_i,
  input logic          cyc_o, stb_o, we_o,
  input logic          ack_i, err_i, rty_i,
  input logic          done_o, ok_o, err_o, rty_o,
  input logic [DW-1:0] rdat_o
);
  default clocking cb @(posedge clk_i); endclocking
  default disable iff (rst_i);

  logic terminated;
  assign terminated = cyc_o && stb_o && (ack_i || err_i || rty_i);

  // E1 — SPECIFICATION (RULE 3.45), observed from the master's side. At
  //      most one termination class is presented at a time. A slave that
  //      violates this is Chapter 10.2's broken slave; the master cannot
  //      fix it, but it can notice.
  E1_one_class: assert property (
    $onehot0({ack_i, err_i, rty_i})
  );

  // E2 — LOCAL CONTRACT. Exactly one completion per terminated transfer,
  //      and none without one. "One accepted request, one done_o."
  E2_done_follows_termination: assert property ( done_o |-> $past(terminated) );

  // E3 — LOCAL CONTRACT. done_o always carries exactly one outcome. This
  //      is what makes "done" unusable as a success signal by accident:
  //      the class is always right beside it.
  E3_exactly_one_outcome: assert property (
    done_o |-> $onehot({ok_o, err_o, rty_o})
  );

  // E4 — LOCAL CONTRACT, and the one this chapter is built on. Success is
  //      reported only for an ACK termination.
  E4_no_success_on_error: assert property ( ok_o |-> $past(ack_i) );

  // E5 — LOCAL CONTRACT. Read data is captured only on a successful
  //      termination. RULE 3.65 permits a slave to drive DAT_O() with an
  //      error, so this is a decision not to trust it, not an inference
  //      that nothing is there.
  E5_no_capture_on_error: assert property (
    $changed(rdat_o) |-> $past(cyc_o && stb_o && ack_i && !we_o)
  );

  // E6 — LOCAL CONTRACT. An error ends the transfer: the master releases
  //      rather than continuing to present. Stated as a property because
  //      the alternative is a liveness failure no signal-level rule
  //      forbids — the master would be waiting for an answer it already
  //      received.
  E6_error_releases: assert property (
    (cyc_o && stb_o && err_i) |=> !stb_o
  );
endmodule

E1 is the only specification property here, and it is written on the master's side deliberately. RULE 3.45 binds the slave, but the master is where the damage lands, and a master that watches for the violation has evidence when a peripheral misbehaves.

E2 through E6 are this core's contract. They would be wrong for a master that defines its client interface differently — one that reported success and silence, say, rather than completion and class. What such a master could not do is leave the definition unstated, because RULE 2.15 requires a master supporting ERR_I to describe how it reacts.

E4 and E5 are the pair worth keeping together. One says the client is not told success; the other says the client is not handed data. A master can get the first right and the second wrong, and then a careful client that checks ok_o still reads a register that a careless one has already corrupted.

8. Common Mistakes

"No ACK means an error."

Wrong mental model: the absence of success is failure.

What is true: it means no termination has occurred yet. The transfer is outstanding. A slave may insert any number of wait states, and Module 9 measured transfers of 1, 2, 4 and 8 clocks that were all perfectly healthy.

Concrete bug: a master that infers failure from elapsed time and abandons a slow but correct slave.

Observable evidence: the slave's latency counter still progressing — Chapter 9.5's discriminator.

Correct model: waiting, explicit error, no responder and system timeout are four different states. Chapter 10.4 separates the last two.

"ERR is ACK with an error bit."

Wrong mental model: one termination event with an attached status.

What is true: they are alternative classes of the same event. The STB_O description says the slave asserts either ACK_I, ERR_I or RTY_I, and RULE 3.45 forbids more than one at a time.

Concrete bug: a decode written as if (ack_i) { ...; if (err_i) fail(); } — which never sees an error at all, because ack_i is low when err_i is high.

Observable evidence: errors silently discarded, with the transfer appearing to hang or to succeed depending on the surrounding logic.

Correct model: one class per transfer, tested as alternatives.

"An ERR means the transfer is still pending, so the master should keep waiting."

Wrong mental model: only an acknowledge ends things.

What is true: the signal description calls ERR_I an "abnormal cycle termination". The handshake is over.

Concrete bug: a master that holds STB_O after an error, waiting for an acknowledge that will never come — converting a clean, reportable failure into a hang.

Observable evidence: STB_O still asserted on the clock after ERR_I was asserted.

Correct model: release on any class. Property E6 states it.

"Data returned with ERR is a valid read result."

Wrong mental model: the bus carried a value, so the value means something.

What is true: RULE 3.65 lists ERR_O among the signals a slave qualifies DAT_O() with, so a conformant slave may well drive something — but the specification defines no meaning for it, and the source of the error is supplier-defined.

Concrete bug: consuming rdat_o without checking ok_o. Measured here: after the errored write, rdat_o held 0x57421001, a real ID value from an earlier read.

Observable evidence: the returned value matching a previous access rather than the failed one.

Correct model: the client contract says when the data is a result. This master's says done_o && ok_o.

"Every Wishbone interface has ERR."

Wrong mental model: the termination classes are a mandatory set.

What is true: PERMISSION 3.20 makes ERR_I / ERR_O optional on both sides, and PERMISSION 3.25 does the same for retry.

Concrete bug: a slave that errors into a master with no ERR_I — two conformant interfaces that deadlock when connected, which OBSERVATION 3.35 records explicitly.

Observable evidence: ERR_O asserted at the slave while the master still presents.

Correct model: check both datasheets, which is what RULE 2.15 is for.

9. Interview Reasoning

They are two classes of the same event — the transfer ended — and they differ in what the master is being told about the operation.

The specification puts the classification in the STB_O description: the slave asserts either ACK_I, ERR_I or RTY_I in response to every assertion of STB_O. Three alternatives, one per presented transfer, and RULE 3.45 forbids a slave that supports the optional ones from asserting more than one at a time.

So ERR is not "ACK with an error bit". The signal description calls it an abnormal cycle termination — the handshake is over either way. ACK says the operation succeeded; ERR says it failed. Both release the bus and both are exactly as fast as each other — I measured two structurally identical accesses against the same slave, both presented for two clocks, differing only in which wire answered.

What I would make sure to add, because it is the part that changes how you design: the specification deliberately does not say why an access failed or what the master should do about it. The ERR_I description hands both to the IP core supplier.

And then RULE 2.15 stops that being meaningless by requiring the DATASHEET to state, for a slave, the conditions that generate ERR_O, and for a master, how it reacts to ERR_I. An undocumented ERR_O is a conformance failure, not a documentation gap — which is a useful thing to be able to say in a design review.

One distinction I would flag as separate. No termination at all is not an error. It is an absence, and at any finite clock it is indistinguishable from a slave that is simply slow.

10. Understanding Check

11. What's Next

The taxonomy is settled: three classes, one per transfer, exclusive, and a client contract that keeps "terminated" and "succeeded" apart.

This chapter's slave had one error policy and its master had one reaction, and both were correct. Neither was examined under pressure.

How should RTL generate an error — and what does it cost when a master consumes one carelessly?

Chapter 10.2 — Error Responses builds both sides, then breaks each: a master that folds ACK || ERR into success, and a slave that asserts two terminations at once. The full path is on the Wishbone curriculum index.

Continue learning

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.