samd: Don't rely on RTC interrupt
I instrumented RTC_Handler and determined that on SAMD51 it was possible for the interrupt to be delivered well before the actual overflow of the RTC COUNT register (e.g., a value as small as 0xffff_fffd could be seen at the time of overflow) Rather than depending on the overflow interrupt coming in at the same time as COUNT overflows (exactly), rely only on observed values of COUNT in _get_count, overflowing when it wraps around from a high value to a low one. With this change, PLUS a second change so that it is possible to warp the RTC counter close to an overflow and test in 20ms instead of 3 days, there was no problem detected over 20000+ overflows. Before, a substantial fraction (much greater than 10%) of overflows failed. Fixes #5985 Change to common-hal/rtc/RTC.c for time warping (plus make rtc_old_count non-static): ```patch void common_hal_rtc_set_calibration(int calibration) { + + common_hal_mcu_disable_interrupts(); + + RTC->MODE0.COUNT.reg = 0xffffff00; + rtc_old_count = 0; + do { + while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) { } + } + while(RTC->MODE0.COUNT.reg < 0xffffff00); + common_hal_mcu_enable_interrupts(); + + mp_printf(&mp_plat_print, "Warping RTC in calibration setter count=%08x rtc_old_count=%08x\n", RTC->MODE0.COUNT.reg, rtc_old_count); ``` Test program: ```python import time from rtc import RTC i = 0 while True: RTC().calibration = 1 # Warps to ~16ms before overflow, with patch to RTC code t0 = time.monotonic_ns() et = t0 + 20_000_000 # 20ms while (t1 := time.monotonic_ns()) < et: pass i += 1 print(f"{i:6d}: duration {t1-t0}") if t1-t0 > 200_000_000: break print() ```
This commit is contained in:
parent
fcde108d03
commit
372306411a
@ -247,7 +247,6 @@ static void rtc_init(void) {
|
|||||||
RTC_MODE0_CTRLA_COUNTSYNC;
|
RTC_MODE0_CTRLA_COUNTSYNC;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_OVF;
|
|
||||||
|
|
||||||
// Set all peripheral interrupt priorities to the lowest priority by default.
|
// Set all peripheral interrupt priorities to the lowest priority by default.
|
||||||
for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) {
|
for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) {
|
||||||
@ -501,45 +500,34 @@ uint32_t port_get_saved_word(void) {
|
|||||||
// TODO: Move this to an RTC backup register so we can preserve it when only the BACKUP power domain
|
// TODO: Move this to an RTC backup register so we can preserve it when only the BACKUP power domain
|
||||||
// is enabled.
|
// is enabled.
|
||||||
static volatile uint64_t overflowed_ticks = 0;
|
static volatile uint64_t overflowed_ticks = 0;
|
||||||
|
static uint32_t rtc_old_count;
|
||||||
|
|
||||||
static uint32_t _get_count(uint64_t *overflow_count) {
|
static uint32_t _get_count(uint64_t *overflow_count) {
|
||||||
while (1) {
|
#ifdef SAM_D5X_E5X
|
||||||
// Disable interrupts so we can grab the count and the overflow atomically.
|
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) {
|
||||||
common_hal_mcu_disable_interrupts();
|
|
||||||
|
|
||||||
#ifdef SAM_D5X_E5X
|
|
||||||
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) {
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// SAMD21 does continuous sync so we don't need to wait here.
|
|
||||||
|
|
||||||
uint32_t count = RTC->MODE0.COUNT.reg;
|
|
||||||
if (overflow_count != NULL) {
|
|
||||||
*overflow_count = overflowed_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool overflow_pending = RTC->MODE0.INTFLAG.bit.OVF;
|
|
||||||
|
|
||||||
common_hal_mcu_enable_interrupts();
|
|
||||||
|
|
||||||
if (!overflow_pending) {
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try again if overflow hasn't been processed yet.
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
// SAMD21 does continuous sync so we don't need to wait here.
|
||||||
|
|
||||||
|
uint32_t count = RTC->MODE0.COUNT.reg;
|
||||||
|
if (count < rtc_old_count) {
|
||||||
|
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
|
||||||
|
// tick.
|
||||||
|
overflowed_ticks += (1L << (32 - 4));
|
||||||
|
}
|
||||||
|
rtc_old_count = count;
|
||||||
|
|
||||||
|
if (overflow_count != NULL) {
|
||||||
|
*overflow_count = overflowed_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile bool _woken_up;
|
volatile bool _woken_up;
|
||||||
|
|
||||||
void RTC_Handler(void) {
|
void RTC_Handler(void) {
|
||||||
uint32_t intflag = RTC->MODE0.INTFLAG.reg;
|
uint32_t intflag = RTC->MODE0.INTFLAG.reg;
|
||||||
if (intflag & RTC_MODE0_INTFLAG_OVF) {
|
|
||||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_OVF;
|
|
||||||
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
|
|
||||||
// tick.
|
|
||||||
overflowed_ticks += (1L << (32 - 4));
|
|
||||||
}
|
|
||||||
#ifdef SAM_D5X_E5X
|
#ifdef SAM_D5X_E5X
|
||||||
if (intflag & RTC_MODE0_INTFLAG_PER2) {
|
if (intflag & RTC_MODE0_INTFLAG_PER2) {
|
||||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2;
|
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2;
|
||||||
|
Loading…
Reference in New Issue
Block a user