Skip to content
VLSI Mentor

Wishbone · Module 5

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.

Chapter 4.10 established what ACK means and that it is the only mandatory termination. Chapter 5.3 showed it doing a second job: marking the one cycle in which a presented transfer is accepted.

How does the slave end the transfer, and what must the master do at that edge?

1. Two Ways to Build an Acknowledge

The specification permits both, and names the tradeoff itself.

Combinational — asserted in the same cycle the transfer is presented.

PERMISSION 3.30"The assertion of [ACK_O], [ERR_O], and [RTY_O] MAY be asynchronous to the [CLK_I] signal (i.e. there is a combinatorial logic path between [STB_I] and [ACK_O])."

OBSERVATION 3.40"The asynchronous assertion of [ACK_O], [ERR_O], and [RTY_O] assures that the interface can accomplish one data transfer per clock cycle."

Registered — asserted one or more cycles after.

OBSERVATION 3.45"The asynchronous assertion ... could proof impossible to implement. For example slave wait states are easiest implemented using a registered [ACK_O] signal."

Neither is better. The combinational form buys one transfer per clock; the registered form costs a cycle and buys timing closure. Chapter 5.7 develops the tradeoff with OBSERVATION 3.50's loopback delay, which is the reason large systems often cannot use the fast form.

What matters here is that the master cannot tell which it is talking to, and must work with both. That is the practical content of RULE 3.55.

2. Level, Not Edge

This is the chapter's central engineering point.

The master's job at every edge is to ask one question: is a termination asserted right now, while my transfer is presented? Not did the acknowledge change.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (active_q && ack_i)  begin ... end     // correct: a level, per transfer
if ($rose(ack_i))       begin ... end     // wrong: assumes a gap

Why the edge version looks reasonable. "Wait for the acknowledge" is how the handshake is described in words, and edge detection is the natural translation. The bug is structural, not careless — which is why the specification needed a rule for it.

3. What a Master Must Do At the Termination Edge

Four things happen at the edge where ACK_I is sampled asserted, and their order in the RTL does not matter but their presence does.

Capture read data, if this was a read. RULE 3.65 qualifies the slave's DAT_O with its termination, so this edge is the only opportunity. Chapter 4.5 traced the off-by-one that follows from missing it.

Record that this transfer completed, so the master can report it once and only once.

Release, or advance. A single-transfer master negates CYC_O and STB_O. A block master negates STB_O and moves the address on.

Do not capture on ERR_I or RTY_I. There is no defined read value on an error and nothing happened on a retry.

Chapter 5.6 takes the completion edge apart in full; this chapter needs only that the edge exists and is sampled as a level.

