Wishbone · Module 13
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.
Module 12 finished addressing. Every transfer so far moved a whole word, and SEL_O has been tied to all-ones since Chapter 4.7 introduced it.
Software rarely wants a whole word. It wants one byte of a status register, or two bytes of a packed structure.
The address already names the location. What else is there to say?
1. Why the Signal Has to Exist
A 32-bit register holds four bytes belonging to four different things. Software wants to change one of them.
Without lane selection the bus has three options, and all of them are worse:
Read, modify, write. Fetch the word, replace one byte, write it back. Two transfers instead of one, and between them the other three bytes are exposed — anything else that writes the word in that gap is lost. That is a genuine bus operation with its own machinery, and it is Module 15's subject, not a substitute for byte writes.
A separate address per byte. Give every byte its own location. The address space grows by the port width, and a peripheral that wants word access as well now needs two ways to reach the same state.
Write the whole word anyway. Supply the one byte that matters and whatever happens to be on the other lanes. This destroys the neighbours, and it is the failure Chapter 13.3 measures.
SEL is the fourth option: one transfer, one address, and a statement of which lanes carry data that matters.
2. Lanes, and Where Their Width Comes From
The select array's width comes from the granularity, not from the port size alone. The SEL_O() description is explicit:
The array boundaries are determined by the granularity of a port. For example, if 8-bit granularity is used on a 64-bit port, then there would be an array of eight select signals with boundaries of
SEL_O(7..0). Each individual select signal correlates to one of eight active bytes on the 64-bit data port.
So the count is port size / granularity:
| port size | granularity | select lines | smallest transferable operand |
|---|---|---|---|
| 32 | 8 | 4 | one byte |
| 32 | 16 | 2 | one 16-bit word |
| 32 | 32 | 1 | the whole 32-bit word |
| 64 | 8 | 8 | one byte |
The third row is the one worth pausing on. A 32-bit port with 32-bit granularity has one select line, and that is not a degenerate case — the specification says granularity "indicates the minimum unit of data transfer that is supported by the interface", and that "the smallest operand that can be passed through a port with 16-bit granularity is a 16-bit WORD. In this case, an 8-bit operand cannot be transferred."
Byte enables are not free. A core that does not implement byte granularity cannot be given them by the interconnect, and RULE 2.15 requires a datasheet to state the port's granularity precisely so that an integrator knows which case applies.
This course uses a 32-bit port with 8-bit granularity throughout, giving SEL[3:0].
3. The Lane Binding Is Normative
Which data bits does SEL[2] cover? The specification answers this, and it is not a convention anyone may vary.
RULE 3.100 — "Data organization on 32-bit ports MUST conform to [the 32-bit organization figure]." That figure, for a 32-bit bus with 8-bit granularity, gives:
| data bus slice | select line |
|---|---|
DAT(31..24) | SEL(3) |
DAT(23..16) | SEL(2) |
DAT(15..08) | SEL(1) |
DAT(07..00) | SEL(0) |
In one expression: SEL[n] covers DAT[GRAN*n +: GRAN].
4. SEL Is Not Write-Only
This is the claim most often got wrong, and the signal description settles it in one sentence.
The select output array
SEL_O()indicates where valid data is expected on theDAT_I()signal array during READ cycles, and where it is placed on theDAT_O()signal array during WRITE cycles.
Both directions. The slave-side description says the same thing from the other end: SEL_I() indicates where data is placed on DAT_I() during writes, and where it should be present on DAT_O() during reads.
The two directions have very different consequences, though, and that asymmetry is real:
| a write | a read | |
|---|---|---|
| what an unselected lane means | do not touch this byte | nothing is expected here |
cost of ignoring SEL | a neighbour's data is destroyed | junk is returned on lanes nobody asked about |
| who is harmed | other software, silently, later | the caller, immediately, visibly |
"Should be present" is a weak obligation, and a simple register slave usually exceeds it by returning the whole word and letting the master take the lanes it asked for. That is implementation behaviour, permitted rather than required — the same shape as Chapter 12.4's finding that RULE 3.65 tells a consumer when to believe data rather than telling a producer what to drive.
The register bank in Chapter 13.5 does exactly that, and says so where it does it.
5. RTL — One Select Bit Becomes One Lane of Mask
// ─────────────────────────────────────────────────────────────────────────
// wb_sel_to_mask — one select bit becomes one lane's worth of mask bits.
//
// THE LANE BINDING IS NORMATIVE, not a house convention. RULE 3.100 binds
// data organization on a 32-bit port to the specification's organization32
// figure, and that figure lists, for 8-bit granularity:
//
// SEL(3) <-> DAT(31..24) SEL(1) <-> DAT(15..08)
// SEL(2) <-> DAT(23..16) SEL(0) <-> DAT(07..00)
//
// So SEL[n] covers DAT[GRAN*n +: GRAN]. That is a statement about WIRES and
// it does not change with endianness — the same figure lists BIG and LITTLE
// ENDIAN as two rows sharing one set of SEL<->DAT column headings. What
// endianness changes is which BYTE NUMBER of an operand lands on which lane
// (Chapter 13.4), never which select controls which wires.
//
// SEL WIDTH COMES FROM GRANULARITY, not from the port size alone. The
// SEL_O() description sets the array boundaries by "the granularity of a
// port", and gives the example of 8-bit granularity on a 64-bit port
// producing SEL_O(7..0). A 32-bit port with 32-bit granularity has ONE
// select line — also shown in organization32 — because the smallest operand
// it can carry is the whole word.
//
// This module is combinational and stateless. It is published mainly so
// that the expansion can be audited exhaustively (Chapter 13.1 sweeps all
// 16 values of a 4-bit select) rather than trusted.
// ─────────────────────────────────────────────────────────────────────────
module wb_sel_to_mask #(
parameter int unsigned DW = 32, // data port size in bits
parameter int unsigned GRAN = 8, // granularity in bits
localparam int unsigned SELW = DW / GRAN
) (
input logic [SELW-1:0] sel_i,
output logic [DW-1:0] mask_o
);
// Configuration legality. These are ENGINEERING CONSEQUENCES of the
// specification's definitions rather than numbered rules: a granularity
// that does not divide the port size cannot produce whole lanes, and the
// SEL_O() description defines the array in terms of that division.
initial begin
if (GRAN == 0)
$fatal(1, "wb_sel_to_mask: granularity must be non-zero");
if (DW == 0)
$fatal(1, "wb_sel_to_mask: data width must be non-zero");
if (DW % GRAN != 0)
$fatal(1, "wb_sel_to_mask: DW %0d is not a multiple of GRAN %0d",
DW, GRAN);
if (SELW == 0)
$fatal(1, "wb_sel_to_mask: computed select width is zero");
end
always_comb begin
mask_o = '0; // explicit default: no latch
for (int unsigned n = 0; n < SELW; n++)
mask_o[n*GRAN +: GRAN] = {GRAN{sel_i[n]}};
end
endmoduleReading it
The whole module is one loop, and the loop is the lane binding written down. mask_o[n*GRAN +: GRAN] = {GRAN{sel_i[n]}} replicates each select bit across its own lane and nowhere else.
SELW is a localparam in the parameter port list, derived from DW / GRAN and not overridable. A caller cannot set a select width that disagrees with the port it belongs to — the relationship comes from the SEL_O() description and is not a free choice.
The elaboration checks are ENGINEERING CONSEQUENCES, not numbered rules. The specification does not contain a rule saying "granularity must divide the port size"; it defines the select array by that division, so a configuration where it does not divide has no meaning to check against. Failing at elaboration is how that becomes visible.
The explicit mask_o = '0 default matters for the same reason it did in Chapter 12.1's decoder: an always_comb with a path that assigns nothing infers a latch.
6. The Data Word, Drawn
The solid arrows are fixed by RULE 3.100. The dashed arrows are this system's endianness — under big endian the bottom row reverses and nothing else in the figure moves.
7. Simulation — SIM A: One Select Bit Becomes One Lane
Ten representative patterns, then an exhaustive audit of all sixteen. The expansion is combinational, so each row is a function of its select value alone.
=== SIM A - one select bit becomes one lane of mask ===
32-bit port, 8-bit granularity, so SELW = 32/8 = 4.
RULE 3.100 binds SEL(n) to DAT(8n+7..8n).
SEL mask lanes carrying data
0000 0x00000000 none
0001 0x000000ff 0
0010 0x0000ff00 1
0100 0x00ff0000 2
1000 0xff000000 3
0011 0x0000ffff 1 0
1100 0xffff0000 3 2
0101 0x00ff00ff 2 0
1010 0xff00ff00 3 1
1111 0xffffffff 3 2 1 0
0101 is lanes 2 and 0. It is not 'five' and not a size.
exhaustive audit: 16 select values x 4 lanes
lanes expanded incorrectly: 0Reading it
Read the mask column as the select value with each bit stretched to eight. 0001 becomes 0x000000ff; 1000 becomes 0xff000000. Nothing is encoded and nothing is looked up — the mask is the pattern, replicated.
0101 is the row that does the most work. It is lanes 2 and 0, mask 0x00ff00ff, and it is not "five", not a size, and not an illegal pattern. Two lanes participate and they are not adjacent. A design that treats select as a size code cannot express this row at all — which is why Chapter 13.3 sweeps every pattern rather than the tidy ones.
0000 expands to an all-zero mask. No lane participates, so a write through it changes nothing. The specification says nothing about all-zero select — it is neither blessed nor forbidden, and this design simply lets the arithmetic produce the obvious answer. Claiming it is illegal would be inventing a rule; Chapter 13.5 shows it costing a transfer and achieving nothing, which is a fair description of what it is.
Then the audit: sixteen values, four lanes each, sixty-four independent checks, zero wrong. This is the cheapest possible test of a mask expander and it is exhaustive for this configuration — there is no seventeenth pattern. It catches reversed lane order, a wrong replication width, and off-by-one slicing, which are the three ways this eight-line module can fail.
SEL travels with the request, and means nothing outside it
8 cyclesSEL_O is qualified by STB_O under RULE 3.60, alongside ADR_O, DAT_O(), WE_O and the tags. Cycle 1 shows it driven with no strobe — the value is on the wires and it selects nothing, because there is no transfer for it to apply to.
The state row changes exactly once, at the acknowledged edge. SEL is spatial, but when it takes effect is the ordinary commit model from Module 7, and P4 in Section 10 says so.
8. What SEL Is Not
Not part of the address. ADR and SEL travel together and answer different questions. A transfer with SEL = 0010 still concerns the whole word at ADR; it just says one lane carries data. Chapter 13.4 shows two low address bits turning into SEL — and the point of that translation is that they stop being address.
Not a write enable for the register. WE_O says the transfer is a write. SEL says which lanes of it participate. A write with SEL = 0000 is still a write — it is a write that delivers nothing.
Not a termination. SEL is qualified by STB_O under RULE 3.60, alongside ADR_O, DAT_O(), WE_O and the tags. It is part of the request, and it means nothing outside a presented transfer.
Not a size field. Four bits that happen to hold 0011 do not encode "two". Chapter 13.3 measures 0101 and 1010 precisely because a size encoding has no way to represent them.
Not a guarantee that the target honours it. SEL_I tells a slave which lanes participate. A slave that ignores it is broken and the bus cannot tell — Chapter 13.3 and Chapter 13.5 measure two such targets, both of which acknowledge normally.
9. Failure Modes and Discriminating Evidence
Symptom: a byte write changes the byte next to the intended one.
Candidate causes. A mask expanded with the wrong replication width, or lane slicing off by one — sel_i[n] driving mask[n*GRAN-1 +: GRAN].
Discriminating evidence. A walking-one test: write each lane separately from a word whose four bytes differ. SIM A's exhaustive audit is the static form, and Chapter 13.2's SIM B is the dynamic one. The displacement tells you which: a consistent one-lane shift is a slicing bug, an inconsistent one is an expansion bug.
Symptom: the byte lanes appear reversed.
Candidate causes. Lane numbering reversed in the mask expansion, or a software endianness assumption that disagrees with the SoC's.
Discriminating evidence. Write 0x01 through SEL = 0001 and read the whole word. Little endian puts it at DAT[7:0]; if it appears at DAT[31:24] with SEL[0] asserted, the expansion is reversed. If the hardware is right and software still sees it reversed, the disagreement is about byte numbering, not wiring — and RULE 2.15 says the datasheet should have settled it.
Symptom: a peripheral with 32-bit granularity ignores byte writes.
Candidate causes. Not a bug. A port whose granularity equals its size has one select line and cannot transfer a byte.
Discriminating evidence. The datasheet's granularity field, which RULE 2.15 requires. The interconnect cannot add byte enables to a core that has none, and a sub-word write to such a target has to be handled above it — or not attempted.
Symptom: a read returns data on lanes that were not selected.
Candidate causes. Usually none — this is normal.
Discriminating evidence. SEL_I says where data should be present, not where it must be absent. A slave returning the full word is exceeding a weak obligation, and the master should be taking only the lanes it asked for. The bug, if there is one, is in a master that consumes unselected lanes — the same shape as Chapter 12.4's ORed response mux.
10. Verification
// ─────────────────────────────────────────────────────────────────────────
// wb_sel_props — what a select pattern promises about a write.
//
// NOTE ON EXECUTION: Icarus Verilog does not support SVA. These properties
// were reviewed by inspection and are NOT claimed to have been executed.
// The numbers in this chapter come from procedural checks, which Icarus
// does run — SIM D's 64-lane sweep is P2 and P3 in executable form.
//
// Every property is labelled. A SPEC-DERIVED property holds for any
// conformant system; a LOCAL one holds for THIS design and could be false
// of another that is equally conformant.
// ─────────────────────────────────────────────────────────────────────────
module wb_sel_props #(
parameter int unsigned DW = 32,
parameter int unsigned GRAN = 8,
localparam int unsigned SELW = DW / GRAN
) (
input logic clk_i,
input logic rst_i,
input logic we_i, // qualified, committed write
input logic [SELW-1:0] sel_i,
input logic [DW-1:0] dat_i,
input logic [DW-1:0] q_i, // register state
input logic [DW-1:0] mask_i
);
default disable iff (rst_i);
// P1 — SPEC-DERIVED (RULE 3.100 via the organization32 figure).
// Select bit n covers data bits GRAN*n +: GRAN, and nothing else. This is
// the lane binding the specification fixes; it does not vary with
// endianness and it is not a house convention.
genvar gn;
generate
for (gn = 0; gn < SELW; gn++) begin : g_mask
property p_mask_lane;
@(posedge clk_i)
mask_i[gn*GRAN +: GRAN] == {GRAN{sel_i[gn]}};
endproperty
a_mask_lane: assert property (p_mask_lane);
end
endgenerate
// P2 — LOCAL RTL POLICY (ordinary read/write semantics).
// A selected lane takes the written data. Stated per lane rather than
// word-wide, because the word-wide form would be satisfied by an
// implementation that got one lane right and compensated elsewhere.
generate
for (gn = 0; gn < SELW; gn++) begin : g_sel
property p_selected_updates;
@(posedge clk_i) (we_i && sel_i[gn]) |=>
(q_i[gn*GRAN +: GRAN] == $past(dat_i[gn*GRAN +: GRAN]));
endproperty
a_selected_updates: assert property (p_selected_updates);
end
endgenerate
// P3 — LOCAL RTL POLICY, and the one wb_broken_mask_reg violates.
// An UNSELECTED lane is unchanged. Not "written with zero", not "written
// with whatever DAT carried" — unchanged. This is the property that makes
// a partial write safe for the neighbouring bytes, which belong to other
// variables entirely.
generate
for (gn = 0; gn < SELW; gn++) begin : g_unsel
property p_unselected_preserved;
@(posedge clk_i) (we_i && !sel_i[gn]) |=>
(q_i[gn*GRAN +: GRAN] == $past(q_i[gn*GRAN +: GRAN]));
endproperty
a_unselected_preserved: assert property (p_unselected_preserved);
end
endgenerate
// P4 — LOCAL ARCHITECTURE (the commit model from Module 7).
// No write signal, no state change. SEL alone changes nothing: a select
// pattern on the wires outside a committed write is not a write.
property p_no_change_without_write;
@(posedge clk_i) (!we_i) |=> (q_i == $past(q_i));
endproperty
a_no_change_without_write: assert property (p_no_change_without_write);
endmoduleP1 is the only specification-derived property here, and it is worth seeing why the others are not. RULE 3.100 fixes the lane-to-data binding; nothing in Wishbone says what a register does with a selected lane, because Wishbone does not reach inside a target. P2, P3 and P4 are this design's semantics.
P2 and P3 are stated per lane rather than word-wide. q == (q_past & ~mask) | (dat & mask) would be satisfied by an implementation that got one lane wrong and compensated in another — the word-wide form checks the answer, the per-lane form checks the reasoning.
P4 is the commit model doing its work. A select pattern sitting on the wires is not a write. Module 7 established that state changes on a committed write and nowhere else, and SEL does not create an exception.
11. Common Mistakes
"SEL is the address of the byte."
Wrong mental model: one signal narrowing another.
What is true: they answer different questions, and the transfer concerns one word either way. Chapter 13.4 shows a byte address splitting into ADR plus SEL — a split, not a refinement, and the two halves go to different places.
"SEL = 0011 means a two-byte transfer."
Wrong mental model: a size encoding.
What is true: it means lanes 1 and 0 participate, which happens to be two adjacent bytes. 0101 also has two bits set and is not two adjacent bytes at all. SIM A prints both. A bitmap with four bits has sixteen values; a size field would need three.
"SEL only matters on writes."
Wrong mental model: byte enables are a write feature.
What is true: the signal description covers both directions explicitly. The asymmetry is in the consequence, not the applicability — an ignored SEL destroys data on a write and returns junk on a read.
"SEL[0] is the lowest-addressed byte."
Wrong mental model: lane number equals byte number.
What is true: only under little endian. The normative figure gives both orderings against one set of lane bindings, and RULE 2.15 requires a datasheet to say which a core uses. This course says little endian everywhere it matters.
"Any port can do byte writes."
Wrong mental model: granularity is a detail.
What is true: a 32-bit port with 32-bit granularity has one select line. The specification is direct: the smallest operand a 16-bit-granular port can pass is a 16-bit word, and "an 8-bit operand cannot be transferred." Byte enables are a property a core has or does not.
12. Interview Reasoning
Because the address names a word and software often wants part of one.
The address resolution stops at the port width. On a 32-bit byte-granular port, ADR is a word address — the specification's own example is ADR_O(n..2), and the two low bits are not on the wires. Four byte addresses therefore share one ADR, and something else has to distinguish them.
SEL is that something. It states which lanes of the addressed word carry data that matters, so one transfer can update one byte and leave the other three alone.
The alternatives are worth naming, because they show what SEL buys. A read-modify-write is two transfers with a window in between. A separate address per byte multiplies the address space. Writing the whole word destroys the neighbours. SEL is one transfer with no exposure and no address-space cost.
Worth adding: it also works in the read direction, where it tells the target which lanes the master will actually consume.
13. Understanding Check
No, and rejecting it would mean inventing a rule the specification does not contain.
What the arithmetic does. Every select bit is zero, so every lane's mask bits are zero. The expansion has no special case; it produces the obvious answer.
What it means for a transfer. No lane participates. A write through it delivers nothing and changes nothing — the transfer still happens, is still acknowledged, and still costs a clock. Chapter 13.5 shows one in a full sweep doing exactly that.
What the specification says about it: nothing. There is no rule blessing it and none forbidding it. So the honest treatment is to let it mean what it arithmetically means and note that it accomplishes nothing.
Whether a target should refuse it is a different question — a datasheet could say so under RULE 2.15, and that would be a local policy, clearly labelled.
14. What's Next
The signal is established: a lane bitmap whose width comes from granularity, whose binding to the data wires is normative, and whose byte numbering is a system convention.
Nothing has used it yet. A mask is not a register.
How does a select pattern become write-enable logic inside a target?
Chapter 13.2 — Byte Enables builds a byte-enabled register two ways, measures a walking-one write across all four lanes, and shows what the two styles buy each other. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- Related topic
SEL_O
How a 32-bit bus writes one byte without disturbing the other three: byte lanes, the per-lane conditional write, where endianness actually lives, and why ignoring the mask destroys data the transfer never named.
- Related topic
Data Transfer
Selection routes a request; it does not move a value. Write data fans out, read data fans in through a multiplexer that every target widens, byte lanes decide which parts of a word participate, and a register narrower than the bus has to be placed on a lane rather than merely connected.
- Related topic
Alignment
The two address bits the bus does not carry become the select pattern. Measured across every offset and size, including the misaligned operand that fits in one transfer.
- Related topic
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.
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.
