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
      • Clock Plan and Crossing Inventory
      • Reset Tree and Release Order
      • Power Domains, Isolation and Power-Up
    • Stage 3: Interrupts and Boot
    • Stage 4: Verification and Signoff
Loading lesson

←Stage 2: Clocks, Resets, Power
  1. Topics
  2. SoC Design
  3. Capstone: Integrate NanoSoC
  4. Stage 2: Clocks, Resets, Power
  5. Reset Tree and Release Order
On this page

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

    Reset Tree and Release Order

    Build the reset tree, sum the 53.948 us release sequence, and catch planted bug 2.

    Why it matters

    After reset the CPU stalls at its vector with ARVALID high and no RVALID, yet resetting just the CPU later boots fine. The release put rst_cpu_n up in the same step as rst_sys_n; the CPU-side 2-flop sync at 192 MHz is faster than the fabric's 18 cycles at 96 MHz, so the first fetch entered the async bridge while the crossbar was still in reset and was lost. Slaves and fabric first, masters last is not a slogan; it is this bug.

    Goal, inputs and steps

    • Goal: the reset tree plus a release order the CPU cannot outrun.

    • Inputs: four sources, three nets, the R0 to R6 step table.

    • Steps: draw the tree; compute each release time from the plan; model the buggy same-step release and confirm the order assertion fires and the first fetch is lost; file the fixed order.

    The core idea

    Four sources feed the controller in clk_per: por_n resets everything including RST_CAUSE, pad_rst_n debounces 16 clk_per cycles (666.7 ns) and spares RST_CAUSE and SRAM0 contents, sw_rst needs the 0xA5 key and holds rst_sys_n plus rst_cpu_n for 16 clk_sys cycles, pwr_rst follows SW_ON rising. Three nets leave, all asynchronously asserted and synchronously released through 2-flop syncs in their own domains: rst_per_n, rst_sys_n, rst_cpu_n. Cold release runs R0 por_n at zero, R1 oscillator ready over 1024 clk_per (42.667 us), R2 rst_per_n after 2 clk_per, R3 PLL lock over 260 clk_per (10.833 us), R4 gates open after 2 clk_per, R5 rst_sys_n after an 18-clk_sys hold and sync (187.5 ns), R6 rst_cpu_n after an 8-clk_sys gap plus 2 clk_cpu (93.75 ns), totalling 53.948 us. Minimum assertion is 16 domain cycles.

    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
    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 ----
    
    # each net: (what it waits for, how long after that), from the R1 to R6 table
    PLAN = {"rst_per_n": ("por", 1026 * T_PER),                    # 1024 oscillator cycles plus a 2-flop sync
            "rst_sys_n": ("gates", 18 * T_SYS),                     # 16-cycle hold plus a 2-flop sync in clk_sys
            "rst_cpu_n": ("rst_sys_n", 8 * T_SYS + 2 * T_CPU)}      # 8-cycle gap plus a 2-flop sync in clk_cpu
    def release_times(plan):
        t = {"por": 0.0}
        t["rst_per_n"] = plan["rst_per_n"][1]
        t["gates"] = t["rst_per_n"] + 262 * T_PER                   # PLL start and lock (260) and gates open (2)
        for net in ("rst_sys_n", "rst_cpu_n"):
            after, wait = plan[net]
            t[net] = t[after] + wait
        return t
    def fetch_lost(t):
        # the first fetch reaches the crossbar 1 clk_cpu + 3 clk_sys after the CPU leaves reset
        return t["rst_cpu_n"] + T_CPU + 3 * T_SYS < t["rst_sys_n"]
    def order_ok(t):
        return t["rst_per_n"] <= t["rst_sys_n"] and t["rst_cpu_n"] >= t["rst_sys_n"] + 8 * T_SYS
    good = release_times(PLAN)
    assert abs(good["rst_cpu_n"] / 1000 - 53.948) < 0.01
    assert abs(good["rst_sys_n"] - good["gates"] - 187.5) < 0.1 and abs(good["rst_cpu_n"] - good["rst_sys_n"] - 93.75) < 0.1
    assert order_ok(good) and not fetch_lost(good)
    bug = release_times(dict(PLAN, rst_cpu_n=("gates", 2 * T_CPU)))   # bug 2: released in the same step as rst_sys_n
    assert not order_ok(bug) and fetch_lost(bug)
    assert abs(bug["rst_sys_n"] - bug["rst_cpu_n"] - 177.1) < 0.1
    print("PASS: CPU released at 53.948 us, 93.75 ns after the fabric; same-step release breaks the order and loses the fetch")

    Expected output

    One PASS line with the CPU release time. The bug plan is the bug-2 demonstration: both order_ok and fetch_lost react, because they compare real release times, and the CPU leaves reset 177 ns before the fabric does.

    What each failure means

    A total failure means a step counted the wrong domain's cycles: the sys and CPU steps use their own periods, the earlier ones the 24 MHz period. An order failure on the good plan means the cpu net is anchored to the gates instead of to rst_sys_n. A pass on the bug plan means the predicates compare names instead of times, which is exactly the bug being hidden.

    Hint ladder

    Hint 1: convert each step to nanoseconds in its own domain before summing. Hint 2: the CPU-side 2-flop sync takes 2 clk_cpu, 10.4 ns; the fabric takes 18 clk_sys, 187.5 ns. Released together, the CPU runs 177 ns before the fabric is out of reset. Hint 3: the lost fetch is already inside the bridge when the fabric wakes, so no retry can save it. Answer: release the CPU 8 clk_sys plus 2 clk_cpu after rst_sys_n, and add a reset-order assertion that rst_cpu_n never rises before rst_sys_n.

    Partial reset across a live transaction: resetting one side of a bridge mid-transfer hangs the bus waiting for a response that will never come. Drain or quiesce with a handshake first, or reset both sides together.

    If asked for the release rule in one sentence, give slaves and fabric first, masters last, then prove it with the two sync depths: 18 slow cycles against 2 fast ones.

    Check yourself Why do the async FIFOs get asserted from both sides at once?

    Each side's flops leave reset on its own clock; asserting both nets together keeps write and read pointers in the same reset epoch so the gray comparison starts valid.

    Related topics

    • Back: Clock Plan and Crossing Inventory

    • Next: Power Domains, Isolation and Power-Up

    • CDC and RDC: Release Order Across Domains

    Previous

    272 / 289

    Next: Power Domains, Isolation and Power-Up

    Up next

    Up nextPower Domains, Isolation and Power-Up

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error