Skip to content
VLSI Mentor

Wishbone · Module 9

ACK Delay

A delayed acknowledge changes a transfer's duration and nothing else. A master driving the bus from its live client asked for one register and returned another, with no error.

Chapter 9.2 built slaves that take their time. Every experiment used wb_wait_safe_master, which latches its client's request and then has nothing left that can move.

That made the slave chapters clean, and it quietly did the hardest part of the master's job.

What is a master actually obliged to do while it waits — and what breaks if its client changes its mind?

1. What "Outstanding" Obliges

RULE 3.60 is the whole of it, and it has been quoted in this course since Chapter 4.3:

RULE 3.60 — the MASTER MUST qualify the following signals with STB_O: ADR_O, DAT_O(), SEL_O(), WE_O and TAGN_O.

The force of it only becomes visible when transfers get long. STB_O is asserted for the entire outstanding transfer — one clock at zero wait states, eight clocks at seven. Whatever STB_O qualifies must be stable for exactly as long as STB_O is asserted, so the stability obligation stretches with the latency.

For a write the specification is more explicit still. The SINGLE WRITE timing says the master's ADR_O, DAT_O(), WE_O and SEL_O() are valid from the presenting edge and remain valid "until the rising CLK_I edge following negation of STB_O".

Read that carefully, because it is stronger than a naive reading of RULE 3.60. The payload must still be coherent at the edge that ends the transfer, not merely up to it. Chapter 7.4 rests on that sentence; Module 9 simply makes the interval arbitrarily long.

What the rule does not say is equally worth stating. It says nothing about where the master keeps those values. Registers, a FIFO output, a constant, or a live wire from somewhere else are all permitted — so long as the result is stable. Section 5's broken master satisfies the letter of "drive these signals" and fails the substance of "hold them stable", which is precisely why it compiles, elaborates and passes casual review.

Termination is still a level

One carry-over deserves an explicit mention, because wait states are where it is most tempting to get wrong.

ACK_I is a level sampled at each clock edge, not an event to detect. Chapter 5.4 established this and measured the consequence; the relevant point here is that a long stretch of ACK_I low followed by one clock high looks exactly like an edge, and a master written as if ($rose(ack_i)) will work perfectly in every simulation in this chapter.

It breaks against a slave that holds ACK_O asserted — which PERMISSION 3.35 explicitly allows, and which RULE 3.55 requires masters to cope with:

RULE 3.55 — MASTER interfaces MUST be designed to operate normally when the SLAVE interface holds ACK_I asserted.

So edge-detection is a bug that wait states hide rather than expose. wb_wait_safe_master samples the level: terminated = cyc_o && stb_o && (ack_i || err_i || rty_i).

2. RTL — Two Masters, One Line Apart

