mimxrt: Increase resolution of RTC to 1/32768 seconds.

Currently the mimxrt port has a resolution of only 1 second for
`machine.RTC().datetime()` and `time.time_ns()`.  This means (among other
things) that it fails the `tests/extmod/time_time_ns.py` test, which
requires requires at least 5ms of resolution.

The underlying RTC hardware is just a 64-bit counter, and the HAL functions
`SNVS_LP_SRTC_GetDatetime()` and `SNVS_LP_SRTC_SetDatetime()` do
conversions between y/m/d/h/m/s and this 64-bit value, which counts at a
rate of 32kHz.

This commit changes the RTC code to access the 64-bit counter directly and
therefore improve resolution of all RTC functions to 1/32768 seconds.

That makes things much simpler because it a lot of places the code wants to
know the number of seconds since the Epoch.  Currently it uses a
combination of `SNVS_LP_SRTC_GetDatetime()` and
`timeutils_seconds_since_epoch()` which converts the 64-bit counter to
date-time and then back to seconds.  Those operations are computationally
expensive.

With this commit, getting the number of seconds since the Epoch is as
simple as reading the 64-bit counter and dividing by 32768.  We can
leverage a lot of the timeutils functions to simplify everything, and make
it similar to other ports like rp2.

Benefits of this change:
- simpler, more efficient code to get/set RTC
- `machine.RTC().datetime()` now has a non-zero value in the last slot,
  being the number of microseconds, and has a resolution of 1/32768 seconds
- `time.time_ns()` now has a resolution of 1/32768 seconds
- the `test/extmod/time_time_ns.py` test now passes

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George
2026-01-15 15:12:50 +11:00
parent af88d65e86
commit 1b2f6e3d1f
6 changed files with 68 additions and 80 deletions

View File

@@ -26,14 +26,12 @@
*/
#include "lib/oofatfs/ff.h"
#include "fsl_snvs_lp.h"
#include "shared/timeutils/timeutils.h"
#include "modmachine.h"
MP_WEAK DWORD get_fattime(void) {
snvs_lp_srtc_datetime_t srtcDate;
SNVS_LP_SRTC_GetDatetime(SNVS, &srtcDate);
return ((srtcDate.year - 1980) << 25) | (srtcDate.month << 21) | (srtcDate.day << 16) |
(srtcDate.hour << 11) | ((srtcDate.minute << 5) | (srtcDate.second / 2));
uint64_t seconds = machine_rtc_get_seconds();
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21) | ((tm.tm_mday) << 16) | ((tm.tm_hour) << 11) | ((tm.tm_min) << 5) | (tm.tm_sec / 2);
}

View File

@@ -73,18 +73,33 @@ void machine_rtc_alarm_on() {
machine_rtc_alarm_set_en();
}
uint32_t machine_rtc_get_seconds() {
uint32_t seconds = 0;
uint32_t tmp = 0;
// Returned ticks are in units of 1/32768 seconds.
uint64_t machine_rtc_get_ticks(void) {
uint64_t ticks = 0;
uint64_t tmp = 0;
// Do consecutive reads until value is correct
uint32_t state = disable_irq();
do {
seconds = tmp;
tmp = (SNVS->LPSRTCMR << 17U);
tmp |= (SNVS->LPSRTCLR >> 15U);
} while (tmp != seconds);
ticks = tmp;
tmp = (uint64_t)SNVS->LPSRTCMR << 32U;
tmp |= SNVS->LPSRTCLR;
} while (tmp != ticks);
enable_irq(state);
return seconds;
return ticks;
}
uint64_t machine_rtc_get_seconds(void) {
return machine_rtc_get_ticks() / 32768U;
}
// Input ticks are in units of 1/32768 seconds.
static void machine_rtc_set_ticks(uint64_t ticks) {
SNVS_LP_SRTC_StopTimer(SNVS);
SNVS->LPSRTCMR = (uint32_t)(ticks >> 32U);
SNVS->LPSRTCLR = (uint32_t)ticks;
SNVS_LP_SRTC_StartTimer(SNVS);
}
void machine_rtc_alarm_helper(int seconds, bool repeat) {
@@ -164,18 +179,9 @@ void machine_rtc_start(void) {
SNVS_LP_SRTC_StartTimer(SNVS);
// If the date is not set, set it to a more recent start date,
// MicroPython's first commit.
snvs_lp_srtc_datetime_t srtc_date;
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
if (srtc_date.year <= 1970) {
srtc_date = (snvs_lp_srtc_datetime_t) {
.year = 2013,
.month = 10,
.day = 14,
.hour = 19,
.minute = 53,
.second = 11,
};
SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date);
if (machine_rtc_get_ticks() < 10) {
mp_timestamp_t seconds = timeutils_seconds_since_epoch(2013, 10, 14, 19, 53, 11);
machine_rtc_set_ticks((uint64_t)seconds * 32768ULL);
}
}
@@ -190,39 +196,34 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s
static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, int hour_index) {
if (n_args == 1) {
// Get date and time.
snvs_lp_srtc_datetime_t srtc_date;
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
uint64_t ticks = machine_rtc_get_ticks();
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(ticks / 32768U, &tm);
mp_obj_t tuple[8] = {
mp_obj_new_int(srtc_date.year),
mp_obj_new_int(srtc_date.month),
mp_obj_new_int(srtc_date.day),
mp_obj_new_int(timeutils_calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day)),
mp_obj_new_int(srtc_date.hour),
mp_obj_new_int(srtc_date.minute),
mp_obj_new_int(srtc_date.second),
mp_obj_new_int(0),
mp_obj_new_int(tm.tm_year),
mp_obj_new_int(tm.tm_mon),
mp_obj_new_int(tm.tm_mday),
mp_obj_new_int(tm.tm_wday),
mp_obj_new_int(tm.tm_hour),
mp_obj_new_int(tm.tm_min),
mp_obj_new_int(tm.tm_sec),
mp_obj_new_int((ticks % 32768) * 15625U / 512U),
};
return mp_obj_new_tuple(8, tuple);
} else {
// Set date and time.
mp_obj_t *items;
mp_int_t year;
mp_obj_get_array_fixed_n(args[1], 8, &items);
snvs_lp_srtc_datetime_t srtc_date;
year = mp_obj_get_int(items[0]);
srtc_date.year = year >= 100 ? year : year + 2000; // allow 21 for 2021
srtc_date.month = mp_obj_get_int(items[1]);
srtc_date.day = mp_obj_get_int(items[2]);
// Ignore weekday at items[3]
srtc_date.hour = mp_obj_get_int(items[hour_index]);
srtc_date.minute = mp_obj_get_int(items[hour_index + 1]);
srtc_date.second = mp_obj_get_int(items[hour_index + 2]);
if (SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date) != kStatus_Success) {
mp_raise_ValueError(NULL);
}
timeutils_struct_time_t tm = {
.tm_year = mp_obj_get_int(items[0]),
.tm_mon = mp_obj_get_int(items[1]),
.tm_mday = mp_obj_get_int(items[2]),
.tm_hour = mp_obj_get_int(items[4]),
.tm_min = mp_obj_get_int(items[5]),
.tm_sec = mp_obj_get_int(items[6]),
};
uint32_t seconds = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
machine_rtc_set_ticks((uint64_t)seconds * 32768ULL);
return mp_const_none;
}
}

