Skip to content
VLSI Mentor

Wishbone · Module 5

STB — Strobe

A transfer is presented for as long as the master waits and accepted in exactly one cycle. A slave that confuses the two performs its write once per waiting cycle, on a bus that stays perfectly conformant.

Chapter 4.8 established that STB_O is what turns bus values into a request. Chapter 5.2 framed the cycle around it. Both assumed a slave that answers immediately.

When is a transfer actually being presented, and what must remain true for as long as it is?

1. What the Strobe Must Do While Waiting

The master's obligation is short to state and easy to get wrong in RTL.

STB_O stays asserted until the transfer terminates. The strobe is not a pulse announcing a request; it is the continuous assertion of the request. OBSERVATION 3.55 gives the single-transfer form: "A MASTER that doesn't generate wait states doesn't negate [STB_O] during a transfer cycle."

Everything STB_O qualifies stays stable with it. RULE 3.60"MASTER interfaces MUST qualify the following signals with [STB_O]: [ADR_O], [DAT_O()], [SEL_O()], [WE_O], and [TAGN_O]." A transfer whose address changes while it is being presented is not one transfer.

Nothing about the wait is negotiated. The master does not know how long it will be. It does not learn. It holds.

Why the master cannot simply pulse the strobe and wait. Because the slave has nothing to remember it with. Wishbone Classic slaves are not required to latch a request — PERMISSION 3.10's slave computes its acknowledge combinationally from the inputs that are present right now. A pulsed strobe would be a request that exists for one cycle and is then gone, and a slave that needed two cycles would be answering a question that no longer exists.

2. What a Slave Sees

From the slave's side, a waited transfer is CYC_I & STB_I true for several consecutive cycles with identical address, direction and data.

The slave cannot tell how many transfers that is. Four cycles of an identical presented transfer and four back-to-back identical transfers look the same on the wires — unless the strobe drops between them, or the slave terminates.

That ambiguity is resolved entirely by the termination. OBSERVATION 3.25: "SLAVE interfaces assert a cycle termination signal in response to [STB_I]. However, [STB_I] is only valid when [CYC_I] is valid." One termination, one transfer.

So the termination is not merely an answer — it is the slave's own record of how many transfers it has handled. A slave that acts on the presented request rather than on its own termination has no such record, and Section 3 shows what that costs.

