Skip to content
VLSI Mentor

Wishbone · Module 8

Cycle Types

Classic B3 names three cycle types and every one of them is optional. A measured classifier names each from the bus alone — and shows what the bus cannot tell you.

Four chapters have each built one cycle type and measured it. This one puts them side by side — same counters, same rules, same questions.

How many cycle types does Classic B3 actually define, what do they share, and how do you tell from a trace which one you are looking at?

1. The Invariant Core

Start with what does not change, because it is most of the protocol and it is why a well-written slave supports cycle types its author never thought about.

Every cycle type uses the same qualification mechanism. No cycle type adds a signal, a mode bit, or a tag. The following hold identically for all five sections above:

CYC_O frames the cycle. The signal description says it is "asserted for the duration of all bus cycles" — all of them, not a subset. RULE 3.25 turns that into a requirement and names the three types it applies to.

STB_O qualifies one transfer. RULE 3.60 requires the master to qualify ADR_O, DAT_O(), SEL_O(), WE_O and the tags with it — a per-transfer obligation in every cycle type.

The slave gates on the AND. RULE 3.30 forbids a slave to respond to slave signals when CYC_I is negated, and RULE 3.35 requires ACK_O, ERR_O and RTY_O to be generated in response to the logical AND of CYC_I and STB_I.

Termination is one of three signals, never more than one at a time (RULE 3.45), and the slave qualifies its read data with them (RULE 3.65).

The consequence is worth stating plainly. A slave that implements the handshake correctly already supports every cycle type, because there is nothing cycle-type-specific for it to implement. wb_window_slave in Chapter 8.3 and wb_sem_slave in Chapter 8.4 have no block awareness and no RMW awareness between them, and both worked.

And so is the corollary. A slave that keys off CYC_I's edge — resetting per-cycle state, counting cycle starts — breaks on exactly the cycle types it was never tested against. The edge is the only thing that differs between them.

2. What Actually Differs

Four axes, and every distinction in this module falls on one of them.

SINGLEBLOCKRMW
Transfers per cycleexactly 1any number ≥ 1exactly 2
Direction within the cycleconstantconstantchanges
Relation between transfersn/aindependentthe write depends on the read
Master-side throttle usednoyes, permittedyes, for the modify

Axis 1 — transfer count. The only axis a counter sees directly, and the one Chapter 8.1's monitor was built for.

Axis 2 — direction. This is the axis that surprises people, and it deserves a careful note because the specification's own wording pulls two ways.

The descriptive text for WE_O says it "indicates whether the current local bus cycle is a READ or WRITE cycle." Read literally and applied to a bus cycle, that would make an RMW impossible.

The normative text settles it. RULE 3.60 qualifies WE_O with STB_O, not with CYC_O — a per-transfer obligation. And section 3.4 defines a cycle whose two halves differ in direction. Where a signal description and a RULE appear to disagree, the RULE governs, and here the existence of §3.4 removes any doubt about the intent.

So: direction belongs to the transfer. Treating it as a per-cycle attribute works for SINGLE and BLOCK and is simply wrong for RMW — the mis-classification Chapter 8.4 §10 describes.

Axis 3 — dependence. A block's transfers could be reordered without changing the result. An RMW's cannot. Nothing on the bus expresses this; it is a property of the master's intent, and Section 8 is about what that costs you.

Axis 4 — the master-side throttle. Chapter 8.3 introduced CYC_O high with STB_O low, and Chapter 8.4 reused it for the modify. A single cycle has no use for it, which is why PERMISSION 3.40 — a master that generates no wait states MAY drive STB_O and CYC_O from the same signal — is available to a single-cycle master and unavailable to the other two.

3. Every Cycle Type Is Optional

This is the part most engineers get wrong, and it is checkable in one minute.

Each cycle type has a rule binding interfaces that support it, and a permission releasing interfaces that do not:

Cycle typeBinding ruleOpt-out
SINGLE READ / WRITERULE 3.75 — conform to 3.2.1 / 3.2.2PERMISSION 3.50
BLOCKRULE 3.80 — conform to 3.3.1 / 3.3.2PERMISSION 3.55
RMWRULE 3.85 — conform to 3.4PERMISSION 3.60