The correct master was published in Chapter 9.1 §3 and is reproduced here in full, because the comparison is the chapter.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// wb_wait_safe_master — a master that is correct under ANY response latency.
//
// The design rule it embodies is one line: the Wishbone request is driven
// from REGISTERS that were loaded when the client request was accepted, and
// nothing downstream of those registers can change until the transfer
// terminates.
//
// That is what makes it safe. It is not that the master "waits properly" —
// waiting requires no effort at all. It is that the master has nothing left
// that CAN move while it waits.
//
// RULE 3.60 requires ADR_O, DAT_O(), SEL_O, WE_O and the tags to be
// qualified by STB_O. Section 3.2.2 is stronger for a write: the payload is
// valid from the presenting edge and remains valid "until the rising CLK_I
// edge following negation of STB_O". Driving any of them from a live client
// input satisfies neither, and Chapter 9.3 measures what that costs.
// ─────────────────────────────────────────────────────────────────────────
module wb_wait_safe_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,        // one pulse per accepted request
  output logic            err_o,
  output logic [DW-1:0]   rdat_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;

  // ── THE LATCHES. Loaded once, at acceptance; read-only thereafter. ──────
  logic [AW-1:0]   adr_q;
  logic [DW-1:0]   dat_q;
  logic [DW/8-1:0] sel_q;
  logic            we_q;
  logic            terminated;

  // One transfer per cycle, so CYC_O and STB_O coincide here. They are still
  // written as two expressions: Chapter 8.3 showed why they are not the same
  // signal in general, and PERMISSION 3.40's shortcut is a licence, not a
  // requirement.
  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);

  // Termination is a LEVEL sampled at the clock edge, not an edge to detect.
  // Chapter 5.4 settled this; a wait state changes nothing about it.
  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; err_o <= 1'b0; rdat_o <= '0;
    end else begin
      done_o <= 1'b0;
      case (state_q)
        S_IDLE: if (req_i) begin
          // ── ACCEPTANCE. The client's request is copied here and the copy
          //    is what reaches the bus. After this edge the client may do
          //    whatever it likes; Chapter 9.3 has it do exactly that.
          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: begin
          // Nothing is assigned to adr_q / dat_q / sel_q / we_q here. That
          // absence IS the stability guarantee, and it holds for a 1-clock
          // transfer and a 50-clock transfer identically.
          if (terminated) begin
            // Capture at the TERMINATING edge, and only then. RULE 3.65 says
            // the slave qualifies DAT_O() with its termination signal, so
            // this is the only edge at which read data is guaranteed valid.
            if (!we_q) rdat_o <= dat_i;
            err_o   <= err_i;
            state_q <= S_IDLE;
            done_o  <= 1'b1;          // exactly one pulse per accepted request
          end
        end

        default: state_q <= S_IDLE;
      endcase
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// wb_passthrough_master — THE BUG, isolated. NOT A REFERENCE DESIGN.
//
// Identical to wb_wait_safe_master in every respect except one: the request
// metadata is driven COMBINATIONALLY from the live client inputs instead of
// from registers latched at acceptance.
//
//     assign adr_o = adr_q;        becomes      assign adr_o = req_adr_i;
//
// The sequencing, the capture edge, the done pulse and the termination
// detection are all unchanged and all correct. The only defect is that the
// master has kept a path from its client straight onto the bus.
//
// Against a zero-wait slave the two masters are INDISTINGUISHABLE: the
// transfer is presented and terminated in the same clock, so there is no
// interval during which the client could change anything. The bug needs
// wait states to exist at all.
//
// What it violates: RULE 3.60 requires the MASTER to qualify ADR_O, DAT_O(),
// SEL_O(), WE_O and the tags with STB_O. STB_O is asserted for the whole
// outstanding transfer, so those signals are obliged to be stable for the
// whole outstanding transfer. This master cannot make that promise, because
// it does not own the values it is driving.
// ─────────────────────────────────────────────────────────────────────────
module wb_passthrough_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            busy_o,
  output logic            done_o,
  output logic            err_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
);
  typedef enum logic { S_IDLE, S_XFER } state_e;
  state_e state_q;
  logic   terminated;

  assign cyc_o = (state_q == S_XFER);
  assign stb_o = (state_q == S_XFER);

  // ── THE BUG. These four lines are a wire from the client to the bus. ────
  assign adr_o = req_adr_i;
  assign dat_o = req_dat_i;
  assign sel_o = req_sel_i;
  assign we_o  = req_we_i;

  assign busy_o     = (state_q != S_IDLE);
  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; done_o <= 1'b0; err_o <= 1'b0; rdat_o <= '0;
    end else begin
      done_o <= 1'b0;
      case (state_q)
        // Nothing is latched here. There is nothing TO latch — the bus is
        // already being driven from the client's outputs.
        S_IDLE: if (req_i) state_q <= S_XFER;

        S_XFER: if (terminated) begin
          if (!req_we_i) rdat_o <= dat_i;
          err_o   <= err_i;
          state_q <= S_IDLE;
          done_o  <= 1'b1;
        end

        default: state_q <= S_IDLE;
      endcase
    end
  end
endmodule

Reading the pair

The difference is four assign statements. One master drives the bus from registers loaded at acceptance; the other drives it from the client's live outputs. Everything else — the state machine, the termination detection, the capture edge, the done_o pulse — is identical.

The correct master's guarantee is an absence. Look at its S_XFER state: nothing is assigned to adr_q, dat_q, sel_q or we_q. That absence is the stability guarantee, and it holds identically for a one-clock transfer and a fifty-clock one. There is no "hold" logic to get wrong because there is no path by which the values could change.

This is the design idea worth taking away. The correct master is not correct because it waits carefully. It is correct because it has nothing left that can move. Safety by construction beats safety by discipline, especially in logic that only misbehaves under latency nobody tested.

The broken master's defect is not laziness. Omitting four registers is a real saving in a wide-address, wide-data design, and the result is a master that works flawlessly against every zero-wait slave. It is a design that has outsourced a protocol obligation to its client without saying so.

Why the bug is invisible at zero wait states. The transfer is presented and terminated in the same clock, so there is no interval during which the client could change anything. The two masters are bit-identical until a slave inserts a wait state — the same structural precondition that hid the defects in Chapter 6.2, Chapter 7.4, Chapter 8.3 and Chapter 9.2.

One honest note about rdat_o in the broken master. It captures at the terminating edge, correctly, and it tests !req_we_i — the live client signal — to decide whether to capture. Even its capture logic is contingent on the client having not moved.

