diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c index bca334fb3..3bbc147f6 100644 --- a/movement/watch_faces/complication/randonaut_face.c +++ b/movement/watch_faces/complication/randonaut_face.c @@ -357,7 +357,7 @@ static uint32_t _get_true_entropy(void) { while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready - hri_trng_clear_CTRLA_ENABLE_bit(TRNG); + watch_disable_TRNG(); hri_mclk_clear_APBCMASK_TRNG_bit(MCLK); return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it #endif diff --git a/movement/watch_faces/complication/toss_up_face.c b/movement/watch_faces/complication/toss_up_face.c index 08dd00522..cf6ca6804 100644 --- a/movement/watch_faces/complication/toss_up_face.c +++ b/movement/watch_faces/complication/toss_up_face.c @@ -255,7 +255,8 @@ uint32_t get_true_entropy(void) { while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready - hri_trng_clear_CTRLA_ENABLE_bit(TRNG); + watch_disable_TRNG(); + hri_mclk_clear_APBCMASK_TRNG_bit(MCLK); return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it #endif diff --git a/watch-library/hardware/hal/include/hpl_sleep.h b/watch-library/hardware/hal/include/hpl_sleep.h index 6731ec30e..4106fb730 100644 --- a/watch-library/hardware/hal/include/hpl_sleep.h +++ b/watch-library/hardware/hal/include/hpl_sleep.h @@ -70,6 +70,16 @@ extern "C" { */ int32_t _set_sleep_mode(const uint8_t mode); +/** + * \brief Get the sleep mode for the device + * + * This function gets the sleep mode for the device. + * + * \return the current value of the sleep mode configuration bits + */ +int32_t _get_sleep_mode(void); + + /** * \brief Reset MCU */ diff --git a/watch-library/hardware/hal/src/hal_sleep.c b/watch-library/hardware/hal/src/hal_sleep.c index 89472f156..2fac64d54 100644 --- a/watch-library/hardware/hal/src/hal_sleep.c +++ b/watch-library/hardware/hal/src/hal_sleep.c @@ -57,6 +57,15 @@ int sleep(const uint8_t mode) if (ERR_NONE != _set_sleep_mode(mode)) return ERR_INVALID_ARG; + // wait for the mode set to actually take, per note in Microchip data + // sheet DS60001465, section 19.8.2: + // + // A small latency happens between the store instruction and actual + // writing of the SLEEPCFG register due to bridges. Software has to make + // sure the SLEEPCFG register reads the wanted value before issuing WFI + // instruction. + while(_get_sleep_mode() != mode); + _go_to_sleep(); return ERR_NONE; diff --git a/watch-library/hardware/hpl/pm/hpl_pm.c b/watch-library/hardware/hpl/pm/hpl_pm.c index d6439f1d2..2e9e37b5f 100644 --- a/watch-library/hardware/hpl/pm/hpl_pm.c +++ b/watch-library/hardware/hpl/pm/hpl_pm.c @@ -63,6 +63,14 @@ int32_t _set_sleep_mode(const uint8_t mode) return ERR_NONE; } +/** + * \brief Get the sleep mode for the device + */ +int32_t _get_sleep_mode() +{ + return hri_pm_read_SLEEPCFG_SLEEPMODE_bf(PM); +} + /** * \brief Set performance level */ diff --git a/watch-library/hardware/startup_saml22.c b/watch-library/hardware/startup_saml22.c index f4982564d..2d2027f0b 100755 --- a/watch-library/hardware/startup_saml22.c +++ b/watch-library/hardware/startup_saml22.c @@ -220,6 +220,5 @@ void Reset_Handler(void) */ void Dummy_Handler(void) { - while (1) { - } + NVIC_SystemReset(); } diff --git a/watch-library/hardware/watch/watch_deepsleep.c b/watch-library/hardware/watch/watch_deepsleep.c index ae2ad31d9..efdad6dda 100644 --- a/watch-library/hardware/watch/watch_deepsleep.c +++ b/watch-library/hardware/watch/watch_deepsleep.c @@ -22,6 +22,8 @@ * SOFTWARE. */ +#include "hpl_systick_config.h" + #include "watch_extint.h" // this warning only appears when you `make BOARD=OSO-SWAT-A1-02`. it's annoying, @@ -158,14 +160,20 @@ void watch_enter_sleep_mode(void) { // disable brownout detector interrupt, which could inadvertently wake us up. SUPC->INTENCLR.bit.BOD33DET = 1; + // per Microchip datasheet clarification DS80000782, + // work around silicon erratum 1.8.4 by disabling the SysTick interrupt, which is + // enabled as part of driver init, before going to sleep. + SysTick->CTRL = SysTick->CTRL & ~(CONF_SYSTICK_TICKINT << SysTick_CTRL_TICKINT_Pos); + // disable all pins _watch_disable_all_pins_except_rtc(); // enter standby (4); we basically hang out here until an interrupt wakes us. sleep(4); - // and we awake! re-enable the brownout detector + // and we awake! re-enable the brownout detector and SysTick interrupt SUPC->INTENSET.bit.BOD33DET = 1; + SysTick->CTRL = SysTick->CTRL | (CONF_SYSTICK_TICKINT << SysTick_CTRL_TICKINT_Pos); // call app_setup so the app can re-enable everything we disabled. app_setup(); diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index cd607b8e8..002a168a3 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -35,6 +35,12 @@ void _watch_init(void) { // Use switching regulator for lower power consumption. SUPC->VREG.bit.SEL = 1; + + // per Microchip datasheet clarification DS80000782, + // work around silicon erratum 1.7.2, which causes the microcontroller to lock up on leaving standby: + // request that the voltage regulator run in standby, and also that it switch to PL0. + SUPC->VREG.bit.RUNSTDBY = 1; + SUPC->VREG.bit.STDBYPL0 = 1; while(!SUPC->STATUS.bit.VREGRDY); // wait for voltage regulator to become ready // check the battery voltage... @@ -106,12 +112,21 @@ int getentropy(void *buf, size_t buflen) { } } - hri_trng_clear_CTRLA_ENABLE_bit(TRNG); + watch_disable_TRNG(); hri_mclk_clear_APBCMASK_TRNG_bit(MCLK); return 0; } +void watch_disable_TRNG() { + // per Microchip datasheet clarification DS80000782, + // silicon erratum 1.16.1 indicates that the TRNG may leave internal components powered after being disabled. + // the workaround is to disable the TRNG by clearing the control register, twice. + hri_trng_write_CTRLA_reg(TRNG, 0); + hri_trng_write_CTRLA_reg(TRNG, 0); +} + + void _watch_enable_tcc(void) { // clock TCC0 with the main clock (8 MHz) and enable the peripheral clock. hri_gclk_write_PCHCTRL_reg(GCLK, TCC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK0_Val | GCLK_PCHCTRL_CHEN); diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index 790f9a163..d23954ec1 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -96,4 +96,8 @@ void watch_reset_to_bootloader(void); */ int read(int file, char *ptr, int len); -#endif /* WATCH_H_ */ \ No newline at end of file +/** @brief Disables the TRNG twice in order to work around silicon erratum 1.16.1. + */ +void watch_disable_TRNG(); + +#endif /* WATCH_H_ */ diff --git a/watch-library/simulator/watch/watch_private.c b/watch-library/simulator/watch/watch_private.c index 3425341a9..03e1f08b8 100644 --- a/watch-library/simulator/watch/watch_private.c +++ b/watch-library/simulator/watch/watch_private.c @@ -57,6 +57,8 @@ void _watch_disable_tcc(void) {} void _watch_enable_usb(void) {} +void watch_disable_TRNG() {} + // this function ends up getting called by printf to log stuff to the USB console. int _write(int file, char *ptr, int len) { // TODO: (a2) hook to UI