PERMISSION 3.50 — interfaces MAY be designed so that they do not support SINGLE READ or SINGLE WRITE.

Read that again. Even the single cycle is optional. There is no cycle type a Wishbone interface is obliged to support.

So what is mandatory? The signal minimum and the handshake. RULE 3.40 requires a master to have ACK_I, CLK_I, CYC_O, RST_I and STB_O, and a slave to have ACK_O, CLK_I, CYC_I, STB_I and RST_I. The qualification and termination rules in Section 1 bind everything. Cycle types are capabilities layered on that core, declared per interface.

Which makes the DATASHEET load-bearing rather than decorative. Cycle-type support is a declared capability — the same mechanism RULE 2.15 uses to make an optional signal's meaning normative, as Chapter 4.11 describes. "It's Wishbone, so it does block transfers" is not an inference the specification supports.

And it explains the shape of every RTL module in this module. Each one supports what it needs and declares it in a header comment. None claims to support a cycle type it does not implement.

4. RTL — Naming a Cycle From the Bus Alone

If the taxonomy is real, it should be mechanisable. It is — here is a classifier that names the cycle type from the wires, with no access to any master's intent.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// wb_cycle_classifier — name the cycle type from the bus alone.
//
// SIMULATION ONLY. Not synthesisable, not part of any interface.
//
// It watches one master port and, at each cycle's end, reports which of the
// cycle types section 3 NAMES this cycle was. The classification uses only
// signals a bystander can see: CYC_O, STB_O, WE_O, ADR_O and the
// terminations. No side channel, no knowledge of the master's intent.
//
// The limits are as interesting as the capability, and Section 7 states
// them: this cannot distinguish a split RMW from two unrelated single
// cycles, because on the bus they are the same thing.
// ─────────────────────────────────────────────────────────────────────────
module wb_cycle_classifier #(
  parameter int unsigned AW = 30
) (
  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          ack_i,
  input  logic          err_i,
  input  logic          rty_i,

  output logic          valid_o,    // pulses on the clock after a cycle ends
  output int unsigned   type_o,     // see the localparams below
  output int unsigned   xfers_o,    // transfers in the cycle just ended
  output int unsigned   reads_o,
  output int unsigned   writes_o,
  output int unsigned   waits_o,    // slave-side: presented, not terminated
  output int unsigned   gaps_o,     // master-side: CYC high, STB low
  output int unsigned   clocks_o    // clocks CYC_I was asserted
);
  // The three cycle types section 3 names, split by direction where the
  // specification splits them (3.2.1/3.2.2 and 3.3.1/3.3.2).
  localparam int unsigned T_NONE         = 0;
  localparam int unsigned T_SINGLE_READ  = 1;
  localparam int unsigned T_SINGLE_WRITE = 2;
  localparam int unsigned T_BLOCK_READ   = 3;
  localparam int unsigned T_BLOCK_WRITE  = 4;
  localparam int unsigned T_RMW          = 5;
  localparam int unsigned T_UNNAMED      = 6;   // legal handshakes, no name

  logic terminated, presented, cyc_q;
  assign presented  = cyc_i && stb_i;
  assign terminated = presented && (ack_i || err_i || rty_i);

  int unsigned n_x, n_r, n_w, n_wait, n_gap, n_clk;
  logic [AW-1:0] first_adr, last_adr;
  logic same_adr, read_first, order_rw;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      cyc_q <= 1'b0; valid_o <= 1'b0; type_o <= T_NONE;
      n_x<=0; n_r<=0; n_w<=0; n_wait<=0; n_gap<=0; n_clk<=0;
      first_adr<='0; last_adr<='0; same_adr<=1'b1;
      read_first<=1'b0; order_rw<=1'b0;
      xfers_o<=0; reads_o<=0; writes_o<=0; waits_o<=0; gaps_o<=0; clocks_o<=0;
    end else begin
      cyc_q   <= cyc_i;
      valid_o <= 1'b0;

      if (cyc_i && !cyc_q) begin
        // ── CYCLE START: one rising edge of CYC is one bus cycle ──────────
        n_x<=0; n_r<=0; n_w<=0; n_wait<=0; n_gap<=0; n_clk<=1;
        same_adr <= 1'b1; read_first <= 1'b0; order_rw <= 1'b0;
        if (presented && terminated) begin
          n_x<=1; first_adr<=adr_i; last_adr<=adr_i;
          if (we_i) n_w<=1; else begin n_r<=1; read_first<=1'b1; end
        end else if (presented) begin
          n_wait<=1; first_adr<=adr_i; last_adr<=adr_i;
        end else begin
          n_gap<=1;
        end
      end else if (cyc_i) begin
        // ── INSIDE A CYCLE ────────────────────────────────────────────────
        n_clk <= n_clk + 1;
        if (!stb_i) begin
          n_gap <= n_gap + 1;                 // master-side pause
        end else if (!terminated) begin
          n_wait <= n_wait + 1;               // slave-side pause
        end else begin
          n_x <= n_x + 1;
          last_adr <= adr_i;
          if (n_x == 0) begin
            first_adr <= adr_i;
            if (!we_i) read_first <= 1'b1;
          end else if (adr_i != first_adr) begin
            same_adr <= 1'b0;
          end
          if (we_i) begin
            n_w <= n_w + 1;
            // a write immediately following the cycle's only read
            if ((n_x == 1) && read_first) order_rw <= 1'b1;
          end else begin
            n_r <= n_r + 1;
          end
        end
      end else if (!cyc_i && cyc_q) begin
        // ── CYCLE END: classify from what was counted ─────────────────────
        valid_o  <= 1'b1;
        xfers_o  <= n_x; reads_o <= n_r; writes_o <= n_w;
        waits_o  <= n_wait; gaps_o <= n_gap; clocks_o <= n_clk;

        if (n_x == 1 && n_r == 1)                        type_o <= T_SINGLE_READ;
        else if (n_x == 1 && n_w == 1)                   type_o <= T_SINGLE_WRITE;
        else if (n_x > 1 && n_w == 0)                    type_o <= T_BLOCK_READ;
        else if (n_x > 1 && n_r == 0)                    type_o <= T_BLOCK_WRITE;
        // RMW: exactly one read then one write, to the SAME address.
        else if (n_x == 2 && n_r == 1 && n_w == 1
                 && order_rw && same_adr)                type_o <= T_RMW;
        else                                             type_o <= T_UNNAMED;
      end
    end
  end