4. RTL — An Always-Acknowledging Slave and a Master That Survives It

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_ack_fast_slave — the PERMISSION 3.10 slave, exactly.
//
//   PERMISSION 3.10 — "If the SLAVE guarantees it can keep pace with all
//   MASTER interfaces and if the [ERR_I] and [RTY_I] signals are not used,
//   then the SLAVE's [ACK_O] signal MAY be tied to the logical AND of the
//   SLAVE's [STB_I] and [CYC_I] inputs."
//
// Both preconditions hold here: it never waits, and it has no ERR_O/RTY_O.
// Adding an error output would move it under PERMISSION 3.15 instead.
//
// Against a master that holds STB_O across transfers, this slave's ACK_O
// NEVER FALLS. That is conformant (PERMISSION 3.35) and is what RULE 3.55
// requires masters to tolerate.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_fast_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 [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 [7:0]        accepts_o        // side-effect count, for QA
);
  localparam int unsigned NL = DW/8;
  logic [DW-1:0] mem_q [16];

  logic xfer;
  assign xfer = cyc_i & stb_i;               // RULES 3.30 & 3.35

  // PERMISSION 3.10, literally. Note this is NOT ack_o = 1'b1: a tied-high
  // acknowledge would terminate transfers never presented to this slave,
  // violating RULE 3.35 — the distinction Chapter 5.1 Section 1 drew.
  assign ack_o = xfer;

  // Accepted == presented here, because this slave never waits. The ack_o
  // term is still written explicitly, for the reason Chapter 5.3 gave: it
  // is redundant now and load-bearing the moment a wait state appears.
  logic write_ok;
  assign write_ok = xfer & we_i & ack_o;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      for (int unsigned i = 0; i < 16; i++) mem_q[i] <= '0;
      accepts_o <= '0;
    end else if (write_ok) begin
      for (int unsigned n = 0; n < NL; n++) begin
        if (sel_i[n]) mem_q[adr_i][n*8 +: 8] <= dat_i[n*8 +: 8];
      end
      accepts_o <= accepts_o + 8'd1;
    end
  end

  always_comb begin
    dat_o = '0;                              // RULE 3.65
    if (xfer && !we_i && ack_o) dat_o = mem_q[adr_i];
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_ack_block_master — holds STB_O across N transfers and counts
// terminations by LEVEL. This is the RULE 3.55-compliant shape.
//
// It exists to be pointed at wb_ack_fast_slave, where ACK_I is asserted
// continuously and never falls. Counting levels gets N; counting edges
// gets 1. The two differ by one line, and Section 7 simulates both.
//
// SCOPE NOTE. Holding STB_O across several transfers is a BLOCK cycle and
// Module 8 owns it properly. It appears here only because it is the
// simplest construction that makes ACK_I stay asserted, which is what
// RULE 3.55 is about.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00, 3.20).
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_block_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            req_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 [DW-1:0]   last_dat_o,

  output logic            cyc_o,
  output logic            stb_o,
  output logic            we_o,
  output logic [AW-1:0]   adr_o,
  output logic [DW-1:0]   dat_o,
  output logic [DW/8-1:0] sel_o,
  input  logic [DW-1:0]   dat_i,
  input  logic            ack_i,
  input  logic            err_i,
  input  logic            rty_i
);
  logic          active_q;
  logic [AW-1:0] adr_q;
  logic [CW-1:0] left_q;

  assign cyc_o = active_q;                   // RULE 3.25: spans the block
  assign stb_o = active_q;                   // held across all transfers
  assign we_o  = 1'b0;                       // read block
  assign adr_o = adr_q;
  assign dat_o = '0;
  assign sel_o = '1;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      active_q   <= 1'b0;                    // RULE 3.20
      adr_q      <= '0;
      left_q     <= '0;
      acked_o    <= '0;
      last_dat_o <= '0;
      done_o     <= 1'b0;
    end else begin
      done_o <= 1'b0;

      if (!active_q) begin
        if (req_i && (count_i != '0)) begin
          active_q <= 1'b1;
          adr_q    <= base_i;
          left_q   <= count_i;
          acked_o  <= '0;
        end
      end else if (ack_i) begin
        // ── RULE 3.55 COMPLIANCE, IN ONE LINE ─────────────────────────
        // ack_i is read as a LEVEL, every cycle the transfer is presented.
        // Nothing here asks whether it CHANGED, so a slave holding it
        // asserted across all N transfers is counted correctly.
        //
        // The bug this avoids:   if ($rose(ack_i)) ...
        // which sees one acknowledge instead of N and never finishes.
        last_dat_o <= dat_i;                 // RULE 3.65 window
        acked_o    <= acked_o + CW'(1);
        if (left_q == CW'(1)) begin
          active_q <= 1'b0;                  // last transfer: release
          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
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_ack_edge_master — THE RULE 3.55 VIOLATION, isolated.
//
// NOT A REFERENCE DESIGN. Identical to wb_ack_block_master except that it
// advances on a RISING EDGE of ack_i instead of on the level. Against a
// slave that ever negates ACK_I between transfers it works perfectly.
// Against wb_ack_fast_slave it stalls after one transfer, with STB_O and
// ACK_I both asserted — a waveform that looks like success.
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_edge_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            req_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,

  output logic            cyc_o,
  output logic            stb_o,
  output logic            we_o,
  output logic [AW-1:0]   adr_o,
  output logic [DW-1:0]   dat_o,
  output logic [DW/8-1:0] sel_o,
  input  logic [DW-1:0]   dat_i,
  input  logic            ack_i,
  input  logic            err_i,
  input  logic            rty_i
);
  logic          active_q;
  logic [AW-1:0] adr_q;
  logic [CW-1:0] left_q;
  logic          ack_q;                      // previous ack_i

  assign cyc_o = active_q;
  assign stb_o = active_q;
  assign we_o  = 1'b0;
  assign adr_o = adr_q;
  assign dat_o = '0;
  assign sel_o = '1;

  // ── THE BUG ───────────────────────────────────────────────────────────
  // A rising edge of ack_i. RULE 3.55 requires a master to operate normally
  // when a slave HOLDS ACK_I asserted; this one cannot, because a held
  // acknowledge produces exactly one rising edge no matter how many
  // transfers complete.
  logic ack_rise;
  assign ack_rise = ack_i & ~ack_q;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      active_q <= 1'b0;
      adr_q    <= '0;
      left_q   <= '0;
      ack_q    <= 1'b0;
      acked_o  <= '0;
      done_o   <= 1'b0;
    end else begin
      done_o <= 1'b0;
      ack_q  <= ack_i;

      if (!active_q) begin
        if (req_i && (count_i != '0)) begin
          active_q <= 1'b1;
          adr_q    <= base_i;
          left_q   <= count_i;
          acked_o  <= '0;
        end
      end else if (ack_rise) begin
        acked_o <= acked_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 else if (err_i || rty_i) begin
        active_q <= 1'b0;
        done_o   <= 1'b1;
      end
    end
  end
endmodule

Reading the group

Purpose. The slave produces a legally-held acknowledge. One master survives it; the other does not.

Interface. The masters take a base address and a count and report how many transfers completed — acked_o is what makes the failure countable.

State. Masters: active flag, address, remaining count. The broken one adds ack_q, and that register is the bug.

Combinational behaviour. Slave: xfer, ack_o = xfer, the read multiplexer. Masters: qualifiers from active_q; the broken one also computes ack_rise.

Sequential behaviour. The good master advances whenever ack_i is asserted; the broken one only on a rising edge.

Request start. Both assert CYC_O and STB_O together at the edge following req_i, per PERMISSION 3.40 — the block holds them for its whole duration.

Waiting. Against this slave there is no waiting; every transfer terminates in the cycle it is presented.

Termination. Level-sampled in the good master. Edge-detected in the broken one, which is the RULE 3.55 violation.

Read data. last_dat_o captured in the acknowledged cycle, per RULE 3.65.

Reset. Synchronous, active high; active_q <= 0 negates both qualifiers per RULE 3.20.

Failure modes. Section 6, and wb_ack_edge_master is one.

Simplifications. Read-only blocks. The block construction itself belongs to Module 8; it appears here only as the simplest way to hold ACK_I asserted.

5. Waveform — One Rising Edge, Three Transfers

ACK held asserted: level counting works, edge counting does not

7 cycles
Seven clock cycles showing a three transfer block read against a slave with no wait states. The cycle and strobe signals rise together in cycle two and stay asserted through cycle four, covering all three transfers with no gap between them. Because the slave computes its acknowledge as a direct function of the qualified transfer, the acknowledge is asserted continuously from cycle two through cycle four and never returns low. The address increments each cycle from four hundred to four hundred and one to four hundred and two. The compliant master's acknowledge counter increments once per cycle reaching three, while the edge detecting master's counter reaches only one and then stalls, because a continuously asserted acknowledge presents exactly one rising edge regardless of how many transfers complete.ACK rises — the only rising edgeACK rises — the only risingedge3rd transfer, same ACK level3rd transfer, same ACKlevellevel: 3 edge: 1level: 3 edge: 1CLK_ICYC_OSTB_OADR_O----0x4000x4010x402------------ACK_Iacked (ok)0012333acked (bad)t0t1t2t3t4t5t6
Figure 1 — a three-transfer block against a never-busy slave. ACK_I never falls, and the compliant master still counts three.

There is exactly one rising edge on ACK_I and three transfers complete. That is RULE 3.55 in a single picture.

The slave is doing nothing exotic. ack_o = cyc_i & stb_i, precisely as PERMISSION 3.10 allows, and the qualified transfer never stopped being true.

The acked (bad) trace flattens at 1. In a real design that master then stalls with STB_O and ACK_I both high — and an engineer looking at the waveform sees an asserted acknowledge and concludes the transfer succeeded.

6. Failure Modes and Discriminating Evidence

Symptom: a multi-transfer operation stalls after the first transfer, with STB_O and ACK_I both asserted.

Candidate causes. The master edge-detects ACK_I — a RULE 3.55 violation — against a slave whose acknowledge does not fall.

Discriminating evidence. ACK_I asserted continuously while the master makes no progress. This is the one hang signature where the acknowledge is high; every other hang in this module shows it low. That single bit is conclusive.

Likely RTL location. The master's completion condition — $rose, an edge-detect flop, or a wait-for-negation state.

Property. P3 in Section 8.

Symptom: a block completes with fewer transfers than requested, and no error.

Candidate causes. The same edge detection, in a master whose exit condition is driven by something other than the count.

Discriminating evidence. Count the cycles where CYC & STB & ACK were all true and compare with the master's acked_o. A mismatch localises the fault to the master immediately.

Symptom: a slave acknowledges transfers addressed to other slaves.

Candidate causes. ACK_O tied high rather than tied to the qualified transfer.

Discriminating evidence. Check ACK_O while CYC_I and STB_I are low. Asserted is a direct RULE 3.35 violation.

Likely RTL location. The slave's ack_o assignment. The fix is PERMISSION 3.10's form — tied to the AND, not tied high.

Property. P1.

Symptom: the design works with one peripheral and hangs with another that is nominally equivalent.

Candidate causes. A master that happens to work against a registered-ACK slave (whose acknowledge does fall between transfers) and fails against a combinational one (whose does not).

Discriminating evidence. Compare ACK_I between the two slaves across consecutive transfers. If it falls with one and not the other, the master's edge dependence is exposed. Both slaves are conformant; the master is not.

Symptom: ACK_O remains asserted after the master drops STB_O.

Candidate causes. A registered acknowledge with no clearing path — a RULE 3.50 violation, contradicting OBSERVATION 3.10's "automatically negate".

Discriminating evidence. ACK_O high with STB_I low. In a shared fabric this corrupts the next master's transfer, so the symptom surfaces somewhere else entirely.

Property. P2.

7. Simulation — Level Versus Edge, Measured

Both masters were run against wb_ack_fast_slave requesting a three-transfer block, observed over a 40-cycle window:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
                              level      edge
  transfers requested          3          3
  cycles CYC&STB&ACK true      3          40
  rising edges on ACK_I        1          1
  TRANSFERS COUNTED            3          1
  completed                   yes        STALLED
  slave still presenting?     no         yes

Read the cycles CYC&STB&ACK true row. The compliant master saw the qualified-and-acknowledged condition three times — once per transfer — and released the bus. The edge master saw it 40 times and counted 1, because the window ended before it did; left alone it would sit there indefinitely.

That row is the stall signature in numeric form. STB_O still asserted, ACK_I still asserted, no progress. Both masters saw exactly one rising edge, which is all a held acknowledge ever offers.

The slave was byte-identical in both runs and fully conformant in both. The only difference is one line in the master — and the specification anticipated that line precisely enough to write RULE 3.55 about it.

8. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_ack_checker — acknowledge properties, both sides.
//
// P1 and P2 are SPECIFICATION (RULES 3.35 and 3.50). P3 is also
// SPECIFICATION — from RULE 3.55 — but is a LIVENESS property about the
// MASTER'S BEHAVIOUR rather than a constraint on any wire, which is why it
// needs a white-box progress signal. P4 is LOCAL POLICY.
// ─────────────────────────────────────────────────────────────────────────
module wb_ack_checker (
  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 m_progress     // white-box: master advanced a transfer
);
  default disable iff (rst_i);

  // P1 — SPECIFICATION (RULE 3.35). The acknowledge is a RESPONSE: it may
  //      only be asserted when the transfer provoking it is present.
  //      Catches a tied-high ACK_O and one derived from STB_I alone.
  property p_ack_is_a_response;
    @(posedge clk_i) ack_o |-> (cyc_i && stb_i);
  endproperty
  a_ack_is_a_response : assert property (p_ack_is_a_response)
    else $error("RULE 3.35: ACK_O asserted without CYC_I & STB_I");

  // P2 — SPECIFICATION (RULE 3.50, OBSERVATION 3.10). Terminations are
  //      negated in response to the negation of STB_I.
  property p_ack_negates_with_stb;
    @(posedge clk_i) !stb_i |-> !ack_o;
  endproperty
  a_ack_negates_with_stb : assert property (p_ack_negates_with_stb)
    else $error("RULE 3.50: ACK_O still asserted after STB_I negated");

  // P3 — SPECIFICATION (RULE 3.55), as LIVENESS. If a transfer is
  //      presented and acknowledged, the master MUST make progress.
  //
  //      This is the ONLY way to catch edge detection. During the failure
  //      every bus signal is legal — qualifiers asserted, acknowledge
  //      asserted, all rules satisfied — and what is wrong is that nothing
  //      happens. "Nothing happens" is not a signal, so the property needs
  //      to see inside the master.
  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");

  // P4 — LOCAL POLICY. The three terminations are mutually exclusive.
  //      RULE 3.45 makes this SPECIFICATION for any slave that supports
  //      ERR_O or RTY_O; wb_ack_fast_slave supports neither, so for THIS
  //      slave it is trivially true rather than a constraint. Kept so the
  //      checker can be reused against slaves that do.
  property p_terminations_exclusive;
    @(posedge clk_i) (int'(ack_o) + int'(err_o) + int'(rty_o)) <= 1;
  endproperty
  a_terminations_exclusive : assert property (p_terminations_exclusive)
    else $error("RULE 3.45: more than one termination asserted");
endmodule

P3 is the one to study. Every other property here constrains a wire and a passive monitor can check it. P3 constrains an outcome, and during the RULE 3.55 failure the bus is entirely conformant.

This is the fourth time this course has reached that boundary — atomicity in Chapter 4.9, repeated side effects in Chapter 5.3, livelock in Chapter 4.12, and this. A conformance-only plan misses all four, and Chapter 5.8 turns that observation into a checklist.

Note what makes RULE 3.55 unusual. Nearly every B3 rule constrains a signal. This one says a master "MUST be designed to operate normally when..." — an obligation on the design, not the wires. When a specification phrases a rule that way, it is telling you the corresponding check needs white-box access, and that budget belongs in the verification plan rather than being discovered when the property cannot be written.

Tooling limitation. Icarus Verilog has no SVA support and cannot execute any of these. They are reviewed by inspection; the synthesizable modules are elaborated and simulated.

9. Common Mistakes

"Wait for ACK to go high, then wait for it to go low."

Wrong mental model: the acknowledge is a pulse per transfer.

Concrete bug: edge detection, which RULE 3.55 exists to forbid.

Observable evidence: a block stalling after one transfer with STB_O and ACK_I both asserted — a waveform that reads as success.

Correct model: sample ACK_I as a level in the cycle the transfer is presented. PERMISSION 3.35 lets a slave hold it; RULE 3.55 makes coping with that the master's job.

"A slave that is always ready can tie ACK_O high."

Wrong mental model: importing valid/ready's always-ready sink.

Concrete bug: terminations for transfers never presented, including other slaves'.

Observable evidence: transfers completing that were addressed elsewhere; merged read data in a shared fabric.

Correct model: PERMISSION 3.10 gives the legal form — tied to the AND of STB_I and CYC_I. That is a response; tied high is not.

"Combinational ACK means the protocol is asynchronous."

Wrong mental model: a combinational path breaks the synchronous model.

Concrete bug: none directly — but the belief leads engineers to register every termination reflexively, adding a cycle of latency to every transfer in the system without asking whether timing required it.

Observable evidence: uniformly doubled access latency with no timing-closure justification.

Correct model: PERMISSION 3.30 explicitly allows the combinatorial path from STB_I to ACK_O, and everything is still observed at rising clock edges. What changes is a timing path, not the clocking model — Chapter 5.7 quantifies it.

10. Interview Reasoning

The master is edge-detecting ACK_I, and the slave's acknowledge never falls. That is a RULE 3.55 violation in the master.

Why the acknowledge never falls. A never-busy slave computes ack_o = cyc_i & stb_i, exactly as PERMISSION 3.10 permits. The master holds both qualifiers across the whole block, so the qualified transfer is continuously true and the acknowledge has no reason to go low. PERMISSION 3.35 explicitly allows this, and the slave is entirely conformant.

What the master does wrong. It waits for a rising edge, or for a negation before advancing. Neither arrives. It stalls, holding the strobe — and the slave, still seeing a qualified transfer, keeps the acknowledge asserted in response.

Why the waveform misleads. ACK_I high normally means success, so the instinct is that the transfer completed and the problem is downstream. The tell is that STB_O is still asserted — a completed transfer would have released it. And compared with every other hang in this module, this one is distinguished by the acknowledge being high rather than low.

Why RULE 3.55 exists at all. "Wait for the acknowledge" is how the handshake is described in prose, and translating that phrase directly into RTL produces edge detection. The bug is structural rather than careless, which is why the specification spends a rule on it — and phrases it as an obligation on the master's design rather than on any signal.

The fix, and what it buys beyond this bug. Sample the level inside the state representing an outstanding transfer. The same change makes wait states work correctly for free, because a level test is indifferent to how long the acknowledge took to arrive.

How I would catch it in verification. Not with a bus monitor — the bus is legal throughout. It needs a liveness property with a white-box progress signal: if a transfer is presented and acknowledged, the master advanced.

11. Understanding Check

12. What's Next

The three handshake signals are now understood in time: a cycle that frames, a strobe that presents and persists, an acknowledge that responds and is sampled as a level.

What has been assumed throughout is that a transfer simply starts. But a master serves a local client with its own timing, and the moment a request crosses from that client onto the bus is where a whole class of corruption enters.

Exactly what must be true for a Wishbone transfer to begin, and what must the master capture before it does?

Chapter 5.5 — Transaction Initiation answers it. 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.