Skip to content

Commit 1b2f6e3

Browse files
committed
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>
1 parent af88d65 commit 1b2f6e3

6 files changed

Lines changed: 68 additions & 80 deletions

File tree

ports/mimxrt/fatfs_port.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,12 @@
2626
*/
2727

2828
#include "lib/oofatfs/ff.h"
29-
#include "fsl_snvs_lp.h"
30-
29+
#include "shared/timeutils/timeutils.h"
30+
#include "modmachine.h"
3131

3232
MP_WEAK DWORD get_fattime(void) {
33-
snvs_lp_srtc_datetime_t srtcDate;
34-
35-
SNVS_LP_SRTC_GetDatetime(SNVS, &srtcDate);
36-
37-
return ((srtcDate.year - 1980) << 25) | (srtcDate.month << 21) | (srtcDate.day << 16) |
38-
(srtcDate.hour << 11) | ((srtcDate.minute << 5) | (srtcDate.second / 2));
33+
uint64_t seconds = machine_rtc_get_seconds();
34+
timeutils_struct_time_t tm;
35+
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
36+
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);
3937
}

ports/mimxrt/machine_rtc.c

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,33 @@ void machine_rtc_alarm_on() {
7373
machine_rtc_alarm_set_en();
7474
}
7575

76-
uint32_t machine_rtc_get_seconds() {
77-
uint32_t seconds = 0;
78-
uint32_t tmp = 0;
76+
// Returned ticks are in units of 1/32768 seconds.
77+
uint64_t machine_rtc_get_ticks(void) {
78+
uint64_t ticks = 0;
79+
uint64_t tmp = 0;
7980

8081
// Do consecutive reads until value is correct
82+
uint32_t state = disable_irq();
8183
do {
82-
seconds = tmp;
83-
tmp = (SNVS->LPSRTCMR << 17U);
84-
tmp |= (SNVS->LPSRTCLR >> 15U);
85-
} while (tmp != seconds);
84+
ticks = tmp;
85+
tmp = (uint64_t)SNVS->LPSRTCMR << 32U;
86+
tmp |= SNVS->LPSRTCLR;
87+
} while (tmp != ticks);
88+
enable_irq(state);
8689

87-
return seconds;
90+
return ticks;
91+
}
92+
93+
uint64_t machine_rtc_get_seconds(void) {
94+
return machine_rtc_get_ticks() / 32768U;
95+
}
96+
97+
// Input ticks are in units of 1/32768 seconds.
98+
static void machine_rtc_set_ticks(uint64_t ticks) {
99+
SNVS_LP_SRTC_StopTimer(SNVS);
100+
SNVS->LPSRTCMR = (uint32_t)(ticks >> 32U);
101+
SNVS->LPSRTCLR = (uint32_t)ticks;
102+
SNVS_LP_SRTC_StartTimer(SNVS);
88103
}
89104

90105
void machine_rtc_alarm_helper(int seconds, bool repeat) {
@@ -164,18 +179,9 @@ void machine_rtc_start(void) {
164179
SNVS_LP_SRTC_StartTimer(SNVS);
165180
// If the date is not set, set it to a more recent start date,
166181
// MicroPython's first commit.
167-
snvs_lp_srtc_datetime_t srtc_date;
168-
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
169-
if (srtc_date.year <= 1970) {
170-
srtc_date = (snvs_lp_srtc_datetime_t) {
171-
.year = 2013,
172-
.month = 10,
173-
.day = 14,
174-
.hour = 19,
175-
.minute = 53,
176-
.second = 11,
177-
};
178-
SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date);
182+
if (machine_rtc_get_ticks() < 10) {
183+
mp_timestamp_t seconds = timeutils_seconds_since_epoch(2013, 10, 14, 19, 53, 11);
184+
machine_rtc_set_ticks((uint64_t)seconds * 32768ULL);
179185
}
180186
}
181187

@@ -190,39 +196,34 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s
190196
static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, int hour_index) {
191197
if (n_args == 1) {
192198
// Get date and time.
193-
snvs_lp_srtc_datetime_t srtc_date;
194-
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
195-
199+
uint64_t ticks = machine_rtc_get_ticks();
200+
timeutils_struct_time_t tm;
201+
timeutils_seconds_since_epoch_to_struct_time(ticks / 32768U, &tm);
196202
mp_obj_t tuple[8] = {
197-
mp_obj_new_int(srtc_date.year),
198-
mp_obj_new_int(srtc_date.month),
199-
mp_obj_new_int(srtc_date.day),
200-
mp_obj_new_int(timeutils_calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day)),
201-
mp_obj_new_int(srtc_date.hour),
202-
mp_obj_new_int(srtc_date.minute),
203-
mp_obj_new_int(srtc_date.second),
204-
mp_obj_new_int(0),
203+
mp_obj_new_int(tm.tm_year),
204+
mp_obj_new_int(tm.tm_mon),
205+
mp_obj_new_int(tm.tm_mday),
206+
mp_obj_new_int(tm.tm_wday),
207+
mp_obj_new_int(tm.tm_hour),
208+
mp_obj_new_int(tm.tm_min),
209+
mp_obj_new_int(tm.tm_sec),
210+
mp_obj_new_int((ticks % 32768) * 15625U / 512U),
205211
};
206212
return mp_obj_new_tuple(8, tuple);
207213
} else {
208214
// Set date and time.
209215
mp_obj_t *items;
210-
mp_int_t year;
211216
mp_obj_get_array_fixed_n(args[1], 8, &items);
212-
213-
snvs_lp_srtc_datetime_t srtc_date;
214-
year = mp_obj_get_int(items[0]);
215-
srtc_date.year = year >= 100 ? year : year + 2000; // allow 21 for 2021
216-
srtc_date.month = mp_obj_get_int(items[1]);
217-
srtc_date.day = mp_obj_get_int(items[2]);
218-
// Ignore weekday at items[3]
219-
srtc_date.hour = mp_obj_get_int(items[hour_index]);
220-
srtc_date.minute = mp_obj_get_int(items[hour_index + 1]);
221-
srtc_date.second = mp_obj_get_int(items[hour_index + 2]);
222-
if (SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date) != kStatus_Success) {
223-
mp_raise_ValueError(NULL);
224-
}
225-
217+
timeutils_struct_time_t tm = {
218+
.tm_year = mp_obj_get_int(items[0]),
219+
.tm_mon = mp_obj_get_int(items[1]),
220+
.tm_mday = mp_obj_get_int(items[2]),
221+
.tm_hour = mp_obj_get_int(items[4]),
222+
.tm_min = mp_obj_get_int(items[5]),
223+
.tm_sec = mp_obj_get_int(items[6]),
224+
};
225+
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);
226+
machine_rtc_set_ticks((uint64_t)seconds * 32768ULL);
226227
return mp_const_none;
227228
}
228229
}

