Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • The NanoSoC Tour
  • Reading Path
  • SoC Glossary10
  • SoC Metrics Reference
  • SoC Cheatsheet
  • What SoC Integration Is12
  • Architecture and Budgets21
  • Memory Maps and Address Decode26
  • Interconnect and Bus Fabrics26
  • Clock and Reset Architecture24
  • Power Architecture21
  • Interrupts, DMA and Peripherals15
  • Memories, Boot and Security21
  • IP Integration and Handoff21
  • Top-Level Verification and Bring-up21
  • Package, Pads and I/O18
  • Chip Signoff and Tapeout18
  • SoC Interview Prep16
  • Capstone: Integrate NanoSoC14
    • Capstone Brief
    • Stage 1: Memory Map and Interconnect
      • Map Table, Overlap and Alignment Check
      • Decode, Response Codes and the Alias
      • Link Table, Bandwidth and the Bottleneck
    • Stage 2: Clocks, Resets, Power
    • Stage 3: Interrupts and Boot
    • Stage 4: Verification and Signoff
Loading lesson

←Stage 1: Memory Map and Interconnect
  1. Topics
  2. SoC Design
  3. Capstone: Integrate NanoSoC
  4. Stage 1: Memory Map and Interconnect
  5. Map Table, Overlap and Alignment Check