endmodule

Reading it

Purpose. To make the taxonomy executable, and to expose exactly where it runs out.

The cycle boundary is a rising edge of CYC_I. Everything else accumulates between two such edges. That single choice is what separates this from the transfer-level checkers of Modules 6 and 7.

T_UNNAMED is not an error code. It means the handshake was legal and the shape is not one section 3 names — for example a read and a write to different addresses under one cycle. I found no rule permitting or prohibiting such a cycle, so the classifier reports what it sees and makes no judgement. Section 7 measures two of them.

The RMW test is the strictest, and deliberately: exactly two transfers, read first, write second, same address. Drop the address check and a read of one word followed by a write of another would be named RMW, which would be wrong.

Simplifications. One master port, no tags, no SEL_O analysis. last_adr is captured but unused by the classification — it is there for the block-stride checks a real bus monitor would add.

5. Waveform — Three Cycle Types, One Bus

Three cycle types, told apart by counting

10 cycles
Ten clock cycles showing three consecutive bus cycles on the same master port. The first bus cycle occupies cycle one alone: one transfer, a read of word four. After one idle clock, a second bus cycle occupies cycles four and five with two read transfers, at word twelve and word thirteen. After another idle clock, a third bus cycle occupies cycles eight and nine with two transfers at the same address, word ten, the first a read and the second a write because the write enable signal rises in cycle nine. The cycle and strobe signals are identical throughout this figure because no cycle inserts a pause. Three separate rises of the cycle signal mark the three bus cycles.cycle 1: SINGLE READcycle 1: SINGLE READcycle 2: BLOCK READ, 2 transferscycle 2: BLOCK READ, 2transferscycle 3: RMW, one addresscycle 3: RMW, one addressCLK_ICYC_OSTB_OWE_OADR_O----0x4--------0xC0xD--------0xA0xAACK_It0t1t2t3t4t5t6t7t8t9
Figure 1 — a single read, a two-transfer block read and an RMW, back to back on one bus. Traced from the simulation in Section 6.

