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 NanoSoC19
    • Capstone Brief
    • Stage 1: Memory Map and Interconnect
    • Stage 2: Clocks, Resets, Power
    • Stage 3: Interrupts and Boot
    • Stage 4: Verification and Signoff
    • Solutions
      • Root Causes, Fixes and Evidence
      • Stage 1 Worked Checks
      • Stage 2 Worked Checks
      • Stage 3 Worked Checks
      • Stage 4 Worked Checks
Loading lesson

←Solutions
  1. Topics
  2. SoC Design
  3. Capstone: Integrate NanoSoC
  4. Solutions
  5. Stage 1 Worked Checks
On this page

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

    Stage 1 Worked Checks

    Worked answer: runnable checks and explicit fault injections after the challenge stages.

    These are the worked solutions. Compare the inputs and injected faults with your written hypothesis before running each block. Each block runs independently and prints one PASS line after its clean-data and failing-data assertions.

    Worked check 1

    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")

    Worked check 2

    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
    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 decode(0x00000000) == "BOOTROM" and decode(0x2000FFFF) == "SRAM0"
    assert decode(0x20010000) == "SRAM1" and decode(0x40006000) == "RSTPWR"
    assert decode(0x00004000) == "H1" and decode(0x20020000) == "H2"
    assert decode(0x40007000) == "H3" and decode(0x40010000) == "H4"
    assert response(0x00004000, True) == "DECERR" and response(0x40007000, True) == "SLVERR"
    assert response(0x40010000, True) == "DECERR"
    assert response(0x20010000, False) == "SLVERR" and response(0x20010000, True) == "OK"
    REG = {0x00: "CTRL", 0x04: "STATUS", 0x08: "LEN", 0x0C: "KEY", 0x10: "COUNT"}
    def dma_read(bus_addr, lo, hi):
        # the bus address has 12 bits; the DMA port paddr has 8 and sees bits [hi:lo] of it
        if hi - lo != 7:
            return "WIDTH"            # a width mismatch is a lint error, never a silent truncation
        return REG.get(((bus_addr & 0xFFF) >> lo) & 0xFF, "PSLVERR")
    def reached(lo, hi):
        return [dma_read(0x40000000 + o, lo, hi) for o in sorted(REG)]
    assert reached(0, 7) == ["CTRL", "STATUS", "LEN", "KEY", "COUNT"]                    # apb_paddr[7:0]: correct
    assert reached(2, 9) == ["CTRL", "PSLVERR", "PSLVERR", "PSLVERR", "STATUS"]          # apb_paddr[9:2]: bug 4
    assert reached(4, 11) == ["CTRL", "CTRL", "CTRL", "CTRL", "PSLVERR"]                 # apb_paddr[11:4]: worse
    assert reached(0, 9)[0] == "WIDTH"
    assert dma_read(0x40000104, 0, 7) == "STATUS" and dma_read(0x40000114, 0, 7) == "PSLVERR"   # the alias, and offsets past 0x10
    print("PASS: decode and codes agree; word-address wiring reaches 1 register right, 3 take PSLVERR, COUNT returns STATUS")

    Worked check 3

    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
    79
    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 ----
    
    FABRIC = sustained(384.0, 16)
    LINKS = {"cpu_fill": 16 / (24 * T_CPU) * 1e3,        # one 16-byte line per 24 clk_cpu
             "fabric": FABRIC, "sram_bank": FABRIC,       # per direction; one bank port
             "apb_fast": 4 * 96 / 5,                      # DMA and SBR registers, 3-clock access plus 2 bridge clocks
             "apb_slow": 4 / 208.33e-9 / 1e6}             # through nano_apb_cdc: 208.33 ns per access
    def stream_rate(src_bank, dst_bank):
        bank = LINKS["sram_bank"] / (2 if src_bank == dst_bank else 1)     # one single-port macro serves both streams
        path = {"sbr_read": LINKS["fabric"], "src_bank": bank, "dma_in": LINKS["fabric"],
                "dma_out": LINKS["fabric"], "dst_bank": bank, "sbr_write": LINKS["fabric"]}
        low = min(path.values())
        return low, sorted(n for n, v in path.items() if v - low < 0.05)
    assert abs(FABRIC - 361.4) < 0.05 and abs(LINKS["cpu_fill"] - 128.0) < 0.05
    assert abs(LINKS["apb_fast"] - 76.8) < 0.01 and abs(LINKS["apb_slow"] - 19.2) < 0.05
    assert abs(2 * FABRIC - 722.8) < 0.05
    assert abs((64 + 8) * T_SYS - 750.0) < 0.5
    rate, tied = stream_rate("SRAM0", "SRAM1")
    assert abs(rate - 361.4) < 0.05 and len(tied) == 6            # six links tie: the 32-bit 96 MHz stream is the ceiling
    rate, tied = stream_rate("SRAM0", "SRAM0")
    assert abs(rate - 180.7) < 0.05 and tied == ["dst_bank", "src_bank"]    # the shared bank port is the bottleneck
    def placement_ok(src, dst):
        return stream_rate(src, dst)[0] > 0.99 * FABRIC
    assert placement_ok("SRAM0", "SRAM1") and not placement_ok("SRAM1", "SRAM1")
    assert min(("apb_slow", LINKS["apb_slow"]), ("apb_fast", LINKS["apb_fast"]), key=lambda x: x[1])[0] == "apb_slow"
    print("PASS: links agree: 361.4 per stream, 180.7 same-bank (bank port is the bottleneck), 64-word job 750 ns")

    The final PASS establishes consistency of the Python model and manifest. Record the known SBR attribute weakness and all implementation evidence still required in the readiness statement. The model reproduces an open partition in v1 and demonstrates a proposed secure-write guard; a model PASS alone does not close that finding.

    Related topics

    • Solution key and closure evidence

    • Next: Stage 2 Worked Checks

    Previous

    281 / 294

    Next: Stage 2 Worked Checks

    Up next

    Up nextStage 2 Worked Checks

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Stage 3 Worked Checks
    • Stage 4 Worked Checks