3. Waveform — Same Request, Two Addresses

The client moves; one master follows it

10 cycles
Ten clock cycles comparing two masters driven by the same client. The cycle and strobe signals rise at cycle two and stay asserted through cycle five for both. The client's own address input reads word four through cycle four and changes to word zero at cycle five. The safe master's address output holds word four for all four presented clocks. The passthrough master's address output follows the client and changes to word zero at cycle five, which is the terminating clock. The acknowledge rises at cycle five for both. At cycle six the safe master's captured data is the identifier value and the passthrough master's is the status value, one for each of the two different addresses that were presented at the terminating edge.both present word 4both present word 4client moves ON the terminating edgeclient moves ON theterminating edgetwo different values capturedtwo different valuescapturedCLK_ICYC+STBclient adr0x40x40x40x40x40x00x00x00x00x0ADR_O safe--------0x40x40x40x4----------------ADR_O pass0x40x40x40x40x40x0----------------ACK_Irdat safe000000IDIDIDIDrdat pass000000STATSTATSTATSTATt0t1t2t3t4t5t6t7t8t9
Figure 1 — the client asks for word 4 and switches its address input to word 0 at cycle 5, while a 3-wait slave is still working. Traced from the simulation in Section 4.

Follow the client adr row first. It reads 0x4 while the client wants the ID register, and changes to 0x0 at cycle 5. The client has done nothing wrong — it issued a request, the master accepted it, and it moved on to thinking about its next one. Nothing in the Wishbone specification governs a master's internal client interface.

ADR_O safe ignores it. Word 4 for all four presented clocks, because the value on the bus is a copy taken at acceptance and the client has no path to it.

ADR_O pass follows it, changing at cycle 5 — which is the terminating clock. The slave decodes whatever is presented at that edge, sees word 0, and returns STATUS.

That timing is the worst case and it is not a coincidence. The change could have landed on any of the four presented clocks; landing on the terminating one means the wrong address is the one that decides the answer. A change on cycle 3 or 4 would still be a RULE 3.60 violation and might still produce a wrong result depending on the slave's style — Chapter 9.2's capturing slave would have latched word 4 at acceptance and been immune, which is exactly the coupling that chapter described.

Both masters report one transfer and no error. ACK_I rises once for each; neither sees ERR_I. The protocol is satisfied from the slave's point of view in both cases — it was asked for word 0 and it answered word 0, correctly.

The two rdat rows are the damage. One client asked for ID and got ID. The other asked for ID and got STATUS, with no indication that anything went wrong.

4. Simulation — SIM F

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM F - the client moves while the transfer is outstanding ===
    client asks for word 4 (ID = 0x57420901),
    then switches its address input to word 0 (STATUS = 0x00000001)
    two clocks later, while the 3-wait slave is still working.

    correct master   address at termination   0x4
                     value returned           0x57420901
                     metadata changes while outstanding   0
                     transfers                1
    passthrough      address at termination   0x0
                     value returned           0x00000001
                     metadata changes while outstanding   1
                     transfers                1

Read the two value returned lines. The correct master returned 0x57420901 — the ID register, which is what its client asked for. The passthrough master returned 0x00000001, which is STATUS.

The client asked for one register and received another's contents. There is no error, no retry, no flag. err_o is low on both masters and both report transfers = 1.

metadata changes while outstanding is the line that assigns blame. Zero for the correct master, one for the broken one. That counter is a direct RULE 3.60 check — it compares the presented metadata against the previous clock's, but only while a transfer was outstanding — and it is the single number that says the master is at fault rather than the slave.

Why that matters more than the wrong value. A wrong value is a symptom that could come from anywhere: a decode bug, a slave bug, a data-path bug, a testbench bug. A non-zero stability count is conclusive and local. It names the master and it names the rule.

Both masters were driven by the same client stimulus, and both were paired with the same conformant slave. The slave did nothing wrong in either runChapter 9.1's Style A slave reads adr_i live, which is sound precisely because RULE 3.60 promises it will be stable. One master kept that promise; the other did not.

And the failure is silent at zero wait states. Re-run the same stimulus against a WAIT_CYCLES = 0 slave and there is no interval for the client to move in: the transfer is presented and terminated in the same clock. The two masters produce identical results, and the test passes.

5. Failure Modes and Discriminating Evidence

Symptom: a read returns the contents of a different register than the one requested.

Candidate causes. Three, and one observation separates them.

Discriminating evidence. The wait monitor's unstable count. Non-zero means the master moved its metadata mid-transfer — a RULE 3.60 violation, and conclusive. Zero means look elsewhere: either the address decode is wrong, or the master's client interface delivered the wrong address in the first place.