On this page

    Part 14 of 14, lesson 3 of 14Part 14 of 14/Capstone: Integrate NanoSoC/Lesson 3 of 14All levels

    Map Table, Overlap and Alignment Check

    Build the ten-row map table, run the overlap and alignment checker, and catch planted bug 1.

    Why it matters

    Writing SBR CTRL at 0x4000_100C flips DMA data bit 0 with COUNT exactly right. Nothing in the symptom points at the map: the address is legal, the access completes, no error fires. The cause is an 8 KB DMA slot select (addr[15:13] == 0) that overlaps slots 0 and 1, so the write also lands on DMA offset 0x0C, which is KEY. Only a checker that proves no two windows overlap catches this class before silicon.

    Goal, inputs and steps

    • Goal: a ten-row map table that passes overlap and alignment checks.

    • Inputs: bases and sizes below; the engine's overlaps() and misaligned(); the APB slot selects as value and mask pairs on addr[15:0].

    • Steps: type the table; run the checker on the clean map; inject the 8 KB DMA window and confirm both the table check and the slot-select audit report it, and that the stray SBR write reaches the DMA KEY; restore and rerun.

    Region

    Base

    Size

    End

    BOOTROM

    0x0000_0000

    16 KB

    0x0000_3FFF

    SRAM0

    0x2000_0000

    64 KB

    0x2000_FFFF

    SRAM1

    0x2001_0000

    64 KB

    0x2001_FFFF

    DMA

    0x4000_0000

    4 KB

    0x4000_0FFF

    SBR

    0x4000_1000

    4 KB

    0x4000_1FFF

    UART..RSTPWR

    0x4000_2000..0x4000_6000

    4 KB each

    0x4000_6FFF

    The checker

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    MAP = [
        ("BOOTROM", 0x00000000, 0x4000),
        ("SRAM0", 0x20000000, 0x10000),
        ("SRAM1", 0x20010000, 0x10000),
        ("DMA", 0x40000000, 0x1000),
        ("SBR", 0x40001000, 0x1000),
        ("UART", 0x40002000, 0x1000),
        ("SPI", 0x40003000, 0x1000),
        ("GPIO", 0x40004000, 0x1000),
        ("INTC", 0x40005000, 0x1000),
        ("RSTPWR", 0x40006000, 0x1000),
    ]
    SECURE_ONLY = {"SRAM1", "RSTPWR"}
    SLOTS = ["DMA", "SBR", "UART", "SPI", "GPIO", "INTC", "RSTPWR"]
    T_CPU, T_SYS, T_PER = 1e9 / 192e6, 1e9 / 96e6, 1e9 / 24e6
    def end_of(base, size):
        return base + size - 1
    def overlaps(entries=MAP):
        bad = []
        xs = sorted(entries, key=lambda e: e[1])
        for a, b in zip(xs, xs[1:]):
            if b[1] < a[1] + a[2]:
                bad.append((a[0], b[0]))
        return bad
    def misaligned(entries=MAP):
        return [n for n, b, s in entries if b % s != 0]
    def decode(addr, slots=SLOTS):
        if addr >> 14 == 0:
            return "BOOTROM"
        if addr >> 16 == 0x2000:
            return "SRAM0"
        if addr >> 16 == 0x2001:
            return "SRAM1"
        if addr >> 16 == 0x4000:
            slot = (addr >> 12) & 0xF
            return slots[slot] if slot < 7 else "H3"
        if addr < 0x20000000:
            return "H1"
        if addr < 0x40000000:
            return "H2"
        return "H4"
    def response(addr, secure, slots=SLOTS):
        t = decode(addr, slots)
        if t in ("H1", "H2", "H4"):
            return "DECERR"
        if t == "H3":
            return "SLVERR"
        if t in SECURE_ONLY and not secure:
            return "SLVERR"
        return "OK"
    def sustained(peak, beats):
        return peak * beats / (beats + 1)
    
    # ---- this lesson starts here ----
    
    assert overlaps() == [] and misaligned() == []
    for name, base, size in MAP:
        assert end_of(base, size) == base + size - 1
        assert base % size == 0 and size & (size - 1) == 0
    SEL = {n: (i << 12, 0xF000) for i, n in enumerate(SLOTS)}      # slot n: addr[15:12] == n
    def hits(sel, addr):
        return [n for n, (v, m) in sel.items() if addr >> 16 == 0x4000 and (addr & 0xFFFF) & m == v]
    def select_audit(sel):
        probes = [0x40000000 + (i << 12) + o for i in range(len(SLOTS)) for o in (0x000, 0x00C, 0xFFC)]
        return [hex(a) for a in probes if hits(sel, a) != [SLOTS[(a >> 12) & 0xF]]]
    def apb_write(sel, regs, addr, data):
        for n in hits(sel, addr):
            regs[n][addr & 0xFF] = data
    assert select_audit(SEL) == []
    BUG = dict(SEL, DMA=(0x0000, 0xE000))                          # 8 KB window: addr[15:13] == 0
    assert select_audit(BUG) == ["0x40001000", "0x4000100c", "0x40001ffc"]
    for sel, expect in ((SEL, None), (BUG, 1)):
        regs = {n: {} for n in SLOTS}
        apb_write(sel, regs, 0x4000100C, 1)                        # SBR CTRL, START = 1
        assert regs["DMA"].get(0x0C) == expect                     # DMA KEY: written only by the bug
    buggy = [("DMA", 0x40000000, 0x2000) if n == "DMA" else (n, b, s) for n, b, s in MAP]
    assert overlaps(buggy) == [("DMA", "SBR")]
    print("PASS: map clean: 10 rows, no overlap, all aligned; 8 KB DMA window caught and its stray KEY write shown")

    Expected output

    One PASS line. The first asserts are the deliverable. The BUG select is the bug-1 demonstration at the place the bug lives, the select expression: the audit names the three probes in the SBR slot that hit two slaves, and the write model shows the symptom, KEY becoming 1. The last assert shows the table-level check catching the same fault from the other side.

    What each failure means

    An overlap hit names the two windows sharing an address: fix the select bits, never the software. A misalignment hit means a base is not a multiple of its size, so the power-of-two decoder cannot select it cleanly. An end-address mismatch means the table was copied wrongly, usually the ROM end 0x0000_3FFF.

    Hint ladder

    Hint 1: sort by base and compare each base against the previous end, which is what overlaps() does. Hint 2: the 8 KB window starts at the right base but is twice the slot size, so it swallows its neighbour. Hint 3: addr[15:13] == 0 covers 0x4000_0000 to 0x4000_1FFF, both slots. Answer: decode addr[15:12] per 4 KB slot and add an elaboration-time no-overlap check on the ADDR_*_BASE/SIZE pairs.

    An invisible alias born from a narrower IP: the DMA has only paddr[7:0] behind a 12-bit slot, so each 256-byte image repeats 16 times. Software must use 0x4000_0000 to 0x4000_00FF, and a lint rule must check apb_paddr[11:8] is unused.

    If asked how an overlap escapes review, answer with the mechanism: the select expression, not the table, was wrong, so table reviews passed. The fix is generating the decoder from the same source as the table.

    Check yourself Why must every base be aligned to its own size?

    A power-of-two select compares the top address bits only; an unaligned base needs extra compare logic and leaves a hole or an overlap at the edge the select cannot express.

    Related topics

    • Next: Decode, Response Codes and the Alias

    • Protocol First Principles: Address Maps and Decoding

    • RTL: Address Decode

    Previous

    268 / 289

    Next: Decode, Response Codes and the Alias

    Up next

    Up nextDecode, Response Codes and the Alias

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Link Table, Bandwidth and the Bottleneck