Wishbone · Module 8
Single Read Cycle
One transfer, one bus cycle — and a monitor that counts them separately. That the two numbers agree here is a property of this master, not a law of the protocol.
Module 6 followed a read from a client request to a captured value, and Module 7 followed a write to a committed register. Both worked at the level of one transfer.
Module 8 changes the unit of analysis.
What makes Module 6's read a complete Wishbone bus cycle, and not merely a read transaction?
1. What RULE 3.25 Actually Says
The cycle signal has one normative requirement, and it names three cycle types:
RULE 3.25 — "MASTER interfaces MUST assert [CYC_O] for the duration of SINGLE READ / WRITE, BLOCK and RMW cycles. [CYC_O] MUST be asserted no later than the rising [CLK_I] edge that qualifies the assertion of [STB_O]."
Read the list. SINGLE, BLOCK, RMW — three cycle shapes, one signal framing all of them. A rule that only ever had to cover single transfers would not need to say "for the duration of".
And the CYC_O signal description spells out why:
"The cycle output [CYC_O], when asserted, indicates that a valid bus cycle is in progress. The signal is asserted for the duration of all bus cycles. For example, during a BLOCK transfer cycle there can be multiple data transfers. The [CYC_O] signal is asserted during the first data transfer, and remains asserted until the last data transfer."
That sentence is the whole of Module 8 in miniature. One cycle, many transfers, CYC_O spanning from the first to the last.
What this chapter establishes is the degenerate case — the cycle that contains exactly one transfer — so that the general case has something to be measured against.
2. Counting a Trace
A single read cycle
7 cyclesCount it the way the rest of Module 8 will ask you to.
Bus cycles = 1. CYC_O rose once and fell once.
Transfers = 1. One termination arrived. The number of transfers is the number of terminations — not the number of cycles STB_O was asserted, which is three here. Chapter 6.5 made that point for waits; it is the same counting rule at cycle level.
Wait cycles = 2. Cycles 2 and 3: presented, not terminated.
The two markers land on the same edges, and that coincidence is the thing to notice rather than to assume.
3. Why the Earlier Masters Tied Them Together
Every master in Modules 5, 6 and 7 drove cyc_o and stb_o from a single active_q register. That was not a shortcut — it is explicitly permitted, and the permission states its own precondition:
PERMISSION 3.40 — "If a MASTER doesn't generate wait states, then [STB_O] and [CYC_O] MAY be assigned the same signal."
"If a master doesn't generate wait states." Those masters did not: each presented one transfer and held STB_O until it terminated.
OBSERVATION 3.55 says the same thing from the other direction: "[CYC_O] needs to be asserted during the entire transfer cycle. A MASTER that doesn't generate wait states doesn't negate [STB_O] during a transfer cycle."
4. RTL — Measuring a Cycle
Module 8 needs to count things Modules 6 and 7 never counted. One small simulation-only block does that for the whole module.
// ─────────────────────────────────────────────────────────────────────────
// wb_cycle_monitor — SIMULATION-ONLY instrumentation.
//
// PURPOSE. Count the quantities Module 8 reasons about, from the bus alone,
// so that every claim in Chapters 8.1 to 8.6 is measured rather than
// asserted. It drives nothing and is not part of any design.
//
// THE COUNTING RULES, which are the chapter's content in executable form:
// bus cycles = rising edges of CYC_O
// transfers = cycles where a QUALIFIED transfer is TERMINATED
// (NOT cycles where STB_O is asserted — a waited transfer
// is presented for many cycles and is still one transfer)
// waits = qualified cycles with NO termination
// idle-in-cycle = CYC_O asserted, STB_O negated
// (impossible in Modules 6/7; normal from Chapter 8.3 on)
//
// Reset: SYNCHRONOUS, ACTIVE HIGH, matching the rest of the course.
// ─────────────────────────────────────────────────────────────────────────
module wb_cycle_monitor (
input logic clk_i,
input logic rst_i,
input logic cyc_i,
input logic stb_i,
input logic we_i,
input logic ack_i,
input logic err_i,
input logic rty_i,
output int unsigned cycles_o, // bus cycles begun
output int unsigned transfers_o, // transfers COMPLETED
output int unsigned reads_o,
output int unsigned writes_o,
output int unsigned waits_o, // presented-but-unterminated cycles
output int unsigned gaps_o, // CYC asserted, STB negated
output int unsigned busy_clks_o // clocks with CYC asserted
);
logic cyc_q; // for rising-edge detection
logic xfer, term;
assign xfer = cyc_i & stb_i;
assign term = xfer & (ack_i | err_i | rty_i);
always_ff @(posedge clk_i) begin
if (rst_i) begin
cyc_q <= 1'b0;
cycles_o <= 0;
transfers_o <= 0;
reads_o <= 0;
writes_o <= 0;
waits_o <= 0;
gaps_o <= 0;
busy_clks_o <= 0;
end else begin
cyc_q <= cyc_i;
// A new bus cycle begins on the RISING EDGE of CYC_O. Counting
// assertions rather than clocks is what makes one long block cycle
// count as one cycle rather than as its duration.
if (cyc_i && !cyc_q) cycles_o <= cycles_o + 1;
if (cyc_i) busy_clks_o <= busy_clks_o + 1;
// A transfer is counted at its TERMINATION, never at its
// presentation. Chapter 6.5 and Chapter 7.4 both measured bugs that
// came from counting presentations instead.
if (term) begin
transfers_o <= transfers_o + 1;
if (we_i) writes_o <= writes_o + 1;
else reads_o <= reads_o + 1;
end
if (xfer && !term) waits_o <= waits_o + 1;
// The state Modules 6 and 7 never produced.
if (cyc_i && !stb_i) gaps_o <= gaps_o + 1;
end
end
endmoduleReading it
Purpose. Turn the counting rules of Section 2 into something a testbench can print, so Module 8's numbers are measurements.
Interface. Inputs only, plus counters. It is not synthesizable design — int unsigned outputs are a simulation convenience, and the module is never instantiated inside anything that ships.
State. Seven counters plus one delayed copy of CYC_O for edge detection.
Combinational logic. xfer and term — the same two terms every slave in this course computes, here used to count rather than to respond.
Sequential logic. All counting, so every number is sampled at a clock edge and there is no race with the signals it observes.
Cycle start. The rising edge of CYC_O. Transfer start. Not counted — deliberately, because a transfer's start is not a well-defined single event when it can be presented for many cycles.
Termination. Counted, and split by direction using we_i sampled in the terminating cycle. That is safe because RULE 3.60 requires WE_O stable while the transfer is presented.
Cycle end. Implied by the next rising edge, or by the run ending.
Reset. Synchronous, active high.
Failure modes. Counting stb_i instead of term would over-count every waited transfer — which is exactly the class of bug Chapter 7.4 measured inside a slave.
Simplifications. No per-address tracking, no latency histogram. Chapter 8.6 adds only what it needs.
5. Simulation — One Cycle, One Transfer
wb_read_master from Chapter 6.1 was pointed at the running peripheral, reading ID at word 4, with the monitor attached.
=== SIM A - single read cycle ===
zero wait states:
bus cycles begun 1
transfers completed 1
reads / writes 1 / 0
wait cycles 0
gaps (CYC & !STB) 0
clocks with CYC high 1
value captured 0x57420601
with 2 wait states:
bus cycles begun 1
transfers completed 1
reads / writes 1 / 0
wait cycles 2
gaps (CYC & !STB) 0
clocks with CYC high 3
value captured 0x57420601One cycle and one transfer in both runs, which is the chapter's claim in numeric form.
The wait states changed the cycle's duration and not its content. Three clocks of CYC_O instead of one; still one transfer. That separation — duration versus content — is what Chapter 8.3 exploits.
gaps = 0 in both. This master cannot produce a gap: cyc_o and stb_o come from the same register, so CYC_O asserted with STB_O negated is unreachable. That zero is the signature of a PERMISSION 3.40 master, and it is the number that changes first in Chapter 8.3.
6. Failure Modes and Discriminating Evidence
Symptom: the cycle never ends — CYC_O stays asserted indefinitely.
Candidate causes. No termination ever arrived, or the master's release logic never ran.
Discriminating evidence. Check STB_O alongside it. Asserted too means the transfer is unanswered — Chapter 6.1 §6's four-probe walk applies unchanged. Negated while CYC_O stays high means the transfer completed and the master did not release, which is a cycle-control bug and lives in different logic entirely.
Likely RTL location. The first case is downstream; the second is the master's own state machine.
Symptom: CYC_O negates while STB_O is still asserted.
Candidate causes. A master releasing the cycle before its transfer terminated.
Discriminating evidence. Conclusive on sight — it is a RULE 3.25 duration violation, and a conformant slave stops responding under RULE 3.30, so the transfer can never complete. Chapter 5.2 §2 measured the resulting hang.
Symptom: the transfer count is higher than the number of operations issued.
Candidate causes. Counting STB_O assertions rather than terminations.
Discriminating evidence. Compare the count against the wait length. If it scales with latency rather than with traffic, the counter is counting presentations. This is the measurement-side twin of the slave bug Chapter 7.4 §5 measured.
Likely RTL location. The counter's enable — in the monitor, or in whatever is reporting.
7. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_single_cycle_checker — cycle-shape properties for a SINGLE cycle.
//
// P1 and P2 are SPECIFICATION (RULE 3.25). P3 is LOCAL DESIGN POLICY — it
// describes a master that performs one transfer per cycle, which is a
// choice, and it must be DELETED for a block or RMW master. It is included
// precisely so that deletion is a deliberate act.
// ─────────────────────────────────────────────────────────────────────────
module wb_single_cycle_checker (
input logic clk_i,
input logic rst_i,
input logic cyc_o,
input logic stb_o,
input logic ack_i,
input logic err_i,
input logic rty_i
);
default disable iff (rst_i);
logic terminated;
assign terminated = ack_i | err_i | rty_i;
// P1 — SPECIFICATION (RULE 3.25, start boundary). CYC_O is asserted no
// later than the edge qualifying STB_O, so a strobe outside a cycle
// is never legal. Checked every cycle as a plain implication:
// $rose(stb_o) |-> $rose(cyc_o) would wrongly forbid a master that
// opens its cycle early to request the bus.
property p_stb_inside_cyc;
@(posedge clk_i) stb_o |-> cyc_o;
endproperty
a_stb_inside_cyc : assert property (p_stb_inside_cyc)
else $error("RULE 3.25: STB_O asserted outside a bus cycle");
// P2 — SPECIFICATION (RULE 3.25, duration). The cycle outlives any
// transfer outstanding inside it.
property p_cyc_spans_outstanding;
@(posedge clk_i) (cyc_o && stb_o && !terminated) |=> cyc_o;
endproperty
a_cyc_spans_outstanding : assert property (p_cyc_spans_outstanding)
else $error("RULE 3.25: CYC_O negated with a transfer outstanding");
// P3 — LOCAL POLICY, TRUE ONLY FOR A SINGLE-TRANSFER MASTER. The cycle
// ends with its transfer. A BLOCK master (Chapter 8.3) and an RMW
// master (Chapter 8.4) both violate this legitimately, and this
// property must be removed when a master grows into either.
property p_one_transfer_per_cycle;
@(posedge clk_i) (cyc_o && stb_o && terminated) |=> !cyc_o;
endproperty
a_one_transfer_per_cycle : assert property (p_one_transfer_per_cycle)
else $error("LOCAL: cycle outlived its transfer in a single-transfer master");
endmoduleP3 is the property this module will spend four chapters escaping. It encodes "this master performs one transfer per cycle" — invisible in the RTL as a statement, and exactly what changes in Chapter 8.3.
A policy property that must be deleted when a design grows is doing its job, provided it is labelled. Chapter 5.2 §6 made the same argument about the same property; Module 8 is where the deletion actually happens.
Tooling limitation. Icarus Verilog has no SVA support and cannot execute any of these. They are reviewed by inspection; the monitor is elaborated and simulated.
8. Common Mistakes
"CYC_O and STB_O are the same signal."
Wrong mental model: generalising from single-transfer masters.
Concrete bug: a slave gated on stb_i alone — correct in a single-master system, a RULE 3.30 violation that fails under arbitration.
Observable evidence: a slave responding to a transfer belonging to another master, appearing as corruption under load.
Correct model: PERMISSION 3.40 permits tying them if the master never generates wait states. It is conditional, and Section 3's callout gives the condition.
"One CYC_O pulse means one transfer."
Wrong mental model: the cycle is the transfer.
Concrete bug: a cycle-level counter or a performance script that reports one transfer per cycle assertion, understating a block by a factor of four.
Observable evidence: measured throughput that contradicts the data actually moved.
Correct model: transfers are counted at terminations. CYC_O says how long the operation lasted, not how much it did.
"ACK ends the cycle."
Wrong mental model: the termination releases CYC_O.
Concrete bug: none in a single-transfer master, where they coincide — but the model predicts the wrong thing for every block and RMW cycle.
Observable evidence: a block trace that the reader cannot account for, because they expect CYC_O to fall at the first acknowledge.
Correct model: ACK terminates a transfer. The master's cycle-control logic decides when the cycle ends, per RULE 3.25's duration requirement.
9. Interview Reasoning
A transfer is one request-and-termination exchange. A bus cycle is the interval a master claims the bus for, and it may contain one transfer or several.
STB_O marks the transfer. It says a request is being presented right now, and the transfer ends when a termination arrives — ACK, ERR or RTY, exclusive under RULE 3.45.
CYC_O marks the cycle. RULE 3.25 requires it asserted for the duration of SINGLE, BLOCK and RMW cycles, and the signal description says it is asserted during the first data transfer and remains asserted until the last.
Why they look identical in most teaching examples. A master doing one transfer per cycle has two intervals that coincide, and PERMISSION 3.40 explicitly allows driving both from one signal if the master doesn't generate wait states. Every master in the earlier modules qualified.
Where the distinction becomes load-bearing. A block cycle holds CYC_O across several transfers, toggling STB_O per transfer. A read-modify-write holds it across a read and a write. And a master may pause mid-cycle — negating STB_O while keeping CYC_O asserted, which the block sections describe as master-inserted wait states.
The counting consequence, which is how I would check someone understood it. Ask how many transfers are in a trace. The answer is the number of terminations, never the number of cycles STB_O was high — a waited transfer is presented for many cycles and is still one transfer.
And the arbitration consequence. CYC_O is what an arbiter watches — RECOMMENDATION 3.05 notes arbitration logic often uses CYC_I to select between masters. So the cycle is also the unit of bus ownership, which is why Chapter 8.4 can build an indivisible sequence out of it.
10. Understanding Check
11. What's Next
One cycle, one transfer, and a clear statement of why those two numbers happened to match.
The write from Module 7 has the same shape. Before generalising to many transfers, it is worth seeing exactly how much of the cycle skeleton survives when the payload direction reverses — because what survives is the part Module 8 is actually about.
How does the same single-transfer cycle structure carry a write?
Chapter 8.2 — Single Write Cycle answers it. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
Single Write Cycle
SINGLE READ / WRITE is one cycle type, not two. The cycle skeleton carries no data ports at all, because data is not cycle structure.
- Related topic
CPU to Peripheral Communication
A CPU reaches hardware outside itself by reading and writing addressed locations, and a peripheral is hardware it cannot execute. Everything a driver does has to be expressed as a read or a write of a location the peripheral answers for — and once more than a couple of peripherals exist, wiring each one to the core separately stops scaling. That is the problem an on-chip bus is the answer to.
- Related topic
Need for Standardized Interconnects
An address map answers where a register lives. It says nothing about which wires carry the request, when they are valid, how the target reports completion, or what happens on an error. Three peripherals with three private interfaces produce three adapters, three verification efforts and three ways to be wrong — which is the argument for standardising the interface rather than the map.
- Related topic
FPGA Design Challenges
Six peripherals and two masters on one FPGA is where on-chip integration stops being theory. The decode that must be exhaustive and one-hot, the read multiplexer that grows with every target and carries the critical path, the latency and reset conventions that refuse to agree, and the point at which the fabric rather than the peripherals starts failing timing.
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.
