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
    • Memory Subsystem Integration
    • ECC and Error Handling
    • External Memory Controller Integration
    • Boot ROM and Boot Modes
      • Reset Vector and Boot-Source Select
      • The NanoSoC Boot Flow in Time
      • ROM to SRAM Handoff and Patching
    • eFuse and OTP
    • Secure Boot and Root of Trust
    • Security Architecture
  • 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
Loading lesson

←Boot ROM and Boot Modes
  1. Topics
  2. SoC Design
  3. Memories, Boot and Security
  4. Boot ROM and Boot Modes
  5. Reset Vector and Boot-Source Select
On this page

    Part 8 of 14, lesson 10 of 21Part 8 of 14/Memories, Boot and Security/Lesson 10 of 21All levels

    Reset Vector and Boot-Source Select

    Address zero, the single boot source, the reset-cause register, and what reset does and does not clear.

    Why it matters

    This lesson has one job: guarantee the CPU fetches its first instruction from somewhere meaningful. The reset vector is address 0x0000_0000, the first word of the 16 KB boot ROM, and the CPU's first 4-word line fill costs 24 CPU clocks (clk_cpu, 192 MHz), which is 125 ns. The second job is the cold-versus-wake decision: if the ROM misreads why it was reset, it either reloads an image that was sitting retained in SRAM0 or jumps into SRAM0 on a chip that holds nothing there.

    The core idea

    The ROM window is exactly the ROM size with no mirror, so there is no alias to remove and no remap bit to forget: fetch from zero and the ROM answers. Boot-source select on NanoSoC is degenerate by design: there are no boot straps and no GPIO is read before the load. There is one source, SPI, plus the retained-image shortcut at wake, and the only input to the decision is RST_CAUSE. On a chip that does have boot straps the discipline is different (see the pitfall below), but NanoSoC's ledger has no strap step. RST_CAUSE names power-on (bit 0), pad (bit 1), software (bit 2) and wake (bit 3) resets. It is reset only by power-on reset; every other reset sets its own bit on top of whatever is already there. So the ROM reads it first (one 281.25 ns APB access), branches on the bits, and then writes 1s to clear the bits it has handled. Without that clear, the first wake after a cold boot would show POR and WAKE together, and the test for cold versus wake would be ambiguous. The clear write is one more register access that the 12.581 ms ledger does not count: it is 281.25 ns if the ROM does it, or it is done by the first software stage after the handoff.

    The script checks the three addresses against the memory map and then plays two reset sequences, a cold boot followed by a wake, against two ROMs: one that clears RST_CAUSE after reading it and one that does not. The decision function can fail: the non-clearing ROM must be caught as ambiguous.

    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
    bases = {"BOOTROM": 0x0000_0000, "SRAM0": 0x2000_0000, "SRAM1": 0x2001_0000}
    ends = {"BOOTROM": 0x0000_3FFF, "SRAM0": 0x2000_FFFF, "SRAM1": 0x2001_FFFF}
    vector = 0x0000_0000
    stack_top = 0x2001_0000
    image_base = 0x2000_0000
    assert ends["BOOTROM"] - bases["BOOTROM"] + 1 == 16 * 1024
    assert ends["SRAM0"] - bases["SRAM0"] + 1 == 64 * 1024
    assert bases["BOOTROM"] <= vector <= ends["BOOTROM"], "vector must be in ROM"
    assert bases["SRAM0"] <= image_base <= ends["SRAM0"], "image must be in SRAM0"
    assert stack_top == ends["SRAM0"] + 1 == bases["SRAM1"], "stack top is SRAM0 end + 1"
    print(f"vector {vector:#010x} in ROM, image {image_base:#010x}, stack {stack_top:#010x}")
    POR, PAD, SW, WAKE = 1, 2, 4, 8
    def path(cause):
        if cause == WAKE:
            return "wake"
        if cause != 0 and not cause & WAKE:
            return "cold"
        return "ambiguous"
    def run(clears):
        cause, seen = 0, []
        for event in (POR, WAKE):
            cause |= event
            seen.append(path(cause))
            if clears:
                cause &= ~cause
        return seen
    assert run(True) == ["cold", "wake"], run(True)
    assert run(False) == ["cold", "ambiguous"], run(False)
    print("clearing ROM:", run(True), "| non-clearing ROM:", run(False))
    print("PASS: addresses fit the map; only a ROM that clears RST_CAUSE tells cold from wake")

    What the review looks for

    • The reset vector row names the ROM window, the first fetch width and the cold-cache cost of 125 ns.

    • The ROM reads RST_CAUSE before any other state-changing step, branches on the bits, and clears the bits it has handled.

    • RST_CAUSE handling is documented per cause: cold boot loads, wake jumps, software reset re-enters safely.

    • If the chip has boot straps (NanoSoC has none), every strap has a defined pull, a settle time before reset release, and a read-back register.

    Verify and debug

    Force each reset source in simulation from a clean power-on and read back RST_CAUSE: exactly one bit per cause, with older bits still present until software clears them, and only power-on reset clears the register by itself. Then run cold boot followed by wake and confirm the ROM takes the wake path only because it cleared the POR bit. On a chip with straps, also float each strap in a scratch build and confirm the pull defines the intended source; a floating strap that changes the boot path is a board-level bug wearing a silicon mask. If the first fetch hangs, check the ROM select equation before anything else: addr bits 31 down to 14 equal zero, or nothing answers.

    On a chip that has boot straps (NanoSoC has none), a strap error: straps float or are sampled before they settle. Symptom: the chip boots from the wrong device while every step looks correct. Caught by a review of the strap circuit and sample timing plus a test that wiggles each strap. Fix: defined pulls and sampling at reset release. The NanoSoC version of the same species is a ROM that never clears RST_CAUSE: the first wake after a cold boot then looks like both.

    If asked where the CPU starts, answer with three addresses: vector 0x0000_0000, image 0x2000_0000, stack 0x2001_0000. Then explain why the stack top equals the next bank's base: the first push lands in the last word of SRAM0.

    Check yourself The ROM window equals the ROM size exactly. What failure mode does that choice remove?

    The boot alias that is never removed: with no mirror there is no remap bit and no stale alias for vectors to fetch from after software remaps. Fixed maps cost one decision and remove a whole class of boot bugs.

    Related topics

    • Next: The NanoSoC Boot Flow in Time

    • Bring-up: Boot ROM Execution and Firmware Stage Handoff

    • Bring-up: Reset Sequencing and Clock Tree Bring-up

    Previous

    160 / 289

    Next: The NanoSoC Boot Flow in Time

    Up next

    Up nextThe NanoSoC Boot Flow in Time

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • ROM to SRAM Handoff and Patching