Wishbone · Module 23
Address Decoder RTL
B3 never describes a decoder but does say whose job it is. A one-digit mask error steals sixteen addresses, and probing inside the overlap shows nothing wrong — the damage is at an address that used to be a hole.
Chapter 16.4 built wb_split2: it decodes one address bit into two slaves. Module 19's SoC built a tree of them, which works, and does not generalise. A five-slave map with unequal region sizes is not a balanced binary tree, and forcing it into one is how address maps acquire holes.
This chapter builds the general case: N regions, any size, any alignment, with the two failure modes a real map actually has — addresses that match nothing, and addresses that match twice.
1. B3 Does Not Describe A Decoder. It Does Say Whose Job It Is.
Search Wishbone B3 for an address decoder and you will not find one. What you will find is the specification quietly assigning the work to somebody, in its Partial Address Decoding section:
"each SLAVE decodes only the range of addresses that it requires... The remaining address bits are decoded by the interconnection system."
That sentence is the entire normative basis for this chapter. It establishes three things and no more:
- A slave decodes only the bits it needs. Chapter 23.3's bank uses
[ADR_I][3:0]and ignores everything above — that is not laziness, it is the model B3 describes. - Somebody else decodes the rest. That somebody is the interconnect.
- Nothing about how.
Everything below — the table format, first-match priority, what happens to an unmapped address — is local policy, and this chapter names each one as it arrives rather than presenting them as though the specification required them.
2. Base And Mask, And Why The Mask Is The Size
A region matches when:
// (adr & mask) == (base & mask)The mask says which bits must match, and its zeros are the region's size. A mask of 0xF00 with base 0x200 selects 0x200…0x2FF — 256 words, because eight bits are free.
This is why base/mask generalises where a split tree does not. The four regions in the running example are deliberately unequal:
| region | range | mask | size |
|---|---|---|---|
| 0 — ROM | 0x000…0x0FF | 0xF00 | 256 words |
| 1 — RAM | 0x400…0x4FF | 0xF00 | 256 words |
| 2 — timer | 0x800…0x80F | 0xFF0 | 16 words |
| 3 — gpio | 0x810…0x81F | 0xFF0 | 16 words |
A 256-word region and a 16-word region, adjacent regions and distant ones, in one flat table. wb_split2 nested to any depth cannot express that map.
The table itself is packed, for the same portability reason as Chapter 23.3's policy table:
// Packed rather than unpacked because Icarus -g2012 rejects unpacked
// arrays as parameters.And the match is a bounded loop over all N regions in parallel — every region is tested, not just until one hits:
logic [7:0] raw; // every region that matches, not just the winner
logic [7:0] onehot; // first match onlyraw is the load-bearing variable in this module. Keeping every match, rather than stopping at the first, is what makes Section 6 possible.
3. First Match Wins — And That Is A Choice
// first match wins - the lowest set bit of raw
always_comb begin
onehot = 8'd0;
for (i = 7; i >= 0; i = i - 1)
if (raw[i]) onehot = (8'd1 << i);
endDeterministic, cheap, and it makes an overlap silent. The second region simply never sees those addresses. So the decoder reports overlaps as well as surviving them:
// more than one region claimed this address
logic multi;
assign multi = any && ((raw & (raw - 8'd1)) != 8'd0);That expression is the standard "more than one bit set" idiom: subtracting one from a value clears its lowest set bit, so a non-zero AND means at least two were set.
An alternative policy is to make overlap an elaboration error. That is stronger, and it cannot describe a legitimately shadowed region — a debug aperture deliberately placed over part of RAM, for instance. This module reports rather than refuses, and says so, which is a different answer from the one many real interconnect generators give.
4. RULE 3.30 Belongs Here, Once
// RULE 3.30 lives here too: a decoder that selects a slave when
// [CYC_I] is negated would make that slave respond outside a cycle.
// The gate is applied once, here, so no downstream slave has to
// remember it.
logic active;
assign active = cyc_i && stb_i;RULE 3.30 — "SLAVE interfaces MAY NOT respond to any SLAVE signals when [CYC_I] is negated." — is a slave's obligation, and Chapter 23.2 showed how easily a slave gets it wrong.
Applying the gate in the decoder does not relieve the slave of the rule. It means a correctly-written slave and a carelessly-written one behave identically behind this interconnect, which is worth having. Section 7 of Chapter 23.2 showed that the careless slave's bug is invisible until something puts a stray strobe in front of it — and one of the things that can do that is a decoder without this line.
5. Every Address Must Go Somewhere
B3 says nothing about an address matching no slave. Three policies exist:
// 1. let it hang - no termination ever arrives. B3 has no
// timeout, so the master waits forever.
// 2. acknowledge it - the write vanishes, the read returns garbage,
// and nothing reports anything.
// 3. ERROR it - a default port answers ERR_O.Policy 3 is the only one that tells anybody, and it is what default_sel_o exists for. Policy 1 is what you get by accident if you never think about it, and it is a hang — which Chapter 12.6 has to clean up from the master's side because the bus has no timeout of its own.
Sweeping the entire 4096-address space:
THE WHOLE 4096-ADDRESS SPACE, SWEPT
decoded to a region 544
reached the default 3552
matched two regions 0
total 4096
-> EVERY ADDRESS DECODES SOMEWHERE. That is not
automatic: it is what the default port is for.544 mapped, 3552 holes, and the two sum to 4096 exactly. That total is the point of the sweep — it is a completeness proof, not a statistic. 256 + 256 + 16 + 16 = 544, so the arithmetic of the map itself is checked at the same time.
Boundary by boundary:
address what decodes to verdict
0x000 ROM first word region 0 PASS
0x0ff ROM last word region 0 PASS
0x100 one past ROM DEFAULT PASS
0x3ff just below RAM DEFAULT PASS
0x400 RAM first word region 1 PASS
0x4ff RAM last word region 1 PASS
0x800 timer first region 2 PASS
0x80f timer last region 2 PASS
0x810 gpio first (adjacent) region 3 PASS
0x81f gpio last region 3 PASS
0x820 one past gpio DEFAULT PASS0x80F and 0x810 are the interesting pair — adjacent addresses in adjacent regions, the classic place for an off-by-one in a mask.
6. The Default Port Is A Slave Somebody Has To Write
default_sel_o is an output, not a behaviour. Policy 3 only reports the unmapped access if something is attached to that wire, and that something is the smallest legal Wishbone slave there is:
// The default responder. RULE 3.40's slave minimum is [ACK_O], [CLK_I],
// [CYC_I], [STB_I], [RST_I] - and this one does not even use [ACK_O].
module wb_default_slave (
input logic cyc_i, stb_i,
output logic ack_o,
output logic err_o
);
// RULE 3.45: one-hot. ACK is never asserted, so ERR alone is trivially
// one-hot. RULE 3.30: both outputs are gated by cyc_i, so this slave
// is silent outside a cycle like any other.
assign ack_o = 1'b0;
assign err_o = cyc_i && stb_i;
endmoduleTwo continuous assigns, no state, no clock. It answers combinationally, which PERMISSION 3.30 allows and which B3 §4.1 warns about — but a default responder is the one slave where the combinational path is hard to object to, because it is on the path only for accesses that are already broken.
Driven from the decoder's own default_sel_o, it answers:
THE DEFAULT PORT, ACTUALLY ANSWERING
address decodes to default ACK_O default ERR_O
0x000 region 0 0 0
0x850 DEFAULT 0 1
-> ERR_O asserted, ACK_O silent. RULE 3.45 is
satisfied trivially because this slave never
asserts ACK_O at all, and RULE 3.30 because
both outputs are gated by the decoder's own
CYC/STB gate.0x000 is mapped, so the default port stays silent; 0x850 is not, and gets [ERR_O].
If you forget to instantiate it, the decoder is still correct and the system still hangs, because the unmapped access now reaches nothing at all. That is policy 1 arriving by omission, and the failure looks identical to a dead slave.
WITHOUT THIS MODULE INSTANTIATED the decoder is
still correct and the unmapped access still hangs,
because nothing answers it. default_sel_o is an
OUTPUT, not a behaviour.7. A Loop That Icarus Refused, And What It Taught
The winning region index started life as the obvious loop and did not survive:
// The winning index, as a ternary chain over CONSTANT bit selects.
//
// A loop with `onehot[i]` would be shorter and makes Icarus warn that
// the process becomes sensitive to every bit of i. Constant selects
// have no such problem, and for a bounded N the chain is no less
// readable than the loop it replaces.
assign index_o = onehot[0] ? 3'd0 :
onehot[1] ? 3'd1 :
onehot[2] ? 3'd2 :The warning is "constant selects in always_* processes are not fully supported", and it fires for any variable bit-select or part-select inside an always_* block — not only in the decoder. It appeared four separate times while building this module, in the register bank's policy lookup, its read multiplexer, this index encoder, and the interconnect's forward route.
The fix is the same every time: move the indexing into a continuous assign, which has no sensitivity list to get wrong. It is a simulator limitation rather than a language rule, and a design that avoids it is portable to more tools than one that does not.
8. One Digit, Sixteen Stolen Addresses
Now break it the way real maps break. Region 3's mask becomes 0xF00 instead of 0xFF0 — one hexadecimal digit — so gpio now claims all of 0x800…0x8FF, including the timer.
=== SIM H - overlap detection ===
correct map addresses matching two regions 0
overlapping addresses matching two regions 16
-> 16 addresses are claimed twice. FIRST MATCH WINS,
so region 2 keeps 0x800..0x80F and region 3
silently never sees them.Here is the part that catches people. The instinct is to probe an address inside the overlap:
0x805 broken map -> region 2
correct map -> region 2 (unchanged:
first match wins, so the timer keeps it
and gpio is the one that lost)0x805 behaves identically in both maps. First match wins, the timer has the lower index, and nothing appears wrong. An engineer checking the overlap by probing inside it will conclude the map is fine.
The damage is somewhere else entirely:
0x850 broken map -> region 3
correct map -> DEFAULT (an ERR)
THAT is the address that changed meaning. A
write that used to be reported as an error now
lands silently on gpio.An address that used to be a hole is now a peripheral. The access that previously came back as
[ERR_O]— visible, logged, debuggable — now completes successfully against the wrong device.
And the map still passes the completeness check:
map mapped unmapped double-claimed
clean 544 3552 0
overlapping 768 3328 16768 mapped and 3328 unmapped still sum to 4096. A design rule that only asked "does every address go somewhere?" would pass this map without comment. That is why the decoder reports overlap as a separate output rather than folding it into a general health check.
9. The Design Rules This Chapter Leaves You With
| check | what it catches | what it misses |
|---|---|---|
| mapped + unmapped = total | a region that decodes nowhere | overlap — both maps pass |
| overlap count = 0 | two regions claiming one address | a region at the wrong base entirely |
| every boundary probed | off-by-one in a mask | a hole between two correct regions |
| unmapped reaches the default | a silent hang | whether the default is wired up |
No single one of these is sufficient, which is the general shape of every checker in this module: Chapter 23.2's conformance monitor caught its own five rules and was blind to a controller that lost twelve transfers.
10. What This Decoder Does Not Do
- No runtime reconfiguration. The table is a parameter; there is no way to move a region after elaboration.
- No more than 8 regions. The vectors are 8 bits wide. Widening them is mechanical; making the priority encoder scale gracefully past that is not.
- No security or privilege checking. Every master sees every region.
- No elaboration-time overlap error. Stated in Section 3 as a deliberate choice, not an omission.
Next: Chapter 23.6 — Interconnect RTL puts the decoder to work routing two masters to four slaves, and finds that the choice between a shared bus and a crossbar is settled by something other than the reason everybody gives for it.
Continue learning
Related tutorials
- Related topic
ADR_O
A Wishbone address names a word, not a byte, and means nothing unless the strobe qualifies it: address width, decode, the master-offset split, and the stability obligation RULE 3.60 imposes.
- Related topic
Address Phase
Classic has one handshake, not an address channel. The address names a device and a register inside it, exists in three representations, and must stay coherent for the whole transfer.
- 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.
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.