Count the rises of CYC_O: three. Cycles 1, 4 and 8. Three bus cycles, and they are three different types.

Count the acknowledges: five. One in the first cycle, two in the second, two in the third — five transfers across three cycles, which is the whole reason the two counts are kept separate.

CYC_O and STB_O are identical in this figure, because none of these three cycles inserts a master-side pause. That is a property of this trace, not of the protocol — and it is exactly the condition PERMISSION 3.40 describes. A figure in which they coincide proves nothing about whether they may.

The third cycle is an RMW and the first two transfers look identical to the second cycle's. What separates them is that ADR_O holds 0xA for both, and WE_O rises at cycle 9. Same address, read then write, one cycle.

What you cannot see anywhere in this figure is intent. Nothing marks cycle 3 as a semaphore operation rather than a coincidence.

6. Simulation — Every Shape Past One Classifier

SIM J drives twelve bus cycles directly — each an exact shape — and prints what the classifier names.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM J - every named cycle shape past one classifier ===
  single read                 SINGLE READ      xfers=1  rd=1 wr=0  waits=0 gaps=0 clocks=1
  single write                SINGLE WRITE     xfers=1  rd=0 wr=1  waits=0 gaps=0 clocks=1
  single read, 2 waits        SINGLE READ      xfers=1  rd=1 wr=0  waits=2 gaps=0 clocks=3
  block read x4               BLOCK READ       xfers=4  rd=4 wr=0  waits=0 gaps=0 clocks=4
  block read x4, 1 gap        BLOCK READ       xfers=4  rd=4 wr=0  waits=0 gaps=1 clocks=5
  block read x4, 1 wait ea    BLOCK READ       xfers=4  rd=4 wr=0  waits=4 gaps=0 clocks=8
  block write x4              BLOCK WRITE      xfers=4  rd=0 wr=4  waits=0 gaps=0 clocks=4
  RMW, same address           RMW              xfers=2  rd=1 wr=1  waits=0 gaps=0 clocks=2
  RMW with a modify gap       RMW              xfers=2  rd=1 wr=1  waits=0 gaps=1 clocks=3
  read+write, DIFFERENT adr   (no named type)  xfers=2  rd=1 wr=1  waits=0 gaps=0 clocks=2
  write then read, same adr   (no named type)  xfers=2  rd=1 wr=1  waits=0 gaps=0 clocks=2
  split RMW: first cycle      SINGLE READ      xfers=1  rd=1 wr=0  waits=0 gaps=0 clocks=1
  split RMW: second cycle     SINGLE WRITE     xfers=1  rd=0 wr=1  waits=0 gaps=0 clocks=1

Every named type was named correctly, and the numbers cross-check against the chapters that produced them: a single read is 1 clock (8.1); a four-transfer block with one wait per transfer is waits=4 (8.3's SIM D measured the same 4); an RMW is 2 clocks and 3 with a registered modify (8.4's SIM F and F(b) measured busy = 2 and 3).

Three rows deserve attention.

read+write, DIFFERENT adr → no named type. Two transfers, one read, one write, read first — every RMW criterion except the address. The classifier refuses to call it an RMW, correctly: a read of one location and a write of another is not what §3.4 defines, whatever the master meant.

write then read, same adr → no named type. Right address, right count, wrong order. §3.4 defines read-then-write. A write followed by a read is a legal pair of transfers under one cycle and is not a named cycle type.

split RMW → SINGLE READ, then SINGLE WRITE. This is the row to remember. The stimulus was a read and a write to the same address, in the right order, with the same intent as the RMW two rows above. Because CYC_O negated in between, the classifier — and the arbiter, and the slave — saw two unrelated single cycles. They are two unrelated single cycles.

