Skip to content

Commit

Permalink
Merge branch 'silicon-errata' into advanced
Browse files Browse the repository at this point in the history
Implements the recommended workarounds for numerous silicon errata,
reducing power consumption and preventing freezes and hard faults.

Tested-by: Alex Maestas <[email protected]>
Reviewed-by: Wesley Aptekar-Cassels <[email protected]>
Reviewed-by: Matheus Afonso Martins Moreira <[email protected]>
Signed-off-by: Matheus Afonso Martins Moreira <[email protected]>
GitHub-Pull-Request: joeycastillo#340
GitHub-Related-Issue: joeycastillo#361
GitHub-Related-Issue: joeycastillo#359
Reference: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU32/ProductDocuments/Errata/SAM-L22-Family-Silicon-Errata-and-Data-Sheet-Clarification-DS80000782.pdf
  • Loading branch information
matheusmoreira committed Mar 5, 2024
2 parents 4752aeb + a2a60eb commit 8763df9
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 7 deletions.
2 changes: 1 addition & 1 deletion movement/watch_faces/complication/randonaut_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion movement/watch_faces/complication/toss_up_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions watch-library/hardware/hal/include/hpl_sleep.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
9 changes: 9 additions & 0 deletions watch-library/hardware/hal/src/hal_sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions watch-library/hardware/hpl/pm/hpl_pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
3 changes: 1 addition & 2 deletions watch-library/hardware/startup_saml22.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,5 @@ void Reset_Handler(void)
*/
void Dummy_Handler(void)
{
while (1) {
}
NVIC_SystemReset();
}
10 changes: 9 additions & 1 deletion watch-library/hardware/watch/watch_deepsleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down
17 changes: 16 additions & 1 deletion watch-library/hardware/watch/watch_private.c
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion watch-library/shared/watch/watch.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,8 @@ void watch_reset_to_bootloader(void);
*/
int read(int file, char *ptr, int len);

#endif /* WATCH_H_ */
/** @brief Disables the TRNG twice in order to work around silicon erratum 1.16.1.
*/
void watch_disable_TRNG();

#endif /* WATCH_H_ */
2 changes: 2 additions & 0 deletions watch-library/simulator/watch/watch_private.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8763df9

Please sign in to comment.