From 89e86fe6294948df0ca8aade93e9893f317dd5fc Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Sun, 17 Dec 2023 17:39:59 +0000 Subject: [PATCH 01/10] work around silicon erratum in TRNG --- movement/watch_faces/complication/randonaut_face.c | 2 +- movement/watch_faces/complication/toss_up_face.c | 3 ++- watch-library/hardware/watch/watch_private.c | 11 ++++++++++- watch-library/shared/watch/watch.h | 6 +++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c index bca334fb3..886aabb78 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(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..dedc90429 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(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/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index cd607b8e8..4cae3ccbb 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -106,12 +106,21 @@ int getentropy(void *buf, size_t buflen) { } } - hri_trng_clear_CTRLA_ENABLE_bit(TRNG); + watch_disable_TRNG(TRNG); hri_mclk_clear_APBCMASK_TRNG_bit(MCLK); return 0; } +void watch_disable_TRNG(Trng *hw) { + hri_trng_clear_CTRLA_ENABLE_bit(hw); + // silicon erratum: the TRNG may leave internal components powered after disable. + // the workaround is to clear the register twice. + hri_trng_write_CTRLA_reg(hw, 0); + hri_trng_write_CTRLA_reg(hw, 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..4043fdf7b 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, working around a silicon erratum. + */ +void watch_disable_TRNG(Trng* hw); + +#endif /* WATCH_H_ */ From 68f38652273bd6356e35dfcec0949f6783009dcf Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Sun, 17 Dec 2023 17:40:19 +0000 Subject: [PATCH 02/10] work around silicon erratum in SUPC/VREG --- watch-library/hardware/watch/watch_private.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index 4cae3ccbb..20f4ee540 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -35,6 +35,10 @@ void _watch_init(void) { // Use switching regulator for lower power consumption. SUPC->VREG.bit.SEL = 1; + // work around a silicon erratum that 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... From 93d7f38d6797f3a145b87964f4c6aa522b513037 Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Sun, 17 Dec 2023 22:40:04 +0000 Subject: [PATCH 03/10] fix simulator build by declaring Trng type as a void pointer --- watch-library/shared/watch/watch.h | 4 ++++ watch-library/simulator/watch/watch_private.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index 4043fdf7b..8ede1f63d 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -96,6 +96,10 @@ void watch_reset_to_bootloader(void); */ int read(int file, char *ptr, int len); +#ifdef __EMSCRIPTEN__ +typedef void* Trng; +#endif + /** @brief Disables the TRNG, working around a silicon erratum. */ void watch_disable_TRNG(Trng* hw); diff --git a/watch-library/simulator/watch/watch_private.c b/watch-library/simulator/watch/watch_private.c index 3425341a9..509a30b98 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(Trng* hw) {} + // 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 From d10fa223b2fb85bbd79918ef6d19d16acb8e7af5 Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 18 Dec 2023 01:29:28 +0000 Subject: [PATCH 04/10] address SysTick erratum, which can hard-fault the chip --- watch-library/hardware/watch/watch_deepsleep.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/watch-library/hardware/watch/watch_deepsleep.c b/watch-library/hardware/watch/watch_deepsleep.c index ae2ad31d9..2e7edd72e 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,19 @@ void watch_enter_sleep_mode(void) { // disable brownout detector interrupt, which could inadvertently wake us up. SUPC->INTENCLR.bit.BOD33DET = 1; + // work around a silicon erratum 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(); From d96d6f9c919816a01a9139adf02a1063289cac27 Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 18 Dec 2023 03:06:10 +0000 Subject: [PATCH 05/10] make the HAL sleep function obey the chip documentation the sleep mode doesn't get set immediately, and needs to be waited upon. --- watch-library/hardware/hal/include/hpl_sleep.h | 10 ++++++++++ watch-library/hardware/hal/src/hal_sleep.c | 3 +++ watch-library/hardware/hpl/pm/hpl_pm.c | 8 ++++++++ 3 files changed, 21 insertions(+) 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..fd9c84cbe 100644 --- a/watch-library/hardware/hal/src/hal_sleep.c +++ b/watch-library/hardware/hal/src/hal_sleep.c @@ -57,6 +57,9 @@ 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 chip doc. + 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 */ From de692e05e243c88cb1bd1c0fabcb0976f6a343d0 Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 18 Dec 2023 06:41:21 +0000 Subject: [PATCH 06/10] make any unknown interrupts/faults reset the microcontroller --- watch-library/hardware/startup_saml22.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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(); } From 83a0e4e992388e6aaba9390cc1212649ae44d239 Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 22 Jan 2024 00:30:25 +0000 Subject: [PATCH 07/10] annotate TRNG erratum, address review comment --- movement/watch_faces/complication/randonaut_face.c | 2 +- movement/watch_faces/complication/toss_up_face.c | 2 +- watch-library/hardware/watch/watch_private.c | 14 +++++++------- watch-library/shared/watch/watch.h | 8 ++------ watch-library/simulator/watch/watch_private.c | 2 +- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c index 886aabb78..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 - watch_disable_TRNG(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 dedc90429..cf6ca6804 100644 --- a/movement/watch_faces/complication/toss_up_face.c +++ b/movement/watch_faces/complication/toss_up_face.c @@ -255,7 +255,7 @@ uint32_t get_true_entropy(void) { while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready - watch_disable_TRNG(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 diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index 20f4ee540..1a33e5c59 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -110,18 +110,18 @@ int getentropy(void *buf, size_t buflen) { } } - watch_disable_TRNG(TRNG); + watch_disable_TRNG(); hri_mclk_clear_APBCMASK_TRNG_bit(MCLK); return 0; } -void watch_disable_TRNG(Trng *hw) { - hri_trng_clear_CTRLA_ENABLE_bit(hw); - // silicon erratum: the TRNG may leave internal components powered after disable. - // the workaround is to clear the register twice. - hri_trng_write_CTRLA_reg(hw, 0); - hri_trng_write_CTRLA_reg(hw, 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); } diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index 8ede1f63d..d23954ec1 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -96,12 +96,8 @@ void watch_reset_to_bootloader(void); */ int read(int file, char *ptr, int len); -#ifdef __EMSCRIPTEN__ -typedef void* Trng; -#endif - -/** @brief Disables the TRNG, working around a silicon erratum. +/** @brief Disables the TRNG twice in order to work around silicon erratum 1.16.1. */ -void watch_disable_TRNG(Trng* hw); +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 509a30b98..03e1f08b8 100644 --- a/watch-library/simulator/watch/watch_private.c +++ b/watch-library/simulator/watch/watch_private.c @@ -57,7 +57,7 @@ void _watch_disable_tcc(void) {} void _watch_enable_usb(void) {} -void watch_disable_TRNG(Trng* hw) {} +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) { From 2ae8f9863bdb01cb57696548d4b0f7f29a92b3fd Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 22 Jan 2024 00:31:04 +0000 Subject: [PATCH 08/10] annotate SysTick erratum --- watch-library/hardware/watch/watch_deepsleep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/watch-library/hardware/watch/watch_deepsleep.c b/watch-library/hardware/watch/watch_deepsleep.c index 2e7edd72e..efdad6dda 100644 --- a/watch-library/hardware/watch/watch_deepsleep.c +++ b/watch-library/hardware/watch/watch_deepsleep.c @@ -160,7 +160,8 @@ void watch_enter_sleep_mode(void) { // disable brownout detector interrupt, which could inadvertently wake us up. SUPC->INTENCLR.bit.BOD33DET = 1; - // work around a silicon erratum by disabling the SysTick interrupt, which is + // 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); From af49d3cfcb5437cf2c476197095fed0210c87ff5 Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 22 Jan 2024 00:31:27 +0000 Subject: [PATCH 09/10] annotate voltage regulation erratum --- watch-library/hardware/watch/watch_private.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index 1a33e5c59..002a168a3 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -35,7 +35,9 @@ void _watch_init(void) { // Use switching regulator for lower power consumption. SUPC->VREG.bit.SEL = 1; - // work around a silicon erratum that causes the microcontroller to lock up on leaving standby: + + // 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; From a2a60eb31a9bd8990705d4799406697bc9b0888f Mon Sep 17 00:00:00 2001 From: Alex Maestas Date: Mon, 22 Jan 2024 00:37:25 +0000 Subject: [PATCH 10/10] annotate SLEEPCFG-register detail --- watch-library/hardware/hal/src/hal_sleep.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/watch-library/hardware/hal/src/hal_sleep.c b/watch-library/hardware/hal/src/hal_sleep.c index fd9c84cbe..2fac64d54 100644 --- a/watch-library/hardware/hal/src/hal_sleep.c +++ b/watch-library/hardware/hal/src/hal_sleep.c @@ -57,7 +57,13 @@ 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 chip doc. + // 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();