Note what did not distinguish anything: waits and gaps. A block appeared with 0, 1 and 4 of them; an RMW with 0 and 1. Pause counts describe how long a cycle took, never what kind it was — the separation this module has been drawing since Chapter 8.1.

7. What the Bus Cannot Tell You

The classifier is complete for what it claims and it has three hard limits. Knowing them is more useful than the classifier.

It cannot see intent. read+write, DIFFERENT adr might be a deliberate paired access; a BLOCK READ might be four unrelated registers a driver happened to poll together. The bus carries shapes, not purposes.

It cannot recover a split RMW. Once CYC_O negates, the grouping information is gone — and it is gone for everybody, not just the monitor. That is not a limitation of the tool. It is the reason the bug in Chapter 8.4 is a bug: the information the arbiter needed was never published.

It cannot tell you whether a cycle was honoured. A master's port shows its own CYC_O held perfectly across an RMW whose grant was taken away mid-cycle. Detecting that requires watching the arbiter, as Chapter 8.4 §9 measured.

The generalisation is the one this module keeps arriving at. In Classic B3, CYC_O is the only carrier of grouping information. No transaction id, no length, no last-beat tag, no lock line. Everything you can know about how transfers relate comes from one signal — which is an argument for instrumenting it deliberately rather than inferring relationships after the fact.

8. Not in Classic B3

Four things this taxonomy does not contain, each of which arrives from a different protocol and gets imported by mistake.

No burst length and no last-beat indicator. A block ends when the master negates both qualifiers. B4's pipelined mode adds CTI_O and BTE_O tags for exactly this — a different profile, and conflating the two is the commonest error in this module.

No pipelining. Each transfer is terminated before the next is presented. There are no outstanding transactions to reorder and no ids to reorder them with.

No burst type. No wrap, no fixed, no increment mode. The master drives every address itself, and RECOMMENDATION 3.20 merely suggests sequential low-to-high ordering.

No lock signal. Discussed at length in Chapter 8.4 §2.

The specification does not use the word "burst" for these sections. It says BLOCK.

9. Failure Modes and Discriminating Evidence

Symptom: a bus monitor reports far more cycles than the driver issued.

Candidate causes. The monitor counts transfers, or STB_O clocks, and calls them cycles.

Discriminating evidence. Compare the monitor's count against CYC_O rising edges on a captured trace. If a four-transfer block reports 4, the monitor is counting terminations. If it reports 8 for the same block with one wait state each, it is counting STB_O clocks.

Likely location: the monitor's edge detection, not the design.

Symptom: a slave passes single-cycle tests and fails inside blocks or RMWs.

Candidate causes. The slave keys off CYC_I's rising edge.

Discriminating evidence. CYC_I rises once per cycle regardless of transfer count. A slave resetting per-cycle state on that edge sees one reset where it expected four. Drive the same four transfers as one block and as four singles: if only the block fails, the edge is the cause.

Likely location: any posedge-of-CYC_I logic. wb_window_slave and wb_sem_slave have none.

Symptom: a coverage model shows zero RMW cycles although the design issues them.

Candidate causes. The model keys cycle direction off WE_O sampled once per cycle.

Discriminating evidence. The same cycles appear as SINGLE READ or BLOCK READ — the direction sampled is whichever half was sampled. Cross-check the transfer count: a "single read" with two acknowledges is the tell.

Likely location: the coverage model's sampling point. The fix is to sample per transfer, per RULE 3.60.

Symptom: an integration fails because a peripheral "doesn't do block transfers".

Candidate causes. The master assumed a capability the slave never declared.

Discriminating evidence. Read both DATASHEETs. PERMISSION 3.50, 3.55 and 3.60 make every cycle type optional, so a slave that does not support blocks is conformant. The bug is the assumption, not the peripheral.

10. Common Mistakes

"Every Wishbone interface supports all the cycle types."

Wrong mental model: cycle types are part of the mandatory protocol.

What is true: all three are optional. PERMISSION 3.50 releases SINGLE, PERMISSION 3.55 releases BLOCK, PERMISSION 3.60 releases RMW. The rules that bind — 3.75, 3.80, 3.85 — each begin "interfaces that support".

