This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 6b71f53b3c30abb58ec238d3f44c6a9238348d7c Author: Max Kriegleder <[email protected]> AuthorDate: Sat Oct 24 19:02:49 2026 +0200 arch/arm/nrf52: Count a pending RTC overflow when reading the time. The time is periods * 512 s plus the COUNTER. An overflow is counted in 'periods' by the RTC interrupt, so a time read with interrupts disabled between the COUNTER wrapping and that interrupt running came out 512 s early: up_timer_gettime() from a critical section or a higher-priority handler, and rtc_prepare_alarm() from up_alarm_start(). up_alarm_cancel() also read the COUNTER inside its critical section but converted it after leaving it, so the interrupt could count the overflow in between and the time came out 512 s late. Read the time in one place, rtc_now(), always with interrupts disabled. While the overflow has not been counted its OVRFLW event is still set; a COUNTER in its lower half was then read after the overflow, and one in its upper half before it. Checked on the host against a modelled COUNTER and OVRFLW event: read just after an overflow whose interrupt has not run, the time was 512 s behind before and is right now; reads before the overflow and after its interrupt are unchanged. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Max Kriegleder <[email protected]> --- arch/arm/src/nrf52/nrf52_tickless_rtc.c | 36 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/arch/arm/src/nrf52/nrf52_tickless_rtc.c b/arch/arm/src/nrf52/nrf52_tickless_rtc.c index e716a30cf8d..91a750ac4bc 100644 --- a/arch/arm/src/nrf52/nrf52_tickless_rtc.c +++ b/arch/arm/src/nrf52/nrf52_tickless_rtc.c @@ -104,15 +104,31 @@ struct nrf52_tickless_dev_s g_tickless_dev; * Private Functions ****************************************************************************/ -static inline void rtc_counter_to_ts(uint32_t counter, struct timespec *now) +/* Read the COUNTER and the absolute time it stands for. Called with + * interrupts disabled, so an overflow may not have been counted in + * 'periods' yet: its OVRFLW event is then still set. A COUNTER in its + * lower half was read after that overflow, one in its upper half before. + */ + +static uint32_t rtc_now(struct timespec *now) { + const uint32_t rtc_base = NRF52_RTC_GETBASE(g_tickless_dev.rtc); + uint32_t periods = g_tickless_dev.periods; + uint32_t counter; uint32_t usec; + counter = NRF52_RTC_GETCOUNTER_REG(rtc_base); + if (getreg32(rtc_base + NRF52_RTC_EVENTS_OVRFLW_OFFSET) != 0 && + counter <= RTC_COUNTER_MASK / 2) + { + periods++; + } + usec = COUNTER_TO_USEC(counter); - now->tv_sec = usec / USEC_PER_SEC; + now->tv_sec = usec / USEC_PER_SEC + periods * NRF52_RTC_PERIOD; now->tv_nsec = (usec % USEC_PER_SEC) * NSEC_PER_USEC; - now->tv_sec += g_tickless_dev.periods * NRF52_RTC_PERIOD; + return counter; } /* Convert a time shorter than one RTC period to counts, rounding up so @@ -138,8 +154,7 @@ static void rtc_prepare_alarm(void) /* Time left until the alarm, zero if it has passed */ - counter = NRF52_RTC_GETCOUNTER_REG(rtc_base); - rtc_counter_to_ts(counter, &now); + counter = rtc_now(&now); clock_timespec_subtract(&g_tickless_dev.alarm, &now, &delta); /* An alarm further away is prepared again on a later overflow */ @@ -257,21 +272,18 @@ static int rtc_handler(int irq, void *context, void *arg) int up_alarm_cancel(struct timespec *ts) { - uint32_t counter; irqstate_t flags; flags = enter_critical_section(); NRF52_RTC_DISABLEINT(g_tickless_dev.rtc, NRF52_RTC_EVT_COMPARE0); - NRF52_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); + rtc_now(ts); NRF52_RTC_ACKINT(g_tickless_dev.rtc, NRF52_RTC_EVT_COMPARE0); g_tickless_dev.alarm_set = false; leave_critical_section(flags); - rtc_counter_to_ts(counter, ts); - return OK; } @@ -303,14 +315,10 @@ int up_alarm_start(const struct timespec *ts) int up_timer_gettime(struct timespec *ts) { - uint32_t counter; irqstate_t flags; flags = enter_critical_section(); - - NRF52_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); - rtc_counter_to_ts(counter, ts); - + rtc_now(ts); leave_critical_section(flags); return OK;
