Every software bug that starts with a wrong address constant starts here: a header typed from a presentation, a base copied from last year's chip, a linker script nobody regenerated. The hardware team experiences these as mysterious silicon failures weeks later. The fix is boring and total: generate every software-facing artifact from the map table, stamp each with the map version, and check the agreement in the build. This lesson specifies those artifacts for NanoSoC.
Three artifacts leave the integration team. The C header carries one base define per region and per slot, plus the register offsets for each block, so driver code names registers instead of numbering them. The linker constants carry the memory regions with their lengths and attributes, so the reset vector, the stack top at one past SRAM0 and the image base cannot drift from the map. The map document is the human version of the same table: regions with attributes, holes with their responders, aliases marked as aliases, and the access rules of the next lesson printed where a driver author will actually read them. All three are regenerated, never edited: the header below is illustrative generic format emitted from the NanoSoC table.
# not-run: format fragment, not executable here
/* nanosoc_map.h - generated from the NanoSoC map table, map version 1.2. Do not hand-edit. */
#define BOOTROM_BASE 0x00000000u
#define SRAM0_BASE 0x20000000u
#define SRAM1_BASE 0x20010000u
#define DMA_BASE 0x40000000u
#define SBR_BASE 0x40001000u
#define RSTPWR_BASE 0x40006000u
#define SRAM0_STACK_TOP 0x20010000u /* one past the end of SRAM0 */Generation alone is not the deliverable; the agreement check is. The script below is the smallest honest version: it holds the map table, renders the header text the way the generator would, and asserts every base in the header equals the table, catching the stale-header failure where one artifact was regenerated and the other was not. Run it in the build and a mismatched release cannot be produced, only a failed build.
MAP = {"BOOTROM": 0x0000_0000, "SRAM0": 0x2000_0000, "SRAM1": 0x2001_0000,
"DMA": 0x4000_0000, "SBR": 0x4000_1000, "RSTPWR": 0x4000_6000}
def render_header(m):
lines = ["/* generated; do not hand-edit */"]
for name, base in m.items():
lines.append(f"#define {name}_BASE 0x{base:08X}u")
return chr(10).join(lines) + chr(10)
hdr = render_header(MAP)
for name, base in MAP.items():
assert f"#define {name}_BASE 0x{base:08X}u" in hdr
stale = render_header({**MAP, "SBR": 0x4000_2000}) # header from an old table
mismatch = [n for n, b in MAP.items() if f"#define {n}_BASE 0x{b:08X}u" not in stale]
assert mismatch == ["SBR"]
print("PASS: header agrees with table; stale SBR base detected")The map carries a version and every artifact prints it, as the growth lesson required. The compatibility rule is the one software feels: adding a region or filling a reserved slot is a minor change, because existing addresses keep their meanings; moving, resizing or re-purposing a region is major, breaks every binary built against it, and needs the same re-qualification as a new chip. The document states the rule on its first page, and the release process refuses to ship artifacts whose version stamps disagree with each other.
Header, linker constants and document all carry the same map version, quoted in the review record.
No hand-written address constants anywhere in the software tree; a search for hex bases outside generated files comes back empty.
The agreement check runs in the build and its failure blocks the release, not just warns.
Change one base in the table and rebuild: both artifacts must change together and the check must pass. Then deliberately stale-date one artifact and confirm the build fails, because a check that cannot fail is decoration. On the bench, the first software act is a reset readback sweep against the header values; the verification topic formalises it as a gate.
Because the next engineer under time pressure will edit the file in front of them. The banner plus regeneration makes the wrong action visible; process alone makes it invisible until silicon.