Concrete bug: a master issuing block cycles to a peripheral whose datasheet declares single transfers only.

Observable evidence: the DATASHEET, which is the declaration mechanism.

Correct model: the mandatory core is RULE 3.40's signal minimum plus the handshake rules. Cycle types are declared capabilities on top.

"WE_O tells you the cycle direction."

Wrong mental model: direction is a per-cycle attribute, encouraged by the signal description's wording.

What is true: RULE 3.60 qualifies WE_O with STB_O — per transfer. Section 3.4 defines a cycle in which it changes.

Concrete bug: a monitor, scoreboard or coverage model that samples direction once per cycle. It reports every RMW as a read or a write and never both.

Observable evidence: Figure 1, cycle 9: WE_O rises inside a cycle that began at cycle 8.

Correct model: sample direction at each termination. The classifier counts reads_o and writes_o separately for this reason.

"A long cycle is a block cycle."

Wrong mental model: duration implies content.

What is true: duration and transfer count are independent. SIM J shows a single read taking 3 clocks and a four-transfer block taking 4.

Concrete bug: triage that starts from "this cycle is long, so it's a burst" and looks for a burst bug.

Observable evidence: clocks versus xfers in the SIM J table — they do not track.

Correct model: count terminations for content, and read waits and gaps for where the time went.

"Classic B3 has bursts with a last-beat signal."

Wrong mental model: imported from AHB, AXI or B4 pipelined.

What is true: Classic B3 has BLOCK cycles with no length, no last-beat indicator and no burst type. The block ends when the master negates both qualifiers.

Concrete bug: a slave that waits for a terminating tag that never arrives, or a monitor that reports every block as malformed.

Observable evidence: there is no such signal in the interface.

Correct model: CTI_O/BTE_O are B4 pipelined. Name the profile before reasoning about the signals.

"The classifier proves the master did the right thing."

Wrong mental model: a correct classification means a correct design.

What is true: the classifier names shapes. SIM J's split RMW rows are classified perfectly — as two single cycles, which is exactly what they are and exactly the bug.

Concrete bug: trusting a green bus monitor while a semaphore fails intermittently.

Observable evidence: the shape is right and the grouping is gone.

Correct model: the classifier tells you what was published on the bus. Whether that matches intent is a question for a property written against the master's own state, as in Chapter 8.4 §9.

11. Interview Reasoning

Name the profile first, then the three types, then the fact people miss.

Profile, because it changes the answer. Classic B3. B4 adds a pipelined mode with different signals, and answering across both produces a wrong list.

Three named types, and RULE 3.25 enumerates them while requiring CYC_O across each: SINGLE READ/WRITE, BLOCK, and RMW. Section 3 is organised the same way — 3.2.1 and 3.2.2 for single read and write, 3.3.1 and 3.3.2 for block read and write, 3.4 for RMW. Five sections, three names, because SINGLE and BLOCK split by direction and RMW contains both.

The fact worth adding: all three are optional. RULE 3.75, 3.80 and 3.85 each bind only interfaces that support the type, and PERMISSION 3.50, 3.55 and 3.60 each explicitly allow an interface not to. There is no cycle type a Wishbone interface must support. What is mandatory is RULE 3.40's signal minimum and the handshake rules.

Which makes the datasheet the authority for what a given core actually does — the same reason RULE 2.15 makes an optional signal's documentation normative.

And I would close on what they share, because it is why the protocol stays small: no cycle type adds a signal. CYC_O frames the cycle, STB_O qualifies the transfer, the slave gates on the AND of both, and termination is one of ACK_O, ERR_O or RTY_O. A slave that implements the handshake correctly supports every cycle type without knowing they exist.

12. Understanding Check

13. What's Next

The taxonomy is complete and mechanised, and the counters that describe it are now familiar: cycles, transfers, waits, gaps, clocks.

Those last three have been reported all module and never used for anything. They are the ingredients of a question this module has repeatedly deferred — and answered so far only with the observation that a longer cycle is not a busier one.

Given a choice of cycle type, what does each one actually cost, and when is a block transfer genuinely faster?

Chapter 8.6 — Performance Considerations measures it rather than asserting 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.