3. RTL — The Delayed Slave, Right and Wrong

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_stb_wait_slave — a slave that takes WAITS cycles to answer.
//
// PURPOSE. Make "presented" and "accepted" differ, then gate every side
// effect on ACCEPTED. This is the reference shape for any Wishbone slave
// that cannot answer in the cycle it is asked.
//
// TERMINATION TIMING. ack_o here is REGISTERED, not combinational, which
// is the form the specification itself points at for wait states:
//
//   OBSERVATION 3.45 — "...slave wait states are easiest implemented using
//   a registered [ACK_O] signal."
//
// Chapter 5.7 weighs registered against combinational termination.
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_stb_wait_slave #(
  parameter int unsigned OFF_AW = 4,
  parameter int unsigned DW     = 32,
  parameter int unsigned WAITS  = 3            // cycles before terminating
) (
  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 [DW-1:0]     ctrl_o,
  output logic [7:0]        writes_o           // side-effect COUNT, for QA
);
  localparam int unsigned NL = DW/8;
  localparam logic [OFF_AW-1:0] R_CTRL  = 'd0;
  localparam logic [OFF_AW-1:0] R_COUNT = 'd1;

  logic [DW-1:0] ctrl_q;
  assign ctrl_o = ctrl_q;

  // ── PRESENTED ─────────────────────────────────────────────────────────
  // True for EVERY cycle of the wait. RULES 3.30 and 3.35.
  logic xfer;
  assign xfer = cyc_i & stb_i;

  // ── THE WAIT COUNTER ──────────────────────────────────────────────────
  // Counts while a transfer is presented and not yet answered; resets the
  // moment it is not presented. That reset is what satisfies RULE 3.50 and
  // OBSERVATION 3.10 structurally — when STB_I goes away, so does ack_o.
  logic [7:0] waited_q;
  always_ff @(posedge clk_i) begin
    if (rst_i)       waited_q <= '0;
    else if (!xfer)  waited_q <= '0;
    else if (!ack_o) waited_q <= waited_q + 8'd1;
    else             waited_q <= '0;           // one transfer per ACK
  end

  // ── ACCEPTED ──────────────────────────────────────────────────────────
  // True for EXACTLY ONE cycle per transfer. This is the difference the
  // chapter is about. With WAITS = 0 it collapses to ack_o = xfer and the
  // distinction disappears — which is why Chapter 5.1's slave could not
  // demonstrate the bug.
  assign ack_o = xfer & (waited_q >= 8'(WAITS));

  // ── SIDE EFFECTS GATED ON ACCEPTED, NEVER ON PRESENTED ────────────────
  // write_ok contains ack_o. Replace it with plain `xfer & we_i` and this
  // slave writes ctrl_q on all four cycles of a three-wait transfer, and
  // increments writes_o four times. The master sees one normal transfer.
  logic write_ok;
  assign write_ok = xfer & we_i & ack_o & (adr_i == R_CTRL);

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      ctrl_q   <= '0;
      writes_o <= '0;
    end else if (write_ok) begin
      for (int unsigned n = 0; n < NL; n++) begin
        if (sel_i[n]) ctrl_q[n*8 +: 8] <= dat_i[n*8 +: 8];
      end
      // A deliberately NON-IDEMPOTENT side effect. An idempotent register
      // write hides a repeated update — writing the same value twice looks
      // identical to writing it once. A counter does not, which is what
      // makes the bug observable in simulation (Section 8).
      writes_o <= writes_o + 8'd1;
    end
  end

  // ── READ PATH ─────────────────────────────────────────────────────────
  // RULE 3.65: read data qualified by the termination. Here that also
  // means it is driven for exactly one cycle of a multi-cycle transfer,
  // which is precisely the window Chapter 4.5's master captures in.
  always_comb begin
    dat_o = '0;
    if (xfer && !we_i && ack_o) begin
      unique case (adr_i)
        R_CTRL:  dat_o = ctrl_q;
        R_COUNT: dat_o = {24'd0, writes_o};
        default: dat_o = '0;
      endcase
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_stb_repeat_slave — THE BUG, isolated for simulation.
//
// NOT A REFERENCE DESIGN. Identical to wb_stb_wait_slave except that the
// write is gated on PRESENTED instead of ACCEPTED:
//
//     write_ok = xfer & we_i & (adr_i == R_CTRL);     // no ack_o
//
// Everything else — the wait counter, the acknowledge, the read path, the
// reset — is unchanged and correct. The bus behaviour is fully conformant:
// one transfer presented, one termination returned. Only the side effect
// count is wrong, and only inside the slave.
// ─────────────────────────────────────────────────────────────────────────
module wb_stb_repeat_slave #(
  parameter int unsigned OFF_AW = 4,
  parameter int unsigned DW     = 32,
  parameter int unsigned WAITS  = 3
) (
  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 [DW-1:0]     ctrl_o,
  output logic [7:0]        writes_o
);
  localparam int unsigned NL = DW/8;
  localparam logic [OFF_AW-1:0] R_CTRL = 'd0;

  logic [DW-1:0] ctrl_q;
  assign ctrl_o = ctrl_q;

  logic xfer;
  assign xfer = cyc_i & stb_i;

  logic [7:0] waited_q;
  always_ff @(posedge clk_i) begin
    if (rst_i)       waited_q <= '0;
    else if (!xfer)  waited_q <= '0;
    else if (!ack_o) waited_q <= waited_q + 8'd1;
    else             waited_q <= '0;
  end

  assign ack_o = xfer & (waited_q >= 8'(WAITS));

  // ── THE BUG: gated on PRESENTED, not ACCEPTED ─────────────────────────
  logic write_bad;
  assign write_bad = xfer & we_i & (adr_i == R_CTRL);

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      ctrl_q   <= '0;
      writes_o <= '0;
    end else if (write_bad) begin
      for (int unsigned n = 0; n < NL; n++) begin
        if (sel_i[n]) ctrl_q[n*8 +: 8] <= dat_i[n*8 +: 8];
      end
      writes_o <= writes_o + 8'd1;
    end
  end

  always_comb begin
    dat_o = '0;
    if (xfer && !we_i && ack_o) dat_o = ctrl_q;
  end
endmodule

Reading the pair

Purpose. The first slave makes a master wait and still performs its side effect once. The second differs by one term and performs it once per waiting cycle.

Interface. Standard Classic slave, plus writes_o — a side-effect counter that is not a Wishbone signal. It exists so the bug is measurable; a real slave would not export it.

State. Both hold the wait counter and the application register. The counter is the only thing that makes "accepted" a distinct event.

Combinational behaviour. xfer (presented), ack_o (accepted), the write condition, the read multiplexer.

Sequential behaviour. The wait counter advances while presented and unanswered; the register updates on the write condition.

Request start. From the slave's side there is no start event — only the first cycle in which xfer is true. A slave that wants a start event must manufacture one, which is exactly what ack_o does.

Waiting. waited_q counts 0, 1, 2, then ack_o asserts at WAITS = 3. xfer is true throughout.

Termination. One cycle. The counter is cleared so a held strobe cannot produce a second acknowledge for the same transfer.

Read data. Driven only when ack_o is asserted, per RULE 3.65 — one cycle of a four-cycle transfer.

Write data. Consumed in the accepted cycle only, in the good slave.

Reset. Synchronous, active high.

Failure modes. wb_stb_repeat_slave is one; Section 7 covers the others.

Simplifications. Fixed wait count rather than a real latency source. No ERR_O/RTY_O — adding them would move this slave from PERMISSION 3.10's shortcut to PERMISSION 3.15's more general allowance. Module 9 owns wait states properly; here they are only a device for separating presented from accepted.

4. Waveform — Four Cycles Presented, One Accepted

STB: held for four cycles, accepted in one

8 cycles
Eight clock cycles showing a write transfer to a slave with three wait states. From cycle two through cycle five the master asserts both the cycle and strobe signals with write enable high, a stable address of four hundred, and stable write data. The slave's wait counter advances through zero, one and two during cycles two, three and four. In cycle five the counter has reached three so the slave asserts its acknowledge, which is the single accepted cycle. The slave's write counter increments exactly once, from zero to one, at the edge following the acknowledge. In cycle six the master has observed the acknowledge and negated both qualifiers, and the acknowledge falls with the strobe. The essential contrast is that the transfer was presented for four cycles and produced exactly one register update.presented — stays presentedpresented — stays presentedACCEPTED: the only oneACCEPTED: the only oneone write, not fourone write, not fourCLK_ICYC_OSTB_OWE_OADR_O----0x4000x4000x4000x400------------DAT_O----0x770x770x770x77------------ACK_Iwrites00000111t0t1t2t3t4t5t6t7
Figure 1 — a write with three wait states. The request is presented for four cycles; the register updates once.

Cycles 2 to 5 are all identical on the bus. Same qualifiers, same address, same direction, same data. There is nothing in those four cycles that distinguishes them from each other — which is exactly why a slave keying its side effect on them performs it four times.

Cycle 5 is the only special one, and what makes it special is generated by the slave: its own acknowledge.

writes goes 0 → 1 and stops. With wb_stb_repeat_slave the same waveform shows writes reaching 4, while CYC_O, STB_O, ADR_O, DAT_O and ACK_I are pixel-for-pixel identical. The bus cannot see the difference.

5. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_stb_checker — strobe-persistence and acceptance properties.
//
// P1 and P2 are SPECIFICATION (RULE 3.60 read across a multi-cycle
// presentation, and RULE 3.50). P3 and P4 are DESIGN OBLIGATIONS: the
// specification does not say "perform your side effect once", because it
// does not know what side effects a slave has. They are nonetheless the
// properties that catch the bug this chapter is about.
// ─────────────────────────────────────────────────────────────────────────
module wb_stb_checker #(
  parameter int unsigned AW = 30,
  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 [AW-1:0] adr_i,
  input logic [DW-1:0] dat_i,
  input logic          ack_o,
  input logic [7:0]    writes_q       // white-box side-effect counter
);
  default disable iff (rst_i);

  logic xfer;
  assign xfer = cyc_i & stb_i;

  // P1 — SPECIFICATION (RULE 3.60, as stability). While a transfer is
  //      presented and unterminated, everything STB_O qualifies holds
  //      still. The end boundary is the termination — getting that
  //      boundary wrong is the trap Chapter 4.6 Section 10 described.
  property p_request_stable_while_waiting;
    @(posedge clk_i) (xfer && !ack_o) |=> ($stable(adr_i) && $stable(we_i) &&
                                           $stable(dat_i) && $stable(stb_i));
  endproperty
  a_request_stable_while_waiting :
    assert property (p_request_stable_while_waiting)
    else $error("RULE 3.60: a qualified signal moved while the transfer waited");

  // P2 — SPECIFICATION (RULE 3.50, OBSERVATION 3.10). The termination is
  //      negated in response to the negation of STB_I. Contrapositive:
  //      no strobe, no acknowledge.
  property p_ack_follows_stb;
    @(posedge clk_i) !stb_i |-> !ack_o;
  endproperty
  a_ack_follows_stb : assert property (p_ack_follows_stb)
    else $error("RULE 3.50: ACK_O asserted with STB_I negated");

  // P3 — DESIGN OBLIGATION, and the one this chapter exists for. A side
  //      effect occurs only in an ACCEPTED cycle. Catches the repeat-slave
  //      on its FIRST extra write rather than when a counter drifts far
  //      enough for someone to notice.
  property p_side_effect_only_on_accept;
    @(posedge clk_i) $changed(writes_q) |-> $past(ack_o && we_i);
  endproperty
  a_side_effect_only_on_accept :
    assert property (p_side_effect_only_on_accept)
    else $error("side effect occurred outside an accepted transfer");

  // P4 — DESIGN OBLIGATION. At most one acceptance per presentation: the
  //      acknowledge does not stay asserted across a held strobe in THIS
  //      slave. Note this is deliberately NOT a general rule — PERMISSION
  //      3.35 allows a slave to hold ACK_O asserted, and RULE 3.55
  //      requires masters to cope. It is this slave's policy only.
  property p_one_accept_per_presentation;
    @(posedge clk_i) (ack_o && stb_i) |=> (!ack_o || !$stable(adr_i) || !stb_i);
  endproperty
  a_one_accept_per_presentation :
    assert property (p_one_accept_per_presentation)
    else $error("LOCAL: a second acceptance followed the first");
endmodule

P3 requires a white-box signal, and that is unavoidable. The bus during the repeat bug is fully conformant — one presented transfer, one termination — so no property written on the interface can detect it. The failure is entirely inside the slave, and catching it requires seeing inside.

This is the third time Module 4 and Module 5 have hit that boundary: atomicity in Chapter 4.9, the RULE 3.55 hang in Chapter 4.10, and repeated side effects here. A conformance-only verification plan misses all three.

P4 is deliberately narrow and deliberately not a rule. PERMISSION 3.35 explicitly allows a slave to hold ACK_O asserted, so a general property forbidding it would be wrong. P4 encodes what this slave does.

Tooling limitation. Icarus Verilog has no SVA support. These are reviewed by inspection; both slaves are elaborated and the repeat bug is simulated in Section 8.

6. Failure Modes and Discriminating Evidence

Symptom: a counter, FIFO or command register in a slave acts more times than software asked.

Candidate causes. A side effect gated on CYC_I & STB_I rather than on the termination.

Discriminating evidence. Compare the number of side effects against the number of cycles the strobe was held, not against the number of transfers. If the count equals the strobe's duration, that is conclusive. The correlation with wait length rather than with transfer count is unique to this bug.

Likely RTL location. The write-enable expression — a missing ack_o term.

Property. P3.

Symptom: the same slave behaves correctly in its own testbench and wrongly in the system.

Candidate causes. The same bug, invisible at zero wait states. A unit testbench with an immediate-ACK configuration never separates presented from accepted.

Discriminating evidence. Re-run the unit test with WAITS > 0. If it only fails with wait states, the bug is acceptance gating — and the lesson is that the testbench never exercised the distinction.

Likely RTL location. As above.

Symptom: an idempotent register looks fine but a neighbouring counter is wrong.

Candidate causes. Same bug. A register written four times with the same value is indistinguishable from one write; a counter incremented four times is not.

Discriminating evidence. Look for the non-idempotent state first. This is why wb_stb_wait_slave exports writes_o: idempotent side effects hide the bug, so a verification plan should deliberately target the ones that cannot.

Symptom: the slave acknowledges, then acknowledges again while the master is still dropping its strobe.

Candidate causes. The wait counter is not cleared on acceptance, so the >= WAITS comparison stays true.

Discriminating evidence. ACK_O asserted for two consecutive cycles with an unchanged address. Whether that is a bug depends on the master — PERMISSION 3.35 allows it and RULE 3.55 requires masters to cope — but for a master that counts terminations it will double-count.

Likely RTL location. The counter's clearing condition.

Property. P4.

Symptom: the address at the slave changes mid-transfer.

Candidate causes. The master drives ADR_O from a live client input rather than a latched copy.

Discriminating evidence. ADR_I changing while STB_I stays asserted and no termination has occurred — a direct RULE 3.60 violation.

Likely RTL location. The master. Chapter 5.5 builds and simulates this.

Property. P1.

7. Common Mistakes

"The slave sees STB_I, so it should do the operation."

Wrong mental model: the strobe is a trigger.

Concrete bug: a side effect gated on xfer, executing once per waiting cycle.

Observable evidence: a FIFO gaining four entries for one push, or a command firing repeatedly — with a completely normal bus waveform.

Correct model: the strobe is a continuous offer. The slave's own termination is the accept, and it happens once.

"It works, so the gating must be right."

Wrong mental model: a passing test proves the acceptance condition.

Concrete bug: a slave tested only at zero wait states, where presented and accepted coincide, then deployed behind a bus bridge that adds latency.

Observable evidence: a slave that passes its own unit tests and fails in integration — and the integration change looks unrelated to the slave.

Correct model: the distinction only exists when the slave waits. A slave's testbench must exercise a non-zero wait, even if the production configuration never waits, because the configuration can change without the RTL changing.

"The master can pulse STB_O and wait for the answer."

Wrong mental model: the request is delivered and remembered.

Concrete bug: a strobe asserted for one cycle against a slave that needs three. The slave sees the request appear and vanish.

Observable evidence: transfers that complete against fast slaves and hang against slow ones.

Correct model: Classic slaves are not required to latch anything — PERMISSION 3.10's slave is purely combinational. The request must be present when the slave is ready to answer it, which means present throughout.

8. Simulation — The Doubled Write, Observed

Both slaves were driven by Chapter 5.1's wb_hs_master performing one write to R_CTRL, with WAITS = 3.

wb_stb_wait_slavewb_stb_repeat_slave
Transfers presented by the master11
Cycles CYC & STB were asserted44
Terminations returned11
Register updates performed14
Master's viewnormalnormal

The bus traces are identical — measured, not assumed. The testbench ran both paths from the same stimulus and compared CYC, STB, WE, ACK, ADR and DAT_O on every edge:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
                         good      buggy
  transfers presented     1         1
  cycles CYC&STB held     4         4
  terminations returned   1         1
  REGISTER UPDATES        1         4
  ctrl value            0x77      0x77
  bus-trace differences   0

Zero differences across the whole transfer. A protocol checker attached to either bus passes, and the two buses are indistinguishable.

Look at the ctrl row. Both slaves end with 0x77 — the correct value. The register write is idempotent, so the corrupted slave's final state is right, and any test that writes then reads back will pass. Only writes_o, which counts rather than stores, shows the four updates. That is the concrete form of the warning in Section 6: a verification plan aimed at idempotent registers will not find this bug, and one aimed at counters, FIFOs or command strobes will find it immediately.

The master is not a witness to this failure. No amount of master-side or bus-side checking will find it.

9. Interview Reasoning

The push is gated on the request being presented rather than accepted, and the slave inserts three wait states.

The mechanism. CYC_I & STB_I is true for every cycle the master waits — four cycles for a three-wait transfer. A push gated on that term executes four times. The master, meanwhile, presented one transfer and received one acknowledge: its view is completely normal.

The confirming observation. Compare the number of pushes against the wait length, not against the number of transfers. Four pushes for a three-wait transfer, two for a one-wait transfer — the count tracking latency rather than traffic is unique to this bug. Nothing else produces that correlation.

Why it escaped testing. Almost certainly the slave was verified at zero wait states, where "presented" and "accepted" are the same cycle and the bug cannot appear. It then met latency — a bus bridge, a clock-domain crossing, a busier arbiter — and started multiplying.

Why the master cannot help. The bus is fully conformant throughout: one transfer, one termination, RULE 3.35 and RULE 3.45 both satisfied. A bus-level protocol checker passes. The failure is entirely internal to the slave, and catching it needs a white-box property — $changed(fifo_level) |-> $past(ack_o && we_i).

The fix and the habit. Add the ack_o term. Then write it that way always, including in slaves that never wait — Chapter 5.1's slave carries a redundant ack_o in its write condition for exactly this reason. The term costs nothing when it is redundant and is the whole defence when it is not.

The broader point I would make. Idempotent side effects hide this. A register written four times with the same value looks correct. So a verification plan should deliberately aim at the non-idempotent state — counters, FIFOs, command strobes — because those are the only places the bug is visible.

10. Understanding Check

11. What's Next

The strobe presents and persists; the termination accepts, once. Both halves of the master's claim are now understood in time as well as in meaning.

That leaves the slave's half of the conversation. It has been treated as a single event — the cycle where the answer arrives — but the specification has more to say about it than "the slave is done", including one requirement that constrains the master's internal design rather than any wire it drives.

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

Chapter 5.4 — ACK — Acknowledge 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.