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 <robert@hammelrath.com>
This commit is contained in:
robert-hh 2023-05-12 11:04:04 +02:00 committed by Damien George
parent 984456731b
commit 2a38531d73
2 changed files with 56 additions and 40 deletions

View File

@ -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;

View File

@ -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) {
}