From 2a38531d73a9795c479635162c0609f6328e088c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 12 May 2023 11:04:04 +0200 Subject: [PATCH] samd/mcu: Reduce the startup time after hard reset. With Crystal: set the crystal startup wait time to 1 second. It was 2 seconds before, and that seeemed too long. With USB-Sync: scan for up to 1 second for the USB to be registered and carry on with boot as soon as it it. Before, the code just waited for 500ms. Side change: improve related comments. Signed-off-by: robert-hh --- ports/samd/mcu/samd21/clock_config.c | 41 +++++++++++++-------- ports/samd/mcu/samd51/clock_config.c | 55 +++++++++++++++------------- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/ports/samd/mcu/samd21/clock_config.c b/ports/samd/mcu/samd21/clock_config.c index 4c05dd7cc7..d84ee52676 100644 --- a/ports/samd/mcu/samd21/clock_config.c +++ b/ports/samd/mcu/samd21/clock_config.c @@ -52,7 +52,7 @@ uint32_t get_peripheral_freq(void) { void set_cpu_freq(uint32_t cpu_freq_arg) { - // Set 1 waitstate to be safe + // Set 1 wait state to be safe NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_MANW | NVMCTRL_CTRLB_RWS(1); int div = MAX(DFLL48M_FREQ / cpu_freq_arg, 1); @@ -75,7 +75,7 @@ void set_cpu_freq(uint32_t cpu_freq_arg) { while (GCLK->STATUS.bit.SYNCBUSY) { } // configure the FDPLL96 - // CtrlB: Set the ref ource to GCLK, set the Wakup-Fast Flag. + // CtrlB: Set the ref source to GCLK, set the Wakeup-Fast Flag. SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_REFCLK_GCLK | SYSCTRL_DPLLCTRLB_WUF; // Set the FDPLL ratio and enable the DPLL. int ldr = cpu_freq / FDPLL_REF_FREQ; @@ -106,25 +106,36 @@ void set_cpu_freq(uint32_t cpu_freq_arg) { while (GCLK->STATUS.bit.SYNCBUSY) { } } - // Set 0 waitstates for slower CPU clock + // Set 0 wait states for slower CPU clock NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_MANW | NVMCTRL_CTRLB_RWS(cpu_freq > 24000000 ? 1 : 0); SysTick_Config(cpu_freq / 1000); } void check_usb_recovery_mode(void) { #if !MICROPY_HW_XOSC32K - mp_hal_delay_ms(500); - // Check USB status. If not connected, switch DFLL48M back to open loop - if (USB->DEVICE.DeviceEndpoint[0].EPCFG.reg == 0) { - // Set/keep the open loop mode of the device. - SYSCTRL->DFLLVAL.reg = dfll48m_calibration; - SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | SYSCTRL_DFLLCTRL_ENABLE; + // Check USB status for up to 1 second. If not connected, + // switch DFLL48M back to open loop + for (int i = 0; i < 100; i++) { + if (USB->DEVICE.DeviceEndpoint[0].EPCFG.reg != 0) { + return; + } + mp_hal_delay_ms(10); } + // Set/keep the open loop mode of the device. + SYSCTRL->DFLLVAL.reg = dfll48m_calibration; + SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | SYSCTRL_DFLLCTRL_ENABLE; #endif // MICROPY_HW_XOSC32K } // Purpose of the #defines for the clock configuration. // +// The CPU is either driven by the FDPLL96 oscillator for f >= 48MHz, +// or by the DFLL48M for lower frequencies. The FDPLL96 takes 32768 Hz +// as reference frequency, supplied through GCLK1. +// +// DFLL48M is used for the peripheral clock, e.g. for PWM, UART, SPI, I2C. +// DFLL48M is either free running, or controlled by the 32kHz crystal, or +// Synchronized with the USB clock. // Both CPU and peripheral devices are clocked by the DFLL48M clock. // DFLL48M is either free running, or controlled by the 32kHz crystal, or // Synchronized with the USB clock. @@ -136,7 +147,7 @@ void check_usb_recovery_mode(void) { // The crystal is used, unless MICROPY_HW_MCU_OSC32KULP is set. // In that case GCLK1 (and the CPU clock) is driven by the 32K Low power oscillator. // The reason for offering this option is a design flaw of the Adafruit -// Feather boards, where the RGB Led and Debug signals interfere with the +// Feather boards, where the RGB LED and Debug signals interfere with the // crystal, causing the CPU to fail if it is driven by the crystal. // // If MICROPY_HW_XOSC32K = 0, the 32kHz signal for GCLK1 (and the CPU) is @@ -144,10 +155,10 @@ void check_usb_recovery_mode(void) { // // If MICROPY_HW_DFLL_USB_SYNC = 0, the DFLL48M oscillator is free running using // the pre-configured trim values. In that mode, the peripheral clock is -// not exactly 48Mhz and has a substantional temperature drift. +// not exactly 48Mhz and has a substitutional temperature drift. // // If MICROPY_HW_DFLL_USB_SYNC = 1, the DFLL48 is synchronized with the 1 kHz USB sync -// signal. If after boot there is no USB sync within 500ms, the configuration falls +// signal. If after boot there is no USB sync within 1000 ms, the configuration falls // back to a free running 48Mhz oscillator. // // In all modes, the 48MHz signal has a substantial jitter, largest when @@ -181,7 +192,7 @@ void init_clocks(uint32_t cpu_freq) { NVMCTRL->CTRLB.bit.RWS = 1; // 1 read wait state for 48MHz #if MICROPY_HW_XOSC32K - // Set up OSC32K according datasheet 17.6.3 + // Set up OSC32K according data sheet 17.6.3 SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP(0x3) | SYSCTRL_XOSC32K_EN32K | SYSCTRL_XOSC32K_XTALEN; SYSCTRL->XOSC32K.bit.ENABLE = 1; @@ -211,12 +222,12 @@ void init_clocks(uint32_t cpu_freq) { while (GCLK->STATUS.bit.SYNCBUSY) { } - // Enable access to the DFLLCTRL reg acc. to Errata 1.2.1 + // Enable access to the DFLLCTRL register according to Errata 1.2.1 SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE; while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) { } // Step 2: Set the coarse and fine values. - // Get the coarse value from the calib data. In case it is not set, + // Get the coarse value from the calibration data. In case it is not set, // set a midrange value. uint32_t coarse = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) & FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos; diff --git a/ports/samd/mcu/samd51/clock_config.c b/ports/samd/mcu/samd51/clock_config.c index 50912b77ed..b30cc46f24 100644 --- a/ports/samd/mcu/samd51/clock_config.c +++ b/ports/samd/mcu/samd51/clock_config.c @@ -117,27 +117,32 @@ void set_cpu_freq(uint32_t cpu_freq_arg) { void check_usb_recovery_mode(void) { #if !MICROPY_HW_XOSC32K - mp_hal_delay_ms(500); - // Check USB status. If not connected, switch DFLL48M back to open loop - if (USB->DEVICE.DeviceEndpoint[0].EPCFG.reg == 0) { - // as per Errata 2.8.3 - OSCCTRL->DFLLMUL.reg = 0; - while (OSCCTRL->DFLLSYNC.bit.DFLLMUL == 1) { - } - // Set the mode to open loop mode - OSCCTRL->DFLLCTRLB.reg = 0; - while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) { - } - OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_RUNSTDBY | OSCCTRL_DFLLCTRLA_ENABLE; - while (OSCCTRL->DFLLSYNC.bit.ENABLE == 1) { - } - OSCCTRL->DFLLVAL.reg = dfll48m_calibration; // Reload DFLLVAL register - while (OSCCTRL->DFLLSYNC.bit.DFLLVAL == 1) { - } - // Set the mode to open loop mode - OSCCTRL->DFLLCTRLB.reg = 0; - while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) { + // Check USB status for up to 1 second. If not connected, + // switch DFLL48M back to open loop + for (int i = 0; i < 100; i++) { + if (USB->DEVICE.DeviceEndpoint[0].EPCFG.reg != 0) { + return; } + mp_hal_delay_ms(10); + } + // No connection. Switch back to open loop mode. + // as per Errata 2.8.3 + OSCCTRL->DFLLMUL.reg = 0; + while (OSCCTRL->DFLLSYNC.bit.DFLLMUL == 1) { + } + // Set the mode to open loop mode + OSCCTRL->DFLLCTRLB.reg = 0; + while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) { + } + OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_RUNSTDBY | OSCCTRL_DFLLCTRLA_ENABLE; + while (OSCCTRL->DFLLSYNC.bit.ENABLE == 1) { + } + OSCCTRL->DFLLVAL.reg = dfll48m_calibration; // Reload DFLLVAL register + while (OSCCTRL->DFLLSYNC.bit.DFLLVAL == 1) { + } + // Set the mode to open loop mode + OSCCTRL->DFLLCTRLB.reg = 0; + while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) { } #endif // MICROPY_HW_XOSC32K } @@ -169,10 +174,10 @@ void check_usb_recovery_mode(void) { // // If MICROPY_HW_DFLL_USB_SYNC = 0, the DFLL48M oscillator is free running using // the pre-configured trim values. In that mode, the peripheral clock is -// not exactly 48Mhz and has a substantional temperature drift. +// not exactly 48Mhz and has a substitutional temperature drift. // // If MICROPY_HW_DFLL_USB_SYNC = 1, the DFLL48 is synchronized with the 1 kHz USB sync -// signal. If after boot there is no USB sync within 500ms, the configuration falls +// signal. If after boot there is no USB sync within 1000 ms, the configuration falls // back to a free running 48Mhz oscillator. // // In all modes, the 48MHz signal has a substantial jitter, largest when @@ -223,16 +228,16 @@ void init_clocks(uint32_t cpu_freq) { OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_XOSC1K; // Setup XOSC32K OSC32KCTRL->INTFLAG.reg = OSC32KCTRL_INTFLAG_XOSC32KRDY | OSC32KCTRL_INTFLAG_XOSC32KFAIL; - OSC32KCTRL->CFDCTRL.bit.CFDEN = 1; // Fall back to internal Osc on crystal fail + OSC32KCTRL->CFDCTRL.bit.CFDEN = 1; // Fall back to internal oscillator on crystal fail OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_CGM_HS | OSC32KCTRL_XOSC32K_XTALEN | OSC32KCTRL_XOSC32K_EN32K | OSC32KCTRL_XOSC32K_EN1K | OSC32KCTRL_XOSC32K_RUNSTDBY | - OSC32KCTRL_XOSC32K_STARTUP(4) | + OSC32KCTRL_XOSC32K_STARTUP(3) | OSC32KCTRL_XOSC32K_ENABLE; - // make sure osc32kcrtl is ready + // Wait until the oscillator is running and stable while (OSC32KCTRL->STATUS.bit.XOSC32KRDY == 0) { }