Skip to content

Commit 3723064

Browse files
committed
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() ```
1 parent fcde108 commit 3723064

File tree

1 file changed

+17
-29
lines changed
  • ports/atmel-samd/supervisor

1 file changed

+17
-29
lines changed

ports/atmel-samd/supervisor/port.c

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ static void rtc_init(void) {
247247
RTC_MODE0_CTRLA_COUNTSYNC;
248248
#endif
249249

250-
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_OVF;
251250

252251
// Set all peripheral interrupt priorities to the lowest priority by default.
253252
for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) {
@@ -501,45 +500,34 @@ uint32_t port_get_saved_word(void) {
501500
// TODO: Move this to an RTC backup register so we can preserve it when only the BACKUP power domain
502501
// is enabled.
503502
static volatile uint64_t overflowed_ticks = 0;
503+
static uint32_t rtc_old_count;
504504

505505
static uint32_t _get_count(uint64_t *overflow_count) {
506-
while (1) {
507-
// Disable interrupts so we can grab the count and the overflow atomically.
508-
common_hal_mcu_disable_interrupts();
509-
510-
#ifdef SAM_D5X_E5X
511-
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) {
512-
}
513-
#endif
514-
// SAMD21 does continuous sync so we don't need to wait here.
515-
516-
uint32_t count = RTC->MODE0.COUNT.reg;
517-
if (overflow_count != NULL) {
518-
*overflow_count = overflowed_ticks;
519-
}
520-
521-
bool overflow_pending = RTC->MODE0.INTFLAG.bit.OVF;
522-
523-
common_hal_mcu_enable_interrupts();
506+
#ifdef SAM_D5X_E5X
507+
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) {
508+
}
509+
#endif
510+
// SAMD21 does continuous sync so we don't need to wait here.
524511

525-
if (!overflow_pending) {
526-
return count;
527-
}
512+
uint32_t count = RTC->MODE0.COUNT.reg;
513+
if (count < rtc_old_count) {
514+
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
515+
// tick.
516+
overflowed_ticks += (1L << (32 - 4));
517+
}
518+
rtc_old_count = count;
528519

529-
// Try again if overflow hasn't been processed yet.
520+
if (overflow_count != NULL) {
521+
*overflow_count = overflowed_ticks;
530522
}
523+
524+
return count;
531525
}
532526

533527
volatile bool _woken_up;
534528

535529
void RTC_Handler(void) {
536530
uint32_t intflag = RTC->MODE0.INTFLAG.reg;
537-
if (intflag & RTC_MODE0_INTFLAG_OVF) {
538-
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_OVF;
539-
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
540-
// tick.
541-
overflowed_ticks += (1L << (32 - 4));
542-
}
543531
#ifdef SAM_D5X_E5X
544532
if (intflag & RTC_MODE0_INTFLAG_PER2) {
545533
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2;

0 commit comments

Comments
 (0)