System
Core system utilities for Daisy hardware platform.
This module provides access to essential system-level functionality including:
- Clock configuration (400MHz or 480MHz CPU speeds)
- Timing functions (milliseconds, microseconds, ticks)
- Bootloader control (firmware updates via DFU/USB)
- Memory region detection
- Cache management (instruction and data caches)
The System class manages the STM32H750 microcontroller's clock tree, MPU (Memory Protection Unit), DMA initialization, and cache handling.
Basic Usage
Example 1: Default initialization (400MHz, caches enabled)
import nimphea/sys/system var sys: System sys.init() # Uses default 400MHz configuration
Example 2: Boost mode initialization (480MHz)
import nimphea/sys/system var sys: System var cfg = boostSystemConfig() # 480MHz preset sys.init(cfg)
Example 3: Custom configuration
import nimphea/sys/system var cfg = defaultSystemConfig() cfg.cpu_freq = FREQ_480MHZ cfg.use_dcache = true cfg.use_icache = true var sys: System sys.init(cfg)
Timing Functions
The System class provides three timing mechanisms:
- Millisecond timing - 1kHz SysTick timer
- Microsecond timing - High-resolution internal timer
- CPU tick timing - Direct CPU cycle counting
let ms = getNow() # Milliseconds since boot let us = getUs() # Microseconds (wraps every ~71 minutes) let ticks = getTick() # CPU ticks (frequency varies) delay(1000) # Wait 1 second delayUs(500) # Wait 500 microseconds # Measure execution time let start = getUs() performOperation() let duration = getUs() - start echo "Operation took ", duration, " us"
Clock Information
Query the system clock frequencies:
echo "System Clock: ", getSysClkFreq(), " Hz" echo "AHB Clock: ", getHClkFreq(), " Hz" echo "APB1 Clock: ", getPClk1Freq(), " Hz" echo "APB2 Clock: ", getPClk2Freq(), " Hz"
Bootloader Control
Return to bootloader for firmware updates:
# Option 1: STM32 DFU bootloader (default) resetToBootloader(STM) # Option 2: Daisy bootloader (if installed) resetToBootloader(DAISY) # Option 3: Daisy bootloader, skip timeout resetToBootloader(DAISY_SKIP_TIMEOUT) # Option 4: Daisy bootloader, infinite timeout resetToBootloader(DAISY_INFINITE_TIMEOUT)
Memory Region Detection
Determine which memory region an address belongs to:
let region = getProgramMemoryRegion() case region of INTERNAL_FLASH: echo "Running from internal flash" of QSPI: echo "Running from external QSPI flash" else: echo "Running from RAM or other region"
See Also
- sys/dma - DMA cache coherency functions
- examples/system_control.nim - Complete usage example
Types
BootInfo {.importcpp: "daisy::System::BootInfo".} = object status*: BootInfoType ## Boot command type data*: uint32 ## Additional data for boot command version*: BootloaderVersion ## Detected bootloader version
-
Boot information structure (internal use).
Stored in backup SRAM to persist across resets. Used for bootloader communication.
Source Edit BootInfoType {.importcpp: "daisy::System::BootInfo::Type", size: 4.} = enum INVALID = 0, ## No boot command JUMP = 3735928559, ## Jump to bootloader SKIP_TIMEOUT = 1526640647, ## Skip bootloader timeout INF_TIMEOUT = 2953268986 ## Infinite bootloader timeout
-
Boot information type codes (internal use).
These magic values are written to backup SRAM to communicate between application and bootloader.
Source Edit BootloaderMode {.importcpp: "daisy::System::BootloaderMode", size: 4.} = enum STM = 0, ## STM32 DFU bootloader (always available) DAISY, ## Daisy bootloader with timeout DAISY_SKIP_TIMEOUT, ## Daisy bootloader, skip timeout window DAISY_INFINITE_TIMEOUT ## Daisy bootloader, wait indefinitely
-
Bootloader mode selection for firmware updates.
STM mode: Uses STM32's built-in DFU bootloader. Works on all boards, allows updating internal flash via USB.
DAISY modes: Require Daisy bootloader to be installed. Allow updating QSPI flash with larger programs (up to 8MB).
Source Edit BootloaderVersion {.importcpp: "daisy::System::BootInfo::Version", size: 4.} = enum LT_v6_0 = 0, ## Bootloader version < 6.0 (legacy) NONE, ## No Daisy bootloader present v6_0, ## Bootloader version 6.0 v6_1, ## Bootloader version 6.1 or greater LAST ## Sentinel value (not a valid version)
-
Daisy bootloader version detection.
Used to check if Daisy bootloader is installed and which version.
Source Edit MemoryRegion {.importcpp: "daisy::System::MemoryRegion", size: 4.} = enum INTERNAL_FLASH = 0, ## Internal flash memory (128KB on STM32H750) ITCMRAM, ## Instruction Tightly Coupled Memory (64KB) DTCMRAM, ## Data Tightly Coupled Memory (128KB) SRAM_D1, ## SRAM in D1 domain (512KB) SRAM_D2, ## SRAM in D2 domain (288KB) SRAM_D3, ## SRAM in D3 domain (64KB) SDRAM, ## External SDRAM (if present on board) QSPI, ## External QSPI flash (8MB on Daisy Seed) INVALID_ADDRESS ## Address does not map to known region
-
Memory regions available on Daisy Seed hardware.
The STM32H750 has multiple memory regions with different characteristics (speed, size, persistence).
Typical program locations:
- INTERNAL_FLASH: Small programs (<128KB)
- QSPI: Large programs via Daisy bootloader
- SRAM_D1/D2/D3: RAM-based programs (lost on power cycle)
SysClkFreq {.importcpp: "daisy::System::Config::SysClkFreq", size: 4.} = enum FREQ_400MHZ, ## 400MHz system clock (default, recommended) FREQ_480MHZ ## 480MHz system clock (boost mode, max performance)
-
System clock frequency options for the STM32H750.
The system clock drives all peripheral buses (AHB, APB1, APB2).
- FREQ_400MHZ: Default safe speed, lower power consumption
- FREQ_480MHZ: Maximum speed, higher performance, higher power draw
Note: Some Daisy Seed boards may not reliably run at 480MHz. If you experience instability, use 400MHz.
Source Edit System {.importcpp: "daisy::System".} = object
-
System controller for Daisy hardware.
Manages clock configuration, timing, bootloader access, and system-level initialization.
Note: Most methods are static - you can call them without creating a System object. The object is only needed for initialization (init(), deInit()).
Source Edit SystemConfig {.importcpp: "daisy::System::Config".} = object cpu_freq*: SysClkFreq ## System clock frequency selection use_dcache*: bool ## Enable data cache (D-cache) use_icache*: bool ## Enable instruction cache (I-cache) skip_clocks*: bool ## Skip clock configuration (advanced use only)
-
Configuration structure for System initialization.
Controls clock frequency and cache settings.
Default values (via defaultSystemConfig()):
- cpu_freq: FREQ_400MHZ
- use_dcache: true (data cache enabled)
- use_icache: true (instruction cache enabled)
- skip_clocks: false (configure clocks normally)
Boost values (via boostSystemConfig()):
- cpu_freq: FREQ_480MHZ (maximum speed)
- Caches enabled
Consts
kQspiBootloaderOffset = 0x00040000'u32
-
Daisy bootloader offset from QSPI base address (0x90000000).
The first 256KB (0x40000 bytes) of QSPI flash is reserved for the Daisy bootloader. User programs start at 0x90040000.
Important: When writing data to QSPI, avoid the first 256KB to preserve the bootloader.
Source Edit
Procs
proc boost(this: var SystemConfig) {.importcpp: "#.Boost()", header: "sys/system.h", ...raises: [], tags: [], forbids: [].}
-
Set configuration to boost mode.
Initializes the configuration with:
- CPU frequency: 480MHz (maximum)
- Data cache: Enabled
- Instruction cache: Enabled
- Skip clocks: false
Warning: Some Daisy Seed boards may not run reliably at 480MHz. If you experience instability, use defaults() instead.
Example:
var cfg: SystemConfig cfg.boost()
Source Edit proc boostSystemConfig(): SystemConfig {....raises: [], tags: [], forbids: [].}
-
Create SystemConfig with boost settings (480MHz).
Returns a configuration struct initialized to:
- CPU frequency: 480MHz (maximum)
- Data cache: Enabled
- Instruction cache: Enabled
- Skip clocks: false
Returns: SystemConfig with boost settings
Warning: Some Daisy Seed boards may not run reliably at 480MHz. If you experience instability, use defaultSystemConfig() instead.
Example:
var cfg = boostSystemConfig() var sys: System sys.init(cfg)
Source Edit proc defaults(this: var SystemConfig) {.importcpp: "#.Defaults()", header: "sys/system.h", ...raises: [], tags: [], forbids: [].}
-
Set configuration to default values.
Initializes the configuration with:
- CPU frequency: 400MHz
- Data cache: Enabled
- Instruction cache: Enabled
- Skip clocks: false
Example:
var cfg: SystemConfig cfg.defaults()
Source Edit proc defaultSystemConfig(): SystemConfig {....raises: [], tags: [], forbids: [].}
-
Create SystemConfig with default settings.
Returns a configuration struct initialized to:
- CPU frequency: 400MHz
- Data cache: Enabled
- Instruction cache: Enabled
- Skip clocks: false
Returns: SystemConfig with defaults
Example:
var cfg = defaultSystemConfig() cfg.cpu_freq = FREQ_480MHZ # Override to boost mode sys.init(cfg)
Source Edit proc delay(delay_ms: uint32) {.importcpp: "daisy::System::Delay(@)", ...raises: [], tags: [], forbids: [].}
-
Blocking delay in milliseconds.
Uses SysTick timer (1ms resolution).
Parameters:
- delay_ms - Time to delay in milliseconds
Warning: This is a blocking delay. Audio processing will stop. Only use during initialization or non-critical sections.
Example:
echo "Waiting 1 second..." delay(1000) echo "Done!"
Source Edit proc delayTicks(delay_ticks: uint32) {.importcpp: "daisy::System::DelayTicks(@)", ...raises: [], tags: [], forbids: [].}
-
Blocking delay in CPU ticks.
Most precise delay mechanism, but tick frequency varies with clock config.
Parameters:
- delay_ticks - Number of ticks to delay
Example:
let freq = getTickFreq() let ticksFor1ms = freq div 1000 delayTicks(ticksFor1ms) # Delay 1ms using ticks
Source Edit proc delayUs(delay_us: uint32) {.importcpp: "daisy::System::DelayUs(@)", ...raises: [], tags: [], forbids: [].}
-
Blocking delay in microseconds.
Uses internal timer for higher precision than delay().
Parameters:
- delay_us - Time to delay in microseconds
Warning: Blocking delay. Audio processing will stop.
Example:
delayUs(500) # Wait 500 microseconds
Source Edit proc getBootloaderVersion(): BootloaderVersion {. importcpp: "daisy::System::GetBootloaderVersion()", ...raises: [], tags: [], forbids: [].}
-
Check which version of Daisy bootloader is installed (if any).
Returns: BootloaderVersion enum
Example:
let ver = getBootloaderVersion() case ver of NONE: echo "No Daisy bootloader (STM DFU only)" of v6_1: echo "Daisy bootloader v6.1+ installed" else: echo "Older bootloader version"
Source Edit proc getClockInfo(): tuple[sysclk, hclk, pclk1, pclk2, tickFreq: uint32] {. ...raises: [], tags: [], forbids: [].}
-
Get all clock frequencies as a tuple.
Convenience function for displaying all clock information.
Returns: Tuple with all clock frequencies in Hz
Example:
let clocks = getClockInfo() echo "System: ", clocks.sysclk div 1_000_000, " MHz" echo "AHB: ", clocks.hclk div 1_000_000, " MHz" echo "APB1: ", clocks.pclk1 div 1_000_000, " MHz" echo "APB2: ", clocks.pclk2 div 1_000_000, " MHz" echo "Tick: ", clocks.tickFreq div 1_000_000, " MHz"
Source Edit proc getConfig(this: System): SystemConfig {.importcpp: "#.GetConfig()", ...raises: [], tags: [], forbids: [].}
-
Get the current System configuration.
Returns a copy of the configuration used during initialization.
Returns: SystemConfig struct with current settings
Example:
let cfg = sys.getConfig() echo "Running at: ", cfg.cpu_freq echo "D-cache: ", cfg.use_dcache
Source Edit proc getHClkFreq(): uint32 {.importcpp: "daisy::System::GetHClkFreq()", ...raises: [], tags: [], forbids: [].}
-
Get AHB (HCLK) frequency in Hz.
The HCLK clocks the CPU, memory, and DMA controllers.
Returns: Frequency in Hz
Source Edit proc getMemoryRegion(address: uint32): MemoryRegion {. importcpp: "daisy::System::GetMemoryRegion(@)", ...raises: [], tags: [], forbids: [].}
-
Determine which memory region an address belongs to.
Parameters:
- address - Memory address to check
Returns: MemoryRegion enum or INVALID_ADDRESS
Example:
let data = [1, 2, 3, 4] let address = cast[uint32](data[0].addr) let region = getMemoryRegion(address) echo "Data is in: ", region
Source Edit proc getNow(): uint32 {.importcpp: "daisy::System::GetNow()", ...raises: [], tags: [], forbids: [].}
-
Get milliseconds since system boot.
Uses 1kHz SysTick timer. Wraps every ~49.7 days.
Returns: Milliseconds since boot (uint32)
Example:
let startTime = getNow() performTask() let elapsed = getNow() - startTime echo "Task took ", elapsed, " ms"
Source Edit proc getPClk1Freq(): uint32 {.importcpp: "daisy::System::GetPClk1Freq()", ...raises: [], tags: [], forbids: [].}
-
Get APB1 peripheral clock frequency in Hz.
Many peripherals use this clock (I2C, UART, SPI, timers).
Note: Some timers run at PCLK1 * 2.
Returns: Frequency in Hz
Source Edit proc getPClk2Freq(): uint32 {.importcpp: "daisy::System::GetPClk2Freq()", ...raises: [], tags: [], forbids: [].}
-
Get APB2 peripheral clock frequency in Hz.
Some peripherals use this faster clock.
Note: Some timers run at PCLK2 * 2.
Returns: Frequency in Hz
Source Edit proc getProgramMemoryRegion(): MemoryRegion {. importcpp: "daisy::System::GetProgramMemoryRegion()", ...raises: [], tags: [], forbids: [].}
-
Detect which memory region the program is executing from.
Returns: MemoryRegion enum (INTERNAL_FLASH, QSPI, etc.)
Example:
let region = getProgramMemoryRegion() case region of INTERNAL_FLASH: echo "Running from internal flash (< 128KB)" of QSPI: echo "Running from QSPI flash (Daisy bootloader)" else: echo "Running from RAM"
Source Edit proc getSysClkFreq(): uint32 {.importcpp: "daisy::System::GetSysClkFreq()", ...raises: [], tags: [], forbids: [].}
-
Get system clock frequency in Hz.
Returns the core system clock that feeds all peripheral buses.
Returns: Frequency in Hz (400000000 or 480000000)
Example:
let freq = getSysClkFreq() echo "System clock: ", freq div 1_000_000, " MHz"
Source Edit proc getTick(): uint32 {.importcpp: "daisy::System::GetTick()", ...raises: [], tags: [], forbids: [].}
-
Get current CPU tick count.
Increments at getTickFreq() Hz (typically PCLK1 * 2). Useful for precise performance measurement.
Returns: Tick count (uint32)
Example:
let ticksStart = getTick() someFunction() let ticksElapsed = getTick() - ticksStart let freq = getTickFreq() echo "Function took ", ticksElapsed, " ticks" echo "At ", freq, " Hz = ", ticksElapsed.float / freq.float, " seconds"
Source Edit proc getTickFreq(): uint32 {.importcpp: "daisy::System::GetTickFreq()", ...raises: [], tags: [], forbids: [].}
-
Get tick timer frequency in Hz.
Returns the rate at which getTick() increments. Typically PCLK1 * 2.
Returns: Frequency in Hz (e.g., 200000000 for 200MHz)
Source Edit proc getUs(): uint32 {.importcpp: "daisy::System::GetUs()", ...raises: [], tags: [], forbids: [].}
-
Get microseconds from internal timer.
Higher resolution than getNow() but wraps faster (~71 minutes).
Returns: Microseconds (uint32, wraps around)
Example:
let start = getUs() criticalOperation() let duration = getUs() - start echo "Operation: ", duration, " us"
Source Edit proc init(this: var System) {.importcpp: "#.Init()", ...raises: [], tags: [], forbids: [].}
-
Initialize System with default configuration.
Default settings:
- CPU frequency: 400MHz
- Data cache: Enabled
- Instruction cache: Enabled
This is equivalent to:
var cfg = defaultSystemConfig() sys.init(cfg)
Example:
var sys: System sys.init() echo "System initialized at ", getSysClkFreq(), " Hz"
Source Edit proc init(this: var System; config: SystemConfig) {.importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
-
Initialize System with custom configuration.
Parameters:
- config - SystemConfig with desired clock and cache settings
Example:
var cfg = boostSystemConfig() # 480MHz preset var sys: System sys.init(cfg)
Source Edit proc initBackupSram() {.importcpp: "daisy::System::InitBackupSram()", ...raises: [], tags: [], forbids: [].}
-
Initialize backup SRAM region.
Backup SRAM retains data when powered by VBAT (coin cell battery). Used for bootloader communication and persistent state.
Note: Usually called automatically by System.init().
Source Edit proc jumpToQspi(this: var System) {.importcpp: "#.JumpToQspi()", ...raises: [], tags: [], forbids: [].}
-
Jump to code at external QSPI flash base address (0x90000000).
Warning: If no valid code exists at that address, the CPU will likely fall into an infinite loop or fault.
This is used for advanced multi-stage boot scenarios.
Source Edit proc resetToBootloader(mode: BootloaderMode = STM) {. importcpp: "daisy::System::ResetToBootloader(@)", ...raises: [], tags: [], forbids: [].}
-
Reset and enter bootloader mode for firmware updates.
Parameters:
- mode - Bootloader mode (default: STM)
Modes:
- STM: STM32 DFU bootloader (always available, updates internal flash)
- DAISY: Daisy bootloader (if installed, updates QSPI flash)
- DAISY_SKIP_TIMEOUT: Daisy bootloader, skip timeout
- DAISY_INFINITE_TIMEOUT: Daisy bootloader, wait forever
Example:
# User holds button for 3 seconds if buttonHeldFor3Seconds(): resetToBootloader(STM) # Enter DFU mode
Source Edit