Likely RTL location. A combinational path from a client input to a bus output, which is often a single assign.

Symptom: a design works in unit test and returns wrong data in the system.

Candidate causes. The unit test used a zero-wait slave, so no transfer was ever outstanding long enough for anything to move.

Discriminating evidence. Re-run the failing case with WAIT_CYCLES = 0. If it passes, the defect is latency-dependent, and metadata stability is the first thing to check. This is a cheap and decisive triage step.

Symptom: a write commits the wrong payload, intermittently.

Candidate causes. Same class, applied to DAT_O(). The write payload must remain coherent at the terminating edge, per the SINGLE WRITE timing — not merely until shortly before it.

Discriminating evidence. Compare the payload at the presenting edge against the payload at the terminating edge. The monitor checks dat_i only when we_i is asserted, precisely so a read's undefined DAT_O does not raise false alarms.

Symptom: a master hangs against a slave that holds ACK_O asserted.

Candidate causes. Termination detected as an edge rather than a level.

Discriminating evidence. ACK_I already high when STB_O rises. An edge-detecting master never sees a rising edge and waits forever. PERMISSION 3.35 allows the slave to do this and RULE 3.55 requires the master to cope, so the fault is the master's. Chapter 5.4 measured this directly.

Symptom: a master re-presents a request it has already issued.

Candidate causes. Treating a delayed acknowledge as a failure and retrying.

Discriminating evidence. STB_O negating and re-asserting with no termination in between, and a slave-side latency counter that repeatedly climbs and resets without reaching its target. A transfer that is restarted can never complete against any slave slower than the retry interval.

6. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Properties for a Wishbone master under arbitrary response latency.
// These are written from the master's own port, which is deliberate: they
// are the obligations a master can be held to without knowing anything
// about the slave it is talking to.
//
// 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 4 come from procedural checks, which Icarus does run.
// ─────────────────────────────────────────────────────────────────────────
module wb_master_wait_props #(
  parameter int unsigned AW = 30,
  parameter int unsigned DW = 32
) (
  input logic            clk_i, rst_i,
  input logic            cyc_o, stb_o, we_o,
  input logic [AW-1:0]   adr_o,
  input logic [DW-1:0]   dat_o,
  input logic [DW/8-1:0] sel_o,
  input logic            ack_i, err_i, rty_i,
  input logic            done_o
);
  default clocking cb @(posedge clk_i); endclocking
  default disable iff (rst_i);

  logic outstanding;
  // Presented and not terminated: exactly the interval RULE 3.60 governs.
  assign outstanding = cyc_o && stb_o && !(ack_i || err_i || rty_i);

  // P1 — SPECIFICATION (RULE 3.25). CYC_O is asserted no later than the
  //      edge that qualifies STB_O. Unchanged by latency.
  P1_cyc_qualifies: assert property ( stb_o |-> cyc_o );

  // P2..P4 — SPECIFICATION (RULE 3.60). The qualified master outputs are
  //      stable while a transfer is outstanding. This is THE property of
  //      this chapter, and it is what wb_passthrough_master fails.
  P2_adr_stable: assert property ( outstanding |=> $stable(adr_o) );
  P3_we_stable:  assert property ( outstanding |=> $stable(we_o)  );
  P4_sel_stable: assert property ( outstanding |=> $stable(sel_o) );

  // P5 — SPECIFICATION (RULE 3.60, and the SINGLE WRITE timing, which
  //      requires the payload to remain valid until the edge following
  //      negation of STB_O). Checked only for writes: DAT_O is undefined
  //      during a read and flagging it would be a false positive.
  P5_wdata_stable: assert property ( (outstanding && we_o) |=> $stable(dat_o) );

  // P6 — LOCAL POLICY. One accepted client request produces exactly one
  //      completion report, whatever the latency was. Wishbone has no
  //      opinion about a master's client interface; this is a contract
  //      this master offers. Chapter 9.4 generalises it across the matrix.
  P6_one_done: assert property (
    done_o |-> $past(cyc_o && stb_o && (ack_i || err_i || rty_i))
  );
endmodule

P2 through P5 are the chapter, and they are pure specification. They restate RULE 3.60 as four checkable obligations, and they are written against the outstanding window rather than against the whole cycle — which is what makes them usable. A property that demanded stability whenever CYC_O was asserted would fire on every legitimate move to the next transfer in a block cycle.

P5's we_o guard is not caution, it is correctness. Chapter 9.1's slave and the SINGLE READ timing figure both leave DAT_O undefined during a read. Asserting stability on it would make a conformant master fail.

