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
    • Clock Sources and PLLs
    • Dividers, Muxes and Glitch-Free Switching
    • Clock Gating Architecture
    • Clock Domain Planning
    • Top-Level Crossings
    • Always-On Domain, Wake Policy and a Timer Extension
      • Always-On Domain and Timer Clocks
      • Extension: Watchdog-to-Reset Wiring
      • Wake Sources and Reset-Versus-Interrupt Policy
    • Reset Sources and Reset Tree
    • Reset Sequencing and Release
  • 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
Loading lesson

←Always-On Domain, Wake Policy and a Timer Extension
  1. Topics
  2. SoC Design
  3. Clock and Reset Architecture
  4. Always-On Domain, Wake Policy and a Timer Extension
  5. Always-On Domain and Timer Clocks
On this page

    Part 5 of 14, lesson 16 of 24Part 5 of 14/Clock and Reset Architecture/Lesson 16 of 24All levels

    Always-On Domain and Timer Clocks

    What stays awake and why: the NanoSoC always-on list, and where a wake timer would sit if you added one.

    Why it matters

    Sleep is a promise that the chip can hear a wake-up while apparently off. That promise holds only if the right logic stays powered on the right clock. Put a wake path in the switchable domain and sleep becomes coma. This lesson places each always-on block once, then shows where a wake timer would go if a derivative added one. Its one job is placement; the watchdog extension is next.

    The core idea

    Everything that must work in sleep lives in PD_AON on clk_per: the NanoSoC specification's always-on list is exactly RSTPWR, PLL, INTC, UART, SPI, GPIO, SRAM0 (arrays, in retention) and ROM. The SLEEP state keeps clk_per running while the PLL, clk_cpu and clk_sys are off, so every block that must react in sleep has to be on the crystal clock. Three of the eight can wake the chip: GPIO and UART through their interrupt lines and the controller itself through irq_pwr. SPI stays powered because the boot ROM loads the image through it, but it is not a wake source. The ROM and the SRAM0 arrays are always-on so that the retained image and the reset vector survive.

    NanoSoC has no timer, real-time clock or watchdog. If a derivative added a wake timer, it would sit in PD_AON on clk_per for the same two reasons: the supply because sleep must not kill it, the clock because the PLL is off in sleep. A free-running 32-bit counter on 24 MHz wraps in 178.96 s, so timekeeping needs a prescaler: divide by 24,000,000 for a 1 Hz tick (25 bits) or by 24,000 for a 1 kHz wake tick (15 bits), both illustrative teaching values. The prescaler must be an exact integer divide of 24 MHz, or the tick drifts.

    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
    CLK_PER = 24e6  # NanoSoC-specification clock
    ALWAYS_ON = {"rstpwr", "pll", "intc", "uart", "spi", "gpio", "sram0", "rom"}  # power domain list
    WAKE_SOURCES = {"gpio", "uart", "rstpwr"}  # GPIO, UART, irq_pwr from the controller
    
    def check_placement(block, domain, clock, wakes):
        errors = []
        if block in ALWAYS_ON and domain != "PD_AON":
            errors.append(block + ": must be PD_AON")
        if wakes and (domain != "PD_AON" or clock != "clk_per"):
            errors.append(block + ": a wake source needs PD_AON and clk_per")
        if wakes and block not in WAKE_SOURCES:
            errors.append(block + ": not a NanoSoC wake source")
        return errors
    
    assert check_placement("gpio", "PD_AON", "clk_per", True) == []
    assert check_placement("spi", "PD_AON", "clk_per", False) == []
    # The checks can fail: a wake timer placed in PD_SW or on clk_sys is rejected,
    # and so is SPI claimed as a wake source.
    assert check_placement("timer", "PD_SW", "clk_sys", True) != []
    assert check_placement("uart", "PD_AON", "clk_sys", True) != []
    assert check_placement("spi", "PD_AON", "clk_per", True) != []
    
    # Extension arithmetic (illustrative): exact prescaler divides of 24 MHz.
    for tick_hz, width in [(1, 25), (1000, 15)]:
        div = int(CLK_PER // tick_hz)
        assert CLK_PER % tick_hz == 0           # exact: no drift
        assert (div - 1).bit_length() == width
    assert CLK_PER % 7 != 0                      # a 7 Hz tick would not be exact
    wrap_s = 2 ** 32 / CLK_PER
    assert abs(wrap_s - 178.96) < 0.01, wrap_s
    print("PASS: placement rules reject bad wake blocks; prescalers exact; raw 32-bit wrap 178.96 s")

    What the review looks for

    • Every always-on block states its clock and power domain with the reason: survival or rate.

    • The wake sources are exactly GPIO, UART and irq_pwr; SPI is powered for boot but is not among them.

    • No wake-path logic sits outside PD_AON; the supply check proves it, and any added timer is checked the same way.

    Verify and debug

    In power-aware simulation, enter sleep and confirm each always-on block still responds while every system-domain block freezes. A wake that never arrives points at the source's power domain or clock before its enable bit. If a timer extension exists, read it across a sleep-wake cycle and confirm monotonic time with no jump: a jump means the prescaler was reset on wake.

    Always-on logic fed from a switched domain: a wake source clocked by clk_sys or supplied by the switchable domain never fires in sleep. Caught by the supply-net check and by a sleep-wake test. Fixed by placing every wake path in PD_AON on clk_per, as above.

    If asked where a wake timer would live, answer with both coordinates, PD_AON and clk_per, and the reason each is forced: the supply because sleep must not kill it, the clock because the PLL is off in sleep. Add that NanoSoC itself has none.

    Check yourself A 1 kHz wake-tick prescaler divides 24 MHz by 24,000 exactly. Why does exactness matter?

    A fractional divide accumulates phase error on every tick, so wake intervals drift against wall time. An exact integer divide keeps every tick the same length forever.

    Related topics

    • Next: Extension: Watchdog-to-Reset Wiring

    • RTL: Counters and Timers

    Previous

    106 / 289

    Next: Extension: Watchdog-to-Reset Wiring

    Up next

    Up nextExtension: Watchdog-to-Reset Wiring

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Wake Sources and Reset-Versus-Interrupt Policy