Wishbone · Module 19
CPU Integration
Byte enables generated from size and offset, misalignment as stated local policy, ERR as a documentation obligation rather than a behavioural rule, and an interrupt that reached the core while every bus control signal stayed bit-identical for thirty-two clocks.
This is the chapter about the boundary no specification describes. Chapter 19.3 showed three cores solving it three ways. This one builds it and measures every obligation it carries.
A core issues a byte-addressed, sized request. Wishbone carries a word-addressed cycle with lane selects. What exactly has to happen in between, and which parts of it can you look up?
1. The Adapter, Stated Before It Is Built
// ─────────────────────────────────────────────────────────────────────────
// wb_dbus_adapter — SEAM 1, the load/store half.
//
// A core's native valid/ready request becomes a conformant Wishbone MASTER
// cycle. Neither specification describes this module: Wishbone describes a
// MASTER port and the RISC-V ISA describes instructions, and the thing in
// between is the integrator's.
//
// ── THE FIVE OBLIGATIONS ────────────────────────────────────────────────
//
// 1. HOLD THE REQUEST UNTIL IT IS ANSWERED.
// A request that has reached the bus belongs to the bus. Nothing the
// core does afterwards - including stalling - may withdraw it. REQ_DROP
// is this rule removed and Chapter 19.3 measures it.
//
// 2. CYC_O FOR THE DURATION (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]."
//
// 3. BYTE ADDRESS -> WORD ADDRESS + SEL.
// The core addresses bytes; the bus addresses words. SEL_O's own
// description says "The array boundaries are determined by the
// granularity of a port", so with 32-bit port size and 8-bit
// granularity there are four select bits and each names one byte lane.
// SEL_O is in RULE 3.60's set that STB_O qualifies, which makes
// generating it correctly this module's job and nobody else's.
//
// size addr[1:0] SEL_O meaning
// word 00 1111 all four lanes
// half 00 0011 lanes 1..0
// half 10 1100 lanes 3..2
// byte 00 0001
// byte 01 0010
// byte 10 0100
// byte 11 1000
//
// 4. RETURN THE RIGHT LANE.
// A byte read returns one lane, right-aligned and zero-extended. Sign
// extension is the core's business, not the bus adapter's, and this
// module does not do it.
//
// 5. AN ERROR REACHES THE CORE.
// ERR_I terminates the cycle and is reported. What the core then does is
// NOT dictated by Wishbone - RULE 2.15 requires the master's DATASHEET
// to describe it. ERR_SWALLOWED is this obligation removed.
//Obligation 3 is the one that repays close reading. SEL_O's own description in B3 says "The array boundaries are determined by the granularity of a port" — so a 32-bit port with 8-bit granularity has four select bits, each naming one byte lane. And RULE 3.60 puts SEL_O in the set of signals STB_O qualifies.
Put those two together and you get: the width is specified, the meaning of each bit is specified, and which bits your half-word at offset 2 should assert is not. That table in the header is this adapter's datasheet entry, and writing it down is the whole of RULE 2.00.
2. Generating the Selects
logic [SW-1:0] sel_good;
always_comb begin
case (req_size_i)
2'd0: sel_good = 4'b0001 << off; // byte: one lane
2'd1: sel_good = off1 ? 4'b1100 // half: a lane pair
: 4'b0011;
default: sel_good = 4'b1111; // word: all four
endcase
end
// DEFECT: every access presented as a full-word write. A byte store then
// overwrites its three neighbours, and Chapter 19.3 measures the damage.
logic [SW-1:0] sel_use;
assign sel_use = SEL_IGNORED ? {SW{1'b1}} : sel_good;
// ── write data placed in its lane ──
// The core supplies the value right-aligned; the bus expects it in the
// lane SEL names. Getting this wrong is invisible on word accesses.
logic [DW-1:0] wdat_lane;
always_comb begin
case (req_size_i)
2'd0: wdat_lane = {4{wb0}};
2'd1: wdat_lane = {2{wh0}};
default: wdat_lane = req_wdata_i;
endcase
endTwo separate things are happening there and they are easy to conflate. sel_good says which lanes. wdat_lane says where in the 32-bit word the core's data has to be placed so that those lanes pick it up. A byte write replicates the byte four times and lets SEL_O choose which copy lands — because the slave takes its data from the lane the select names, not from the bottom of the bus.
3. SIM E — SEL Is a Granularity Map, Not a Size Code
The defect SEL_IGNORED is one line:
logic [SW-1:0] sel_use;
assign sel_use = SEL_IGNORED ? {SW{1'b1}} : sel_good;It is a plausible mistake. A 32-bit port, four lanes, always assert all four — it works perfectly for every word access, and every word access is most of them.
=== SIM E - SEL is a granularity map, not a size code ===
the CSR scratch register at byte 0x3000 is plain storage
with SEL_O honoured per lane. SEL_O's own description:
"The array boundaries are determined by the granularity
of a port." 32-bit port, 8-bit granularity, four lanes.
access SEL scratch after
correct SEL_IGNORED
byte 0xAB -> +0 0001 0x000000ab 0xabababab
byte 0xCD -> +2 0100 0x00cd00ab 0xcdcdcdcd
half 0x1234 -> +0 0011 0x00cd1234 0x12341234
half 0x5678 -> +2 1100 0x56781234 0x56785678
read back correct SEL_IGNORED
byte at +0 0x00000034 0x00000078
byte at +2 0x00000078 0x00000078
half at +2 0x00005678 0x00005678
word at +0 0x56781234 0x56785678
SEL did not match the size: correct 0 broken 14
-> the broken adapter presents every access as a full
word, so a byte store overwrites its three neighbours
and the register that held two independent fields no
longer can.Follow the correct column down. A byte at offset 0, then a byte at offset 2, then a half at offset 0, then a half at offset 2 — and each one leaves its neighbours alone, so the register accumulates four independent writes into 0x56781234.
Now the broken column. Every write splattered across all four lanes. The final value is right only by accident of the last write being a half-word replicated. A register holding two independent fields cannot survive this, and nothing on the bus was violated: the broken adapter drove a legal SEL_O, stable and qualified by STB_O, exactly as RULE 3.45 requires.
4. Misalignment Is a Local Policy, Stated
assign bad_align = (req_size_i == 2'd2 && off != 2'b00)
|| (req_size_i == 2'd1 && off[0] != 1'b0);
assign misaligned_o = req_valid_i && bad_align;This adapter refuses a misaligned access at the seam and issues no bus cycle at all. That is a choice, and the header says so:
// ── MISALIGNMENT IS A LOCAL POLICY ──────────────────────────────────────
// A halfword at an odd byte address, or a word not at a multiple of four,
// is refused HERE: the adapter answers the core with an error and issues
// NO bus cycle at all. That is a choice. A different adapter could split
// the access into two cycles, or pass it down and let a slave complain.
// B3 says nothing about any of this. The choice is stated so it can be
// measured, and Chapter 19.4 measures it. misalignment, a LOCAL POLICY of this adapter
half at byte +1 trap 1 bus transfers added 0
-> refused at the seam. No cycle reached the bus at
all, which is a choice: an adapter could split it,
or pass it down. B3 says nothing about any of this.Zero bus transfers were added. The trap came from the adapter, not from a slave, and a bus trace of that access is empty. A different adapter could split the access into two cycles, or pass it down and let a slave complain — all three behaviours are conformant, and a system built from two of them will behave inconsistently without anything being wrong.
5. SIM F — An Error the Core Has To Hear About
ERR_SWALLOWED removes obligation 5:
assign rsp_ready_o = busy_q && term;
assign rsp_rdata_o = dat_i;
assign rsp_err_o = ERR_SWALLOWED ? 1'b0 : (busy_q && err_i); === SIM F - an error the core has to hear about ===
a word write to the boot ROM at byte 0x0000. The ROM
answers ERR_O because the request is wrong for it, which
is Chapter 11.2's test rather than a deferral.
measure correct ERR_SWALLOWED
ROM writes refused 1 1
traps raised at the core 1 0
trap disagreed with the error 0 0
bus transfers on the data port 1 1
Both slaves refused. Both buses behaved identically. The
difference is entirely at the seam, and the core in the
second column is about to act on a write that did not
happen.
B3 does not say a core must trap. RULE 2.15 item 4 says
the MASTER's DATASHEET "MUST describe how it reacts in
response to the signal". The obligation is to write it
down, and this adapter's answer is: raise trap, commit
nothing.Read the table carefully, because the interesting column is the one that is identical. Both systems refused the write. Both performed exactly one bus transfer. Both buses behaved identically. The difference is entirely at the seam — and the core in the second column is about to carry on as though a write succeeded.
6. SIM G — The Interrupt, and What the Bus Did
This is the module's headline measurement.
The same stimulus is run twice. In the first run a GPIO input edge raises the interrupt while the core is working; in the second run it does not. Every shared-bus signal is folded into a rolling signature on every clock, so two runs can be compared as one integer.
// TWO signatures, because they answer two different questions.
//
// sig_ctrl covers only the signals that decide WHAT TRANSFERS HAPPEN:
// cyc, stb, we, adr, sel, ack, err, and the write data.
// sig_full adds the READ-DATA lines, which are a slave's internal state
// presented on a wire.
//
// For Chapter 19.4's question - did the interrupt change the bus? - the
// first is the honest instrument. A peripheral whose status register now
// reads differently has not changed the bus; it has changed itself, and
// its read port shows that on every clock whether anybody is addressing
// it or not. Reporting both, and saying which is which, is more useful
// than picking one.
logic [31:0] acc_c_q, acc_f_q;
logic [31:0] fold_c, fold_f;
assign fold_c = dat_i
^ {{(32-AW){1'b0}}, adr_i}
^ {{(32-SW){1'b0}}, sel_i}
^ {27'd0, err_i, ack_i, we_i, stb_i, cyc_i};
assign fold_f = fold_c ^ rdat_i;
assign sig_ctrl_o = acc_c_q;
assign sig_full_o = acc_f_q;
always_ff @(posedge clk_i) begin
if (rst_i) begin
acc_c_q <= 32'h1357_9BDF; acc_f_q <= 32'h1357_9BDF; clocks_o <= 0;
end else begin
// rotate then mix: order-sensitive, so a one-clock difference cannot
// be cancelled by a later one
acc_c_q <= {acc_c_q[30:0], acc_c_q[31]} ^ fold_c ^ 32'h9E37_79B9;
acc_f_q <= {acc_f_q[30:0], acc_f_q[31]} ^ fold_f ^ 32'h9E37_79B9;
clocks_o <= clocks_o + 1;Two signatures, because there are two different questions.
=== SIM G - what the bus did when the interrupt fired ===
the same stimulus is run twice. In the first run a GPIO
input edge raises the interrupt; in the second it does
not. Every shared-bus signal is folded into a rolling
signature every clock, so two identical signatures mean
two identical bus traces.
run edges irq clocks control sig full sig
interrupt fires 1 1 32 0x1ded2bc0 0x01420933
interrupt quiet 0 0 32 0x1ded2bc0 0x02420933
CONTROL SIGNALS: IDENTICAL over all 32 clocks.
WITH READ DATA: different.
An interrupt was raised, reached the core, and was
counted - and CYC, STB, WE, ADR, SEL, ACK, ERR and the
write data were bit-identical on every one of those
clocks. No transfer happened differently.
The full signature DOES differ, and that is worth saying
plainly rather than hiding: the GPIO's read port carries
its own flag register, and that register now holds a
different value. The peripheral changed. The bus did
not. A slave's read lines are its state on a wire, and
they mean nothing until a termination qualifies them -
which is Chapter 16.3's point arriving here.
The word "interrupt" appears five times in B3 and not
once as an interface signal.The two irq edges cross the entire figure without touching a single box in the middle. That is not a drawing convenience — it is the measurement in SIM G. The path exists, it carries information to the core, and it is invisible to every instrument that watches the bus.
Thirty-two clocks, bit-identical on every control signal. CYC, STB, WE, ADR, SEL, ACK, ERR and the write data were the same in both runs. An interrupt was raised, reached the core, and was counted — and not one transfer happened differently.
7. Why There Is No Rule To Cite Here
B3 contains the word interrupt five times. They are: "Interrupt vectors" as an example of a user-defined TAG; "interrupt acknowledge" twice, as an example of a cycle type a TAG could discriminate; and "uninterruptible" twice, in the LOCK descriptions.
Not one of them is an interface signal. There is no IRQ_I, no INT_O, no recommendation, no permission, no observation. The wire from the timer to the core in this module's SoC is a wire somebody drew.
// offset 2 COUNT RO a write is answered ERR_O
// offset 3 STATUS [0] expired W1C
//
// ── THE INTERRUPT IS NOT A BUS SIGNAL ───────────────────────────────────
// irq_o is a wire to the core. It is not CYC_O, not ACK_O, not ERR_O, and
// it appears in no B3 signal list - the word "interrupt" occurs five times
// in the whole specification and not once as an interface signal. A core
// may be interrupted with no transfer in flight, and a transfer may
// complete with no relationship to any interrupt. Chapter 19.4 measures
// both directions of that independence.
//
// IRQ_VIA_BUS is the defect where irq_o is removed and the condition is
// only discoverable by polling STATUS - which is a legitimate design if it
// is what you meant, and a silent latency bug if it is not.IRQ_VIA_BUS is the defect where that wire is removed and the condition is discoverable only by polling STATUS. Note what the header says about it: that is a legitimate design if it is what you meant. Polling is a real strategy with real trade-offs. It becomes a bug only when the rest of the system was built assuming a line that is not there — which is exactly the failure a negative control can catch and a protocol checker cannot. Chapter 19.5 §7 runs it.
8. The Same Invariants as Assertions
Every property this chapter measures has been written in SystemVerilog Assertions as well:
// ── SEAM 1 ────────────────────────────────────────────────────────────
// A request that has been accepted at the seam must not be withdrawn
// because the core stopped being ready. Readiness is about the ANSWER.
property p_request_not_withdrawn;
@(posedge clk_i)
(cyc_i && !(ack_i || err_i) && core_stall_i) |=> cyc_i;
endproperty
a_request_not_withdrawn: assert property (p_request_not_withdrawn);
// One request produces at most one bus termination.
property p_one_termination;
@(posedge clk_i)
(cyc_i && stb_i && (ack_i || err_i)) |=> !(ack_i && stb_i && cyc_i);
endproperty
a_one_termination: assert property (p_one_termination);
// The address presented on the bus is the core's address shifted, and it
// does not move while the phase is unanswered. B3 RULE 3.50 requires the
// stability; the shift is the adapter's own arithmetic and is nobody's
// rule but the designer's.
property p_address_stable;
@(posedge clk_i)
(cyc_i && stb_i && !(ack_i || err_i)) |=> $stable(adr_i);
endproperty
a_address_stable: assert property (p_address_stable);One property in that file is a cover, not an assert, and the reason is the chapter's thesis:
// There is nothing to assert about irq_i against the bus, and that is
// the point. It may rise on any clock, inside a phase or outside one,
// and no Wishbone signal constrains it. The property below is written
// as a COVER rather than an ASSERT for exactly that reason: it records
// that the case happened, and claims nothing about what must follow.
property p_irq_inside_phase;
@(posedge clk_i) $rose(irq_i) && cyc_i && stb_i && !(ack_i || err_i);
endproperty
c_irq_inside_phase: cover property (p_irq_inside_phase);There is nothing to assert about the interrupt against the bus. It may rise on any clock, inside a phase or outside one, and no Wishbone signal constrains it. A cover records that the case occurred and claims nothing about what must follow — which is the only honest thing an assertion language can say about a wire two specifications both decline to mention.
9. What Seam 1 Costs, Summarised
| obligation | what removing it looks like | caught by |
|---|---|---|
| hold the request | three cycles vanish, the core gets the right answer anyway | a seam counter, not a bus checker |
SEL from size | a byte store overwrites three neighbours | comparing SEL against the core's request |
| error reaches the core | the core proceeds after a failed write | counting traps against refusals |
| the interrupt wire | latency, silently | nothing on the bus at all |
Not one row in that table is a Wishbone protocol violation. Chapter 19.5 §7 runs all four defects plus two more past a full conformance-style checker set and publishes the matrix.
Continue learning
Related tutorials
- Related topic
SEL Signals
The address names a word; SEL names which bytes of it take part. The lane binding is normative, the byte numbering is not, and confusing the two is the expensive mistake.
- Related topic
Multi-Master Systems
Byte-identical masters against two interconnects. The ownership timeline names clock 19, and three checkers are validated against the defects they claim to catch.
- Related topic
Bus Ownership
Ownership has two directions, not one. Three targeted interconnect defects measured against one stimulus — and a conformance monitor that finds every one of them faultless.
- Related topic
Arbitration Logic
The integrated arbiter: one stimulus against two policies with everything downstream identical, an audit across every situation in the module, and five arbiters — three broken — all found perfectly conformant.
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.
