Wishbone · Module 22
Design Optimization
B3 publishes its own throughput formulae — async N, sync 2N, advanced N+1. All fifteen verified with zero tolerance, and the specification's own note about wait states does not survive measurement.
There is a chapter of Wishbone B3 that no module in this curriculum has cited. 04_registered.rst is 824 lines with 22 numbered identifiers, and it exists because of a problem the previous twenty-one modules have been quietly benefiting from.
B3 publishes its own throughput table. This chapter measures it.
1. The Problem Chapter 4 Was Written To Solve
"To achieve the highest possible throughput, WISHBONE Classic requires asynchronous cycle termination signals. This results in an asynchronous loop from the MASTER, through the INTERCONN to the SLAVE, and then from the SLAVE through the INTERCONN back to the MASTER... In large System-on-Chip devices this routing delay between MASTER and SLAVE is the dominant timing factor. This is especially true for deep sub-micron technologies."
"The simplest solution for reducing the delay is to cut the loop, by using synchronous cycle termination signals. However, this introduces a wait state for every transfer." — B3, §4.1
2. Three Schemes, One Slave
// This module implements all three so the cost can be measured rather
// than argued about. SCHEME selects:
//
// 0 ASYNC [ACK_O] combinational from [STB_I] && [CYC_I].
// PERMISSION 3.10 permits exactly this. One clock per
// transfer, and the long path chapter 4 warns about.
//
// 1 SYNC [ACK_O] registered, AND NEGATED AFTER EVERY BEAT. The
// loop is cut and the wait state chapter 4 predicts
// appears. Two clocks per transfer.
//
// The negation is not an implementation choice. B3 is
// explicit about why it happens:
//
// "During cycle-3 the MASTER initiates a second cycle,
// addressing the same SLAVE. Because the SLAVE does not
// know in advance it is being addressed again, IT HAS TO
// NEGATE ACK_O. At the earliest it can respond in
// cycle-4, after which it has to negate ACK_O again in
// cycle-5." (B3, 04_registered.rst, s4.1 intro)
//
// A registered slave with no CTI cannot distinguish "the
// master is still asserting STB for the same beat I just
// answered" from "the master has started a new beat". It
// must assume the former, so it drops ACK_O and re-decides.
// That is precisely the information CTI_I() supplies, and
// precisely why SCHEME 2 can keep ACK_O up.
//
// 2 ADV Registered feedback. [ACK_O] is still registered, but the
// slave uses [CTI_I()] to know a burst continues and holds
// [ACK_O] asserted across beats. N+1 clocks for a burst of N.The SCHEME 1 negation is not an implementation choice, and getting it wrong was this module's most instructive error — §7 has the account.
3. What The Cycle Type Identifier Is For
Registered feedback works by telling the slave what is coming, so it can decide its next [ACK_O] one clock early. That is what [CTI_O()] carries:
CTI_O(2:0) | meaning |
|---|---|
000 | Classic cycle |
001 | Constant address burst |
010 | Incrementing burst |
111 | End-of-Burst |
| others | Reserved |
And it requires a permission Classic does not grant. Classic's RULE 3.35 says terminations "must be generated in response to the logical AND of [CYC_I] and [STB_I]". A registered slave cannot do that — it must decide before it knows. So:
"In addition to the WISHBONE Classic rules for generating cycle termination signals
[ACK_O],[RTY_O], and[ERR_O], a SLAVE MAY assert a termination cycle without checking the[STB_I]signal." — PERMISSION 4.15
"...it cannot determine the state of
[STB_I]for the next cycle, therefore it must generate the response independent of[STB_I]." — OBSERVATION 4.00
And the bound that makes it safe:
"A cycle terminates when both the cycle termination signal and
[STB_I],[STB_O]is asserted." — RULE 4.15
An early [ACK_O] standing while [STB_O] is low terminates nothing. §6 shows what happens when a master relies on that and forgets to say when the burst ends.
4. B3's Table, Measured
The specification publishes the predictions, so this chapter did not have to derive them:
"Asynchronous cycle termination requires only one cycle per transfer, synchronous cycle termination requires two cycles per transfer, and the advanced synchronous cycle termination requires (burst_length+1) cycles." — B3, §4.1, below Table 4-1
=== SIM I - Table 4-1, measured ===
predictions are literals in this testbench, taken from
B3 Table 4-1. Tolerance 0 - the table gives exact
integers.
case pred meas res verdict
async N=1 (pred N) 1 1 0 MATCH
sync N=1 (pred 2N) 2 2 0 MATCH
advanced N=1 (pred N+1) 2 2 0 MATCH
async N=2 (pred N) 2 2 0 MATCH
sync N=2 (pred 2N) 4 4 0 MATCH
advanced N=2 (pred N+1) 3 3 0 MATCH
async N=4 (pred N) 4 4 0 MATCH
sync N=4 (pred 2N) 8 8 0 MATCH
advanced N=4 (pred N+1) 5 5 0 MATCH
async N=8 (pred N) 8 8 0 MATCH
sync N=8 (pred 2N) 16 16 0 MATCH
advanced N=8 (pred N+1) 9 9 0 MATCH
async N=16 (pred N) 16 16 0 MATCH
sync N=16 (pred 2N) 32 32 0 MATCH
advanced N=16 (pred N+1) 17 17 0 MATCH
checks run 15 failures 0
ALL THREE FORMULAE HELD EXACTLY, at every burst
length, with zero tolerance. B3 published the
arithmetic and this module confirms it.
THE TWO CLAIMS B3 MAKES ABOUT THE TABLE
N=2: advanced 3 against synchronous 4.
B3: "A two cycle burst now takes three cycles toFifteen predictions, fifteen matches, zero residual, zero tolerance.
increase of 33%." measured 33%
N=8: advanced 9 against synchronous 16.
B3: "An eight cycle burst takes nine cycles to
complete, instead of 16. This is a throughput
increase of 77%." measured 78%
THE CLAIM THAT MATTERS MOST, AND IT IS EASY TO MISREAD
B3: "System layout requires that all block have
registered outputs. The average burst length used in
the system is 4. Moving to the advanced synchronous
termination scheme improves performance by 60%."
At N=4: async 4 sync 8 advanced 5
against SYNCHRONOUS 60% better
against ASYNCHRONOUS 25% WORSE5. The Claim That Does Not Survive Measurement
N=4, TWO wait states on every rig:
asynchronous 12 clocks
advanced, waits PER BEAT 10 clocks
advanced, waits as PIPELINE FILL 7 clocks
THE NOTE DOES NOT SURVIVE PRECISE MEASUREMENT, and
that is this chapter's residual.
B3 says the two schemes "provide the same
throughput" once wait states are present. Measured,
the per-beat slave gives 10 against 12 - advanced is
still 2 clocks ahead over 4 beats, which is 17%.
The NOTE is directionally right and numerically
wrong: the advantage COLLAPSES from 1.6x at zero
waits to 1.20x at two, but it does not reach parity.
What remains is the address-setup clock the CTI code
still removes even when the slave is slow.
And the PIPELINE-FILL slave is 7 clocks - further
ahead, not closer. Table 4-1 is explicitly "for zero
wait state bursts" so it never had to choose between
these two slaves, and the NOTE reads as though only
the per-beat one exists.
THE DIFFERENCE BETWEEN THE TWO SLAVE MODELS (10 vs 7)
IS LARGER THAN THE DIFFERENCE THE NOTE IS ABOUT. In a
real design the slave's internal structure decides
more than the termination scheme does.
THE TWO DEFECTS, FOR REFERENCE
rig clocks beats slave beats
advanced correct 9 8 58
CTI_IGNORED (degrades) 16 8 586. The Defect A Throughput Benchmark Reports As An Improvement
data signatures correct 0xadf3d1d7 ignored 0xadf3d1d7
-> CTI_IGNORED returned IDENTICAL DATA and took 16
clocks instead of 9. It is not broken; it is
slow, and RULE 4.10 makes that conformant:
unsupported burst types "MUST complete... as
though they were WISHBONE Classic cycle".
NO_EOB IS THE ONE TO READ TWICE. It took 8 clocks -
FEWER than the correct rig's 9 - and a benchmark
that counts only clocks would report it as an
IMPROVEMENT.
It is not. Without the End-of-Burst code the slave
never learns the burst ended, so [ACK_O] is still
asserted when the NEXT cycle begins. The first beat
of that cycle is terminated by an acknowledgement the
slave decided during the PREVIOUS burst, before it
had seen this beat's address.
Traced directly: on a second 3-beat burst the correct
rig starts with ACK low and takes 4 clocks; the
NO_EOB rig starts with ACK already high and takes 3.
The saved clock is a beat that was never evaluated.
RULE 4.15 is what keeps this from corrupting data:
"A cycle terminates when both the cycle termination
signal and [STB_I], [STB_O] is asserted." So the
stale [ACK_O] harms nothing while [STB_O] is low -
and does real harm the instant it rises.
=== errors: 0 ===
tb_rf.sv:281: $finish called at 2650000 (1ps)7. The Error This Chapter Made, And How It Was Caught
The first version of the synchronous slave held [ACK_O] across beats, and the formula checker failed four of fifteen predictions: sync measured N+1 where Table 4-1 says 2N.
The specification says exactly why that is wrong:
"During cycle-3 the MASTER initiates a second cycle, addressing the same SLAVE. Because the SLAVE does not know in advance it is being addressed again, it has to negate
ACK_O. At the earliest it can respond in cycle-4, after which it has to negateACK_Oagain in cycle-5." — B3, §4.1
A registered slave with no CTI cannot distinguish "the master is still asserting STB for the beat I just answered" from "the master has started a new one". It must assume the former. That is precisely the information [CTI_I()] supplies, and precisely why the advanced scheme can keep [ACK_O] up.
The predict-then-measure method is what caught it. A module that had only measured and reported would have published N+1 for the synchronous scheme and contradicted B3's own table without noticing.
8. The Negative-Control Gate
In a measurement module the instruments are the DUT.
=== NEGATIVE-CONTROL GATE ===
one stimulus, seven systems. Each differs from the
correct one by exactly one parameter, and every
parameter is a bug in an INSTRUMENT.
checker corr DBLC WUSE ALLL FOLD WTOL PMEA
CLOCKS ATTRIBUTED ONCE PASS FAIL PASS PASS PASS PASS PASS
USEFUL EXCLUDES WAIT STATES PASS PASS FAIL PASS PASS PASS PASS
BYTES COUNT SELECTED LANES PASS PASS PASS FAIL PASS PASS PASS
HANDOVER SEPARATE FROM SLAVE PASS - - - FAIL - -
FORMULA CHECKER CAN FAIL PASS - - - - FAIL -
PREDICTION IS INDEPENDENT PASS - - - - - FAIL
raw numbers
column issue present answer recover sum total
correct 1 32 16 15 64 64
DOUBLE_COUNT 33 32 16 15 96 64
column useful transfers payload eff
correct 16 16 54 25%
WAITS_USEFUL 48 16 54 75%
ALL_LANES 16 16 64 25%
arbitration probe, single master that always owns
correct handover 0 slave 32
FOLD_SLAVE handover 32 slave 32
formula checkers, all given the SAME wrong prediction
(16 predicted, 48 measured - a 32 clock error)
correct residual 32 failures 1
WIDE_TOL residual 32 failures 0
PRED_FROM_MEAS residual 0 failures 0 CHECKERS REQUIRED: >= 5
CHECKERS DECLARED: 6
CHECKERS PASSING THE CORRECT SYSTEM: 6/6
EACH CHECKER FAILS ITS OWN TARGET:
6/6
THE LAST TWO COLUMNS ARE THE ONES THAT MATTER.
Every rig above was handed the SAME wrong prediction -
16 clocks predicted against 48 measured. The correct9. The Two Columns That Matter Most
reported the same residual and PASSED, because its
tolerance is wider than the error. PRED_FROM_MEAS
reported a residual of 0, because it graded the
measurement against itself.
A PREDICTION COMPUTED FROM THE MEASUREMENT IS NOT A
PREDICTION. It always matches, it proves nothing, and
it is indistinguishable from a real check in every
published table. The only way to tell them apart is to
feed both a value that is KNOWN to be wrong and see
which one notices.
WHAT A CONFORMANCE CHECKER WOULD CATCH HERE: NOTHING.
Not one of these six defects touches a bus signal. The
waveforms of all seven systems are bit-identical -
only the numbers reported about them differ. That is
the expected result for a measurement module, and it
is why the running total across Modules 9-21 stays at
FOUR.
=== errors: 0 ===
tb_negctl.sv:211: $finish called at 770000 (1ps)10. The Same Invariants As Assertions
// ── GROUP 3: THE INSTRUMENTS — owned by this module alone ────────────
//
// No rule number exists for any of these. They are the properties that
// make a measurement trustworthy, and in a performance module they
// matter more than the protocol ones.
// Every clock lands in exactly one latency component.
property p_components_sum;
@(posedge clk_i)
(issue_i + present_i + answer_i + recover_i) == total_i;
endproperty
a_components_sum: assert property (p_components_sum);
// A clock counted as useful is a clock on which a transfer completed.
property p_useful_is_a_transfer;
@(posedge clk_i) useful_i <= transfers_i;
endproperty
a_useful_is_a_transfer: assert property (p_useful_is_a_transfer);
// Payload cannot exceed the bus width times the transfers that
// happened - a meter claiming more bytes than lanes existed is broken.
property p_payload_bounded;
@(posedge clk_i) payload_i <= (transfers_i * SW);
endproperty
a_payload_bounded: assert property (p_payload_bounded);11. What To Actually Do
| if | then | evidence |
|---|---|---|
| single transfers, timing closes | asynchronous — 1 clock, and nothing beats it | §4, Table 4-1 row 1 |
| single transfers, timing does not close | synchronous; accept 2 clocks | §1, and it is APB's floor |
| bursts, and timing is tight | registered feedback — N+1 against 2N | §4 |
| bursts, and timing is fine | asynchronous still wins: N against N+1 | §4 |
| the slave prefetches | registered feedback wins by more than B3's note suggests | §5 |
| you are quoting "60% faster" | check the baseline | §5 |
12. What Module 22 Established
| the line | bytes per clock is published; bytes per second is refused |
| the formula | clocks(N,W) = N × (1+W), exact at five settings |
| where it breaks | a second master (22.4), and a different termination scheme (§4) |
| B3's Table 4-1 | 15/15 predictions verified, zero tolerance |
| B3's wait-state note | does not survive precise measurement — §5 |
| efficiency | falls as 1/(2+W); the first wait state is the expensive one |
| the audit | wb_master_seq spends one clock per transfer that no rule requires |
| defects seeded | 6 instrument bugs + 2 RTL defects; a protocol checker catches none |
| protocol-checker total, Modules 9–22 | 4, unchanged |
Continue learning
Related tutorials
- Related topic
Burst-Like Behavior
A legal block cycle whose addresses run forward four, back three, forward six — every phase acknowledged. And where B3's burst metadata actually lives.
- Related topic
Arbitration Cost
A master waiting for the bus and a master waiting for a slave look identical and have different fixes. Total handover grows with burst length while per-beat handover falls.
- 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
Memory-Mapped IO
Memory-mapped I/O does not turn a peripheral into memory. It gives the peripheral's registers addresses in the processor's address space, so an ordinary load or store selects them. The address then does two jobs — name the target, name the register inside it — and the map that assigns them is a contract between software and RTL.
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.
