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
    • Stage 2: Clocks, Resets, Power
    • Stage 3: Interrupts and Boot
      • Interrupt Map and Latency Budget
      • DMA Programming Sequence and Contract Traps
      • Boot Flow and Secure Partition
    • Stage 4: Verification and Signoff
Loading lesson

←Stage 3: Interrupts and Boot
  1. Topics
  2. SoC Design
  3. Capstone: Integrate NanoSoC
  4. Stage 3: Interrupts and Boot
  5. Boot Flow and Secure Partition
On this page

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

    Boot Flow and Secure Partition

    Walk the sixteen boot steps to 12.581 ms, verify the banner, and catch planted bug 7.

    Why it matters

    A DMA copy from SRAM1 to SRAM0 ends with SBR STATUS.ERR on the very first beat while CPU reads of the same address work. The SBR master issues non-secure because its CTRL.NS bit resets to 1, and SRAM1 is secure-only, so the interconnect answers SLVERR before any data moves. Reset-safe is not access-safe: the default suits the common non-secure case and breaks the secure one. The intended cure is secure software clearing the bit, but the register that holds the bit sits in a non-secure slot, and that is a hole.

    Goal, inputs and steps

    • Goal: the boot timeline with each step's owner and duration, plus the per-master security attributes.

    • Inputs: the sixteen steps, the 115,384.6 baud banner math, the secure-only rows.

    • Steps: sum the flow in both orders of RST_CAUSE read and clear; verify the 16-character banner; probe SRAM1 through the SBR with NS at its reset value, after secure software clears it, and from non-secure software; file the NS-clear rule and the open weakness.

    The core idea

    Boot starts by reading RST_CAUSE: a WAKE cause means SRAM0 was retained and holds the image, so the clear is skipped and the ROM jumps straight to it. A cold boot (POR, PAD or SW cause) sets the stack pointer to 0x2001_0000 and clears 4 KB of SRAM0, then initialises the UART, prints the banner, self-tests the DMA, loads the image over SPI and checksums it. Each master carries a security attribute: the CPU is secure, and the SBR issues AxPROT[1] from its CTRL.NS bit, which resets to 1 (non-secure). Secure-only SRAM1 therefore refuses the SBR until secure software clears NS. Known weakness of NanoSoC v1: the SBR register slot is non-secure, so non-secure software can also clear CTRL.NS and reach SRAM1 through the SBR. The partition is not closed until NS is writable only from the secure side, or the SBR slot becomes secure-only. The checker below shows the bug, the intended fix and the hole, and the readiness review lists the hole as open.

    Step group

    Duration

    Share

    Hardware reset R1-R6 and first fetch

    54.073 us

    0.43%

    Read RST_CAUSE, stack, clear (cold boot), UART init

    11.792 us

    0.09%

    16-char banner at 115,384.6 baud

    1,386.667 us

    11.0%

    DMA self-test, 64 words

    8.385 us

    0.07%

    SPI image 8,196 bytes at 6 Mb/s

    10,928.000 us

    86.9%

    Checksum 2,048 word reads

    192.000 us

    1.5%

    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
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    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 ----
    
    banner = "NanoSoC boot ok\n"
    assert len(banner) == 16
    baud = 24e6 / (16 * 13)
    assert abs(baud - 115384.6) < 0.1 and abs(160 / baud * 1e6 - 1386.667) < 0.01
    def boot_steps(cause, cause_first=True):
        hw = [("osc", 1024 * T_PER), ("rst_per", 2 * T_PER), ("pll", 260 * T_PER), ("gates", 2 * T_PER),
              ("rst_sys", 18 * T_SYS), ("rst_cpu", 8 * T_SYS + 2 * T_CPU), ("fetch", 24 * T_CPU)]
        rd, clr = ("read_cause", 54 * T_CPU), ("clear_sram0", 1024 * T_SYS)
        if cause == "WAKE":
            return hw + ([rd] if cause_first else [clr, rd])           # the clear before the read erases the retained image
        rest = [("uart_init", 3 * 54 * T_CPU), ("banner", 160 / baud * 1e9),
                ("dma_selftest", 5 * 125 + 7 * 125 + 72 * T_SYS + 26 * T_CPU + 6000),
                ("spi_load", (8192 + 4) * 8 / 6e6 * 1e9), ("checksum", 2048 * 18 * T_CPU)]
        return hw + ([rd, clr] if cause_first else [clr, rd]) + rest
    def names(steps):
        return [n for n, _ in steps]
    cold = boot_steps("POR")
    assert len(cold) == 14 and abs(sum(ns for _, ns in cold) / 1000 - 12580.917) < 0.01
    assert names(cold).index("read_cause") < names(cold).index("clear_sram0")
    assert "clear_sram0" not in names(boot_steps("WAKE"))                 # the retained image survives
    assert "clear_sram0" in names(boot_steps("WAKE", cause_first=False))   # the wrong order would erase it
    SBR_CTRL_RESET = 0x4                                               # CTRL.NS (bit 2) resets to 1
    def sbr_ctrl_write(ctrl, wdata, writer_secure, ns_secure_only=False):
        if ns_secure_only and not writer_secure:
            wdata = (wdata & ~0x4) | (ctrl & 0x4)                      # the proposed fix: NS writable only from the secure side
        return wdata & 0x6                                             # START self-clears
    def sbr_reaches_sram1(ctrl):
        return response(0x20010000, (ctrl >> 2) & 1 == 0) == "OK"      # AxPROT[1] = NS: the access is secure when NS is 0
    assert response(0x20010000, True) == "OK" and response(0x20000000, False) == "OK" and response(0x20010000, False) == "SLVERR"
    assert not sbr_reaches_sram1(SBR_CTRL_RESET)                       # bug 7: the SBR is non-secure out of reset
    assert sbr_reaches_sram1(sbr_ctrl_write(SBR_CTRL_RESET, 0x0, True))      # secure software clears NS: it works
    assert sbr_reaches_sram1(sbr_ctrl_write(SBR_CTRL_RESET, 0x0, False))     # known weakness: non-secure software can too
    assert not sbr_reaches_sram1(sbr_ctrl_write(SBR_CTRL_RESET, 0x0, False, ns_secure_only=True))   # closed by the fix
    assert sbr_reaches_sram1(sbr_ctrl_write(SBR_CTRL_RESET, 0x0, True, ns_secure_only=True))        # secure copy still works
    print("PASS: boot 12.581 ms over 14 steps, banner 16 chars; SBR blocked at reset, opened by NS = 0, hole shown and closed")

    Expected output

    One PASS line with the boot total, the banner length and the security verdicts. The first sbr_reaches_sram1 assert is the bug-7 demonstration: the model starts from the reset value 0x4, so a reset value of 0 would fail it. The two writes with a non-secure writer show the known weakness open in v1 and closed by the proposed fix.

    What each failure means

    A banner failure means the newline was forgotten: 15 visible characters plus backslash-n make 16. A baud failure means BAUDDIV 13 was misread: the divisor is 16 times 13. A boot-total failure means a step was dropped or the 4 command bytes of the SPI load were forgotten. A wake failure means the RST_CAUSE read was placed after the clear. A secure failure on the closed case means the fix was applied to the register slot but not to the NS bit.

    Hint ladder

    Hint 1: the CPU read works, so the memory and the map are fine; only the master's attribute differs. Hint 2: read the SBR CTRL reset value bit by bit: 0x4 means NS is 1. Hint 3: the reset default is deliberate, so the cure for the bug is software; but ask who is allowed to write that bit. Answer: secure software clears CTRL.NS before any secure transfer, the integration spec documents the security attribute of every master, and the hole that non-secure software can do the same is listed as open until NS is writable only from the secure side.

    Assuming the DMA self-test proves the SBR path: the test's 5 reads and 7 writes are APB register checks plus one 64-word job, and it passes with NS set because SRAM0 is non-secure. Only a transfer touching SRAM1 exercises the attribute.

    If asked where boot time goes, give the shares before the total: SPI 86.9, banner 11.0, everything else 2.1. Then say what that implies: boot optimisation starts at the SPI clock, nowhere else. If asked whether the partition is secure, say no, and name the hole: a non-secure slot in front of the NS bit.

    Check yourself After wake the ROM jumps to 0x2000_0000 instead of reloading. Why is that safe?

    SRAM0 is retained through SLEEP in PD_AON, so the image survived; RST_CAUSE bit 3 records the wake so software knows not to reload.

    Related topics

    • Back: DMA Programming Sequence and Contract Traps

    • Stage 4: Verification and Signoff

    • Bring-up: Boot ROM and Firmware

    Previous

    276 / 289

    Next: Stage 4: Verification and Signoff

    Up next

    Up nextStage 4: Verification and Signoff

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Interrupt Map and Latency Budget