Wishbone · Module 12
Address Maps
An address map is a contract with five signatories and only one of them is checked by a compiler. Measured at the four addresses per region where decode bugs live.
Chapter 12.1 built a decoder and handed it a map as a parameter. The map arrived without justification: three bases, three sizes, no explanation of why those numbers and not others.
Where does an address map come from, and what distinguishes a good one from one that merely works?
1. The Map, as the Document Writes It
The running SoC's map in full, in both units, with the two facts that a table of bases alone cannot express.
| target | byte base | byte last | size | word base | word last | implemented |
|---|---|---|---|---|---|---|
| RAM | 0x0000_0000 | 0x0000_0FFF | 4 KiB | 0x0000_0000 | 0x0000_03FF | 256 words |
| (gap) | 0x0000_1000 | 0x3FFF_FFFF | ~1 GiB | — | — | nothing |
| GPIO | 0x4000_0000 | 0x4000_0FFF | 4 KiB | 0x1000_0000 | 0x1000_03FF | 2 registers |
| TIMER | 0x4000_1000 | 0x4000_1FFF | 4 KiB | 0x1000_0400 | 0x1000_07FF | 2 registers |
| (reserved) | 0x4000_2000 | 0x4000_2FFF | 4 KiB | — | — | nothing yet |
| (gap) | 0x4000_3000 | 0xFFFF_FFFF | ~3 GiB | — | — | nothing |
The byte last column is the one that prevents bugs. A map that prints only base and size makes every reader compute the last address themselves, and the decoder has to compute it too — so the same arithmetic happens in two places with no way to compare them. Chapter 12.1's decoder precomputes LAST for exactly this reason: it is the number that can be read against the document.
The gap rows are not padding. An address map that lists only what exists cannot be checked for completeness, because the reader cannot tell a deliberate hole from a forgotten region. Naming the gaps makes "nothing is here" an assertion rather than an absence.
2. What Makes a Map Good
Six properties, and only the first is non-negotiable.
Non-overlap is a correctness requirement. Two windows containing one address means two owners, and every guarantee downstream fails. It is the only property on this list that is not a trade-off, and Chapter 12.3 measures what its absence costs.
Natural alignment is an implementation enabler. A window whose base is a multiple of its size can be decoded by prefix comparison instead of magnitude comparison. It buys cheaper logic, and it costs address space — a 4 KiB window must start on a 4 KiB boundary, so the space between windows is not free to use.
Window sizing is a forecast, not a measurement. GPIO implements two registers and reserves a thousand. The reservation is a bet that the block will grow, and it is a cheap bet in a 32-bit space and an expensive one in a 16-bit space.
Grouping is for humans. Putting peripherals at 0x4000_xxxx and memory at 0x0000_xxxx means an engineer reading a trace can classify an address before decoding it. The hardware does not care; the person debugging at 2 a.m. does.
Stability is for software. A base that never moves lets a driver ship a constant. Moving a peripheral one window "to tidy the map" invalidates every binary that ever touched it.
Compactness competes with all of the above. A packed map wastes no space and leaves no room; a sparse one leaves room and cannot be decoded by a few high bits.
3. The Map as a Picture
The left column is what the map reserves; the right column is what exists. Read the dashed edges as "reserves, and mostly does not use" — that ratio is the subject of Chapter 12.7, and it is visible here as three narrow boxes against three wide ones.
The two unmapped bands are most of the picture and almost all of the space. Drawn to scale they would be the entire diagram; the map's interesting part is a rounding error in its own address space.
4. The Four Addresses That Matter
Interior addresses do not test a decoder. An address in the middle of a window passes on a correct decoder and on one with an off-by-one at either end. Every range bug lives at a boundary, so the test is four addresses per region and nothing else:
| probe | expected | catches |
|---|---|---|
BASE - 1 word | outside | a lower bound that is too low |
BASE | inside, offset 0 | a lower bound that is too high |
LAST word | inside, offset max | < BASE + SIZE - 1, which rejects the last word |
LAST + 1 word | outside | <= BASE + SIZE, which admits one too many |
Four probes per region, twelve for this map. That is a complete boundary test of a three-target SoC, and it runs in microseconds.
"Outside" does not mean "unmapped". In a densely packed map the address one word above a window is the first address of the next one. SIM B measures both cases in the same table, and the difference matters: an off-by-one between adjacent windows produces a misroute, while the same error at an isolated window produces an unmapped access. One is silent; the other is loud.
5. Simulation — SIM B: Every Boundary, Both Decode Forms
Twelve boundary probes, plus a 108-address audit. The same map is given to two independently written decoders — the range form from Chapter 12.1 and the mask form Chapter 12.3 builds — and every probe is checked against both.
=== SIM B - every window boundary, both decode forms ===
region position byte adr sel target
RAM one word below 0xfffffffc 000 -none-
RAM first valid 0x00000000 001 RAM offset 0x000
RAM last valid 0x00000ffc 001 RAM offset 0x3ff
RAM one word above 0x00001000 000 -none-
GPIO one word below 0x3ffffffc 000 -none-
GPIO first valid 0x40000000 010 GPIO offset 0x000
GPIO last valid 0x40000ffc 010 GPIO offset 0x3ff
GPIO one word above 0x40001000 100 TIMER
TIMER one word below 0x40000ffc 010 GPIO
TIMER first valid 0x40001000 100 TIMER offset 0x000
TIMER last valid 0x40001ffc 100 TIMER offset 0x3ff
TIMER one word above 0x40002000 000 -none-
addresses probed 108
two targets at once 0
unmapped disagreed 0
range vs mask mismatch 0Reading it
Three rows in that table are worth more than the other nine.
GPIO one word above is 0x4000_1000, and it selects TIMER. Not unmapped — the next target. GPIO and TIMER are adjacent, so the address immediately past one window is the first address of the next. An off-by-one at GPIO's upper bound would not produce an error; it would produce a GPIO access to a TIMER register, acknowledged, with plausible data. That is the misroute described in Section 4, and this row is where it would appear.
TIMER one word below is the same address seen from the other side, 0x4000_0FFC, and it selects GPIO. The two rows are the same boundary reported twice, which is exactly what a boundary between adjacent windows is.
RAM one word below is 0xFFFF_FFFC. RAM's base is zero, so one word below it wrapped to the top of the address space — which is itself unmapped, so the row reads correctly by luck rather than by design. The probe is still worth running: on a map whose lowest window sat above a populated region, the wrap would land somewhere real.
The offsets confirm the arithmetic in both directions. first valid gives offset 0x000 and last valid gives 0x3FF for every 4 KiB window — 1024 words, numbered 0 to 1023, which is what a correctly computed LAST produces and what BASE + SIZE would not.
Then the audit lines, which are the part that generalises.
108 addresses probed, 0 with two targets at once. The one-hot guarantee, measured rather than asserted, across every boundary and a sweep of the regions between them.
0 disagreements between unmapped and an empty select vector. The two outputs are the same fact stated twice, and a decoder in which they could disagree would be one where unmapped was computed independently rather than derived.
0 mismatches between the range form and the mask form. Two decoders, written differently, agreeing on all 108 addresses. Chapter 12.3 explains exactly which properties of this map make that agreement possible — and builds a window where it fails.
6. Failure Modes and Discriminating Evidence
Symptom: firmware reads a peripheral and gets a different peripheral's registers.
Candidate causes. The firmware header and the RTL parameters disagree about a base. A window moved and only one copy was updated.
Discriminating evidence. Compare the address in the firmware constant against the select vector the hardware produced. If the hardware selected the target whose window contains that address, the hardware is right and the header is stale. The map is not the arbiter here — the RTL parameters are, because they are what the silicon does.
Where the fault sits: the map's second signatory, not the first.
Symptom: a region works for every register except the last.
Candidate causes. An upper bound written as < BASE + SIZE - 1, which excludes the final word.
Discriminating evidence. Probe LAST and LAST - 1 word. If the second is inside and the first is outside, the bound is short by exactly one. The boundary table in SIM B is this test, and it takes four addresses to be certain.
Symptom: an access lands in the right target but at the wrong offset.
Candidate causes. A correct sel and a wrong base used for the subtraction — which happens when the offset is computed from a different constant than the predicate.
Discriminating evidence. global - base computed by hand against the reported offset. A constant difference across every access in that window means one base, used twice, with two different values. The cure is structural: Chapter 12.1's decoder derives both from wbase_of(), so they cannot disagree.
Symptom: adding a peripheral breaks an existing one.
Candidate causes. The new window overlaps an old one, and the decoder resolves the tie by loop order.
Discriminating evidence. The select vector for an address in the old peripheral. Two bits set is conclusive. If the decoder has the elaboration check, this symptom is impossible — the build fails instead, which is the entire argument for having it.
7. Common Mistakes
"The map is documentation, so it can be fixed later."
Wrong mental model: the RTL is the design and the map describes it.
What is true: the map is the design, and the RTL is one of five things that implement it. Firmware, tests, documentation and debug traces all encode the same numbers. Changing the RTL alone does not change the map; it forks it.
"Sizes should be as small as the hardware needs."
Wrong mental model: reserved space is wasted space.
What is true: reserved space is the option to grow without moving. GPIO's 4 KiB window costs nothing in a 4 GiB space and buys the ability to add 1022 registers without touching firmware. In a 64 KiB space the same reservation would be extravagant — which is why this is a trade-off and not a rule.
"Alignment is an optimisation, so it can be skipped."
Wrong mental model: alignment only affects decoder cost.
What is true: it determines which decode forms are available at all. Chapter 12.3 shows that a mask decode on an unaligned window silently decodes a different region. The map's alignment is a precondition of an implementation choice, and the decoder that relies on it must check it rather than assume it.
"Adjacent windows are safer than separated ones because there are no holes."
Wrong mental model: holes are the hazard.
What is true: adjacency turns an off-by-one into a misroute instead of an error. SIM B's GPIO one word above row is a TIMER selection. With a gap between the windows, the same off-by-one produces an unmapped access, which the default responder reports loudly. Neither layout is wrong, but they fail differently, and the packed one fails quietly.
8. Interview Reasoning
Overlap, then alignment, then the last address of every region.
Overlap first because it is the only correctness failure. Everything else on the list is a trade-off; two owners for one address is simply broken. It is also mechanically checkable — pairwise, at elaboration — so there is no reason to leave it to review.
Alignment second because it gates implementation choices. A window that is not naturally aligned cannot be decoded by prefix comparison. That may be fine, but it has to be a decision rather than something discovered when the cheap decoder produces wrong answers.
The last address third because it is where the documentation and the RTL diverge. A map listing base and size makes every reader do the arithmetic. Ask for the LAST column and compare it against the decoder's — the two are computed independently, and if they agree, the boundary is probably right.
A strong answer adds: ask what is deliberately empty. A map with unexplained gaps is one nobody can safely change.
9. Understanding Check
No. That is the correct answer, and the reason the probe is worth running.
GPIO's window ends at byte 0x4000_0FFF and TIMER's begins at 0x4000_1000. They are adjacent, so the first address outside GPIO is the first address inside TIMER. A decoder that reported unmapped there would be wrong.
What the probe actually establishes is where the seam is. If GPIO's upper bound were one word too high, this row would show MULTIPLE or GPIO, and either would be a defect. The test is not "is it unmapped" but "is it exactly the next thing".
And this is why adjacency deserves a mention in the map's rationale. An off-by-one here produces an acknowledged access to the wrong peripheral rather than an error — the same mistake, made silent by the layout.
10. What's Next
The map is a contract now rather than a table: non-overlapping by construction, aligned on purpose, with its gaps named and its last addresses written down.
Turning it into logic is a separate problem, and there is more than one correct answer.
Which decode form should this map use, and what does each one require of it?
Chapter 12.3 — Decoder Logic builds range, mask and case decoders from the same parameters, measures where they agree, and builds the window where the cheap one silently decodes something else. The full path is on the Wishbone curriculum index.
Continue learning
Related tutorials
- 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.
- Related topic
Address Decoding
A bus address answers two questions, not one. Measured across a three-target SoC, including the boundary where one window ends and the next begins.
- 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
LiteX
What a SoC generator settles that B3 leaves open — word addressing as a default, a separate narrow CSR bus with a named Wishbone bridge, and a CPU base class whose fields are an inventory of unspecified things.
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.