What these properties deliberately do not include is an obligation about detecting termination as a level rather than an edge. That failure mode is real — PERMISSION 3.35 lets a slave hold ACK_O asserted and RULE 3.55 requires masters to cope — but it shows up as a hang, and a liveness property written from these pins alone cannot distinguish "hung master" from "very slow slave". The check that finds it is a testbench with an always-acknowledging slave, which is what Chapter 5.4 built.

P6 is local policy and says so. "One completion report per accepted request" is this master's contract with its client, and Wishbone governs none of it.

7. Common Mistakes

"Nothing has completed yet, so the address can still change."

Wrong mental model: an unterminated transfer is provisional.

What is true: it is outstanding, not pending. RULE 3.60 qualifies the address with STB_O, and STB_O is asserted for every clock of the wait.

Concrete bug: wb_passthrough_master. Measured: the client asked for word 4 and the master returned word 0's contents, with one transfer and no error.

Observable evidence: metadata changes while outstanding = 1 on the wait monitor.

Correct model: the transfer's identity is fixed at the presenting edge. Only its duration is still open.

"Driving the bus straight from the client saves registers and is equivalent."

Wrong mental model: stability is about the values, not about who owns them.

What is true: the master is accountable for RULE 3.60 whether or not it owns the source. Driving from a live client outsources a protocol obligation without a contract.

Concrete bug: four assign statements, and a master that is correct against every zero-wait slave.

Observable evidence: the identical S_XFER states of the two masters — the difference is entirely in what feeds the outputs.

Correct model: latch at acceptance. The guarantee is then an absence of logic rather than a discipline.

"A delayed ACK means something went wrong."

Wrong mental model: silence is failure.

What is true: it is the normal progress of a transfer that is still running. The specification's own words are that the slave "may insert wait states" to "throttle the cycle speed".

Concrete bug: a master that de-asserts and re-presents after a timeout of its own, restarting the slave's latency counter each time. The transfer can never complete against any slave slower than the retry interval.

Observable evidence: a slave-side counter that repeatedly climbs and resets without reaching its target.

Correct model: hold and wait. A failed transfer in Wishbone is ERR_I; a deferred one is RTY_I. Module 10 and Module 11 own those.

"$rose(ACK_I) is a fine way to detect completion."

Wrong mental model: termination is an event.

What is true: it is a level, sampled at the clock edge. PERMISSION 3.35 lets a slave hold ACK_O asserted and RULE 3.55 requires masters to work anyway.

Concrete bug: a master that hangs against an always-acknowledging slave — and which passes every test in this chapter, because a delayed ACK does produce a rising edge.

Observable evidence: ACK_I already high when STB_O rises.

Correct model: sample the level, as Chapter 5.4 measured.

8. Interview Reasoning

Everything that STB_O qualifies, for as long as STB_O is asserted — and the second half of that sentence is where wait states change the picture.

The rule is RULE 3.60: the master qualifies ADR_O, DAT_O(), SEL_O(), WE_O and the tags with STB_O. At zero wait states that is a one-clock obligation and it is hard to violate. At seven wait states it is an eight-clock obligation, and now it takes real design to honour.

For writes the specification is more specific still. The SINGLE WRITE timing says the payload is valid from the presenting edge and stays valid until the rising edge following negation of STB_O — so it must still be coherent at the terminating edge, not merely up to it.

Why the obligation exists. The slave may be reading those signals live. That is a legitimate implementation — it is relying on a stated promise — so a master that breaks the promise corrupts a slave that did nothing wrong.

What I measured. Two masters, same client, same slave, three wait states. The correct one latches the request at acceptance; the broken one drives the bus from the client's live outputs. When the client changed its address mid-transfer, the broken master returned the contents of a completely different registerSTATUS instead of ID — with one transfer, no error and nothing on the bus to suggest a problem.

The design conclusion I would give. Latch the request at acceptance. Then the guarantee is an absence of logic rather than a discipline — there is simply no path by which the values could change, and that holds for a one-clock transfer and a fifty-clock one identically.

And the thing I would add about testing, because it is why this bug ships: at zero wait states the two masters are bit-identical. The defect is unreachable unless the suite includes a slave that waits.

9. Understanding Check

10. What's Next

Both sides now have their obligations. A slave may take as long as it needs and must not act more than once; a master must hold what it presented and must not capture early.

Each chapter has demonstrated this on one operation at a time — a read here, a write there, one latency per experiment.

If a transfer can be stretched to any length, what exactly is preserved across all of those lengths?

Chapter 9.4 — Transaction Extension runs the same read and the same write across a matrix of delays and measures what stays constant. 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.