Skip to content
VLSI Mentor

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.

targetbyte basebyte lastsizeword baseword lastimplemented
RAM0x0000_00000x0000_0FFF4 KiB0x0000_00000x0000_03FF256 words
(gap)0x0000_10000x3FFF_FFFF~1 GiBnothing
GPIO0x4000_00000x4000_0FFF4 KiB0x1000_00000x1000_03FF2 registers
TIMER0x4000_10000x4000_1FFF4 KiB0x1000_04000x1000_07FF2 registers
(reserved)0x4000_20000x4000_2FFF4 KiBnothing yet
(gap)0x4000_30000xFFFF_FFFF~3 GiBnothing

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 running system's address map drawn from low addresses at the top to high addresses at the bottom. RAM occupies the first four kilobytes from 0x00000000, of which only the first kilobyte is implemented storage. A gap of roughly one gigabyte follows, owned by nothing. The GPIO block occupies four kilobytes at 0x40000000 and the timer four kilobytes at 0x40001000, each implementing two registers. A four-kilobyte region at 0x40002000 is reserved for a future peripheral. Everything above, roughly three gigabytes, is unmapped.RAM — 4 KiB window0x0000_0000 … 0x0000_0FFF1 KiB implemented256 words of storageunmapped — ~1 GiB0x0000_1000 … 0x3FFF_FFFFGPIO — 4 KiB window0x4000_0000 … 0x4000_0FFF2 registersoffsets 0x000, 0x001TIMER — 4 KiB window0x4000_1000 … 0x4000_1FFF2 registersoffsets 0x000, 0x001reserved — 4 KiB0x4000_2000, for growthunmapped — ~3 GiB0x4000_3000 … 0xFFFF_FFFF12

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:

probeexpectedcatches
BASE - 1 wordoutsidea lower bound that is too low
BASEinside, offset 0a lower bound that is too high
LAST wordinside, offset max< BASE + SIZE - 1, which rejects the last word
LAST + 1 wordoutside<= 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === 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    0

Reading 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

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.