ports/mimxrt/mbedtls/mbedtls_port.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030

3131
#include "mbedtls_config_port.h"
3232
#if defined(MBEDTLS_HAVE_TIME) || defined(MBEDTLS_HAVE_TIME_DATE)
33-
#include "fsl_snvs_lp.h"
3433
#include "shared/timeutils/timeutils.h"
34+
#include "modmachine.h"
3535
#include "mbedtls/platform_time.h"
3636
#endif
3737

@@ -49,9 +49,7 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t
4949
#if defined(MBEDTLS_HAVE_TIME)
5050
time_t mimxrt_rtctime_seconds(time_t *timer) {
5151
// Get date and date in CPython order.
52-
snvs_lp_srtc_datetime_t date;
53-
SNVS_LP_SRTC_GetDatetime(SNVS, &date);
54-
return timeutils_seconds_since_epoch(date.year, date.month, date.day, date.hour, date.minute, date.second);
52+
return machine_rtc_get_seconds();
5553
}
5654

5755
mbedtls_ms_time_t mbedtls_ms_time(void) {

ports/mimxrt/modmachine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ void mimxrt_sdram_init(void);
4242
void machine_i2s_init0();
4343
void machine_i2s_deinit_all(void);
4444
void machine_rtc_start(void);
45+
uint64_t machine_rtc_get_ticks(void);
46+
uint64_t machine_rtc_get_seconds(void);
4547
void machine_rtc_alarm_helper(int seconds, bool repeat);
4648
void machine_uart_set_baudrate(mp_obj_t uart, uint32_t baudrate);
4749

ports/mimxrt/modtime.c

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,17 @@
2525
* THE SOFTWARE.
2626
*/
2727

28-
#include "py/obj.h"
2928
#include "shared/timeutils/timeutils.h"
30-
#include "fsl_snvs_lp.h"
29+
#include "modmachine.h"
3130

3231
// Get the localtime.
3332
static void mp_time_localtime_get(timeutils_struct_time_t *tm) {
3433
// Get current date and time.
35-
snvs_lp_srtc_datetime_t t;
36-
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
37-
tm->tm_year = t.year;
38-
tm->tm_mon = t.month;
39-
tm->tm_mday = t.day;
40-
tm->tm_hour = t.hour;
41-
tm->tm_min = t.minute;
42-
tm->tm_sec = t.second;
43-
tm->tm_wday = timeutils_calc_weekday(t.year, t.month, t.day);
44-
tm->tm_yday = timeutils_year_day(t.year, t.month, t.day);
34+
uint64_t seconds = machine_rtc_get_seconds();
35+
timeutils_seconds_since_epoch_to_struct_time(seconds, tm);
4536
}
4637

4738
// Return the number of seconds since the Epoch.
4839
static mp_obj_t mp_time_time_get(void) {
49-
snvs_lp_srtc_datetime_t t;
50-
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
51-
return timeutils_obj_from_timestamp(
52-
timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second)
53-
);
40+
return timeutils_obj_from_timestamp(machine_rtc_get_seconds());
5441
}

ports/mimxrt/mphalport.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#include "extmod/misc.h"
3535
#include "ticks.h"
3636
#include "tusb.h"
37-
#include "fsl_snvs_lp.h"
37+
#include "modmachine.h"
3838

3939
#ifndef MICROPY_HW_STDIN_BUFFER_LEN
4040
#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) {
9696
}
9797

9898
uint64_t mp_hal_time_ns(void) {
99-
snvs_lp_srtc_datetime_t t;
100-
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
101-
uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second);
102-
return s * 1000000000ULL;
99+
uint64_t ticks = machine_rtc_get_ticks();
100+
// Need to compute:
101+
// nanoseconds = ticks * 1_000_000_000 / 32768
102+
// = ticks * 5**9 / 64
103+
// but split it into upper and lower 32-bit values so the multiplication doesn't overflow.
104+
return (((ticks >> 32U) * 1953125ULL) << 26U) + (((ticks & 0xffffffff) * 1953125ULL) >> 6U);
103105
}
104106

105107
/*******************************************************************************/

0 commit comments

Comments
 (0)