View File

@@ -30,8 +30,8 @@
#include "mbedtls_config_port.h"
#if defined(MBEDTLS_HAVE_TIME) || defined(MBEDTLS_HAVE_TIME_DATE)
#include "fsl_snvs_lp.h"
#include "shared/timeutils/timeutils.h"
#include "modmachine.h"
#include "mbedtls/platform_time.h"
#endif
@@ -49,9 +49,7 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t
#if defined(MBEDTLS_HAVE_TIME)
time_t mimxrt_rtctime_seconds(time_t *timer) {
// Get date and date in CPython order.
snvs_lp_srtc_datetime_t date;
SNVS_LP_SRTC_GetDatetime(SNVS, &date);
return timeutils_seconds_since_epoch(date.year, date.month, date.day, date.hour, date.minute, date.second);
return machine_rtc_get_seconds();
}
mbedtls_ms_time_t mbedtls_ms_time(void) {

View File

@@ -42,6 +42,8 @@ void mimxrt_sdram_init(void);
void machine_i2s_init0();
void machine_i2s_deinit_all(void);
void machine_rtc_start(void);
uint64_t machine_rtc_get_ticks(void);
uint64_t machine_rtc_get_seconds(void);
void machine_rtc_alarm_helper(int seconds, bool repeat);
void machine_uart_set_baudrate(mp_obj_t uart, uint32_t baudrate);

View File

@@ -25,30 +25,17 @@
* THE SOFTWARE.
*/
#include "py/obj.h"
#include "shared/timeutils/timeutils.h"
#include "fsl_snvs_lp.h"
#include "modmachine.h"
// Get the localtime.
static void mp_time_localtime_get(timeutils_struct_time_t *tm) {
// Get current date and time.
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
tm->tm_year = t.year;
tm->tm_mon = t.month;
tm->tm_mday = t.day;
tm->tm_hour = t.hour;
tm->tm_min = t.minute;
tm->tm_sec = t.second;
tm->tm_wday = timeutils_calc_weekday(t.year, t.month, t.day);
tm->tm_yday = timeutils_year_day(t.year, t.month, t.day);
uint64_t seconds = machine_rtc_get_seconds();
timeutils_seconds_since_epoch_to_struct_time(seconds, tm);
}
// Return the number of seconds since the Epoch.
static mp_obj_t mp_time_time_get(void) {
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
return timeutils_obj_from_timestamp(
timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second)
);
return timeutils_obj_from_timestamp(machine_rtc_get_seconds());
}

View File

@@ -34,7 +34,7 @@
#include "extmod/misc.h"
#include "ticks.h"
#include "tusb.h"
#include "fsl_snvs_lp.h"
#include "modmachine.h"
#ifndef MICROPY_HW_STDIN_BUFFER_LEN
#define MICROPY_HW_STDIN_BUFFER_LEN 512
@@ -96,10 +96,12 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
}
uint64_t mp_hal_time_ns(void) {
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second);
return s * 1000000000ULL;
uint64_t ticks = machine_rtc_get_ticks();
// Need to compute:
// nanoseconds = ticks * 1_000_000_000 / 32768
// = ticks * 5**9 / 64
// but split it into upper and lower 32-bit values so the multiplication doesn't overflow.
return (((ticks >> 32U) * 1953125ULL) << 26U) + (((ticks & 0xffffffff) * 1953125ULL) >> 6U);
}
/*******************************************************************************/