Skip to content
Snippets Groups Projects
Commit f1251d66 authored by Ferdinand Bachmann's avatar Ferdinand Bachmann Committed by rahix
Browse files

feat(rtc): Add monotonic clock

Squashed commits:

e94f7bf9 epicardium/rtc: add monotonic time
e0691c6d pycardium/modules/utime.c: add bindings for monotonic time
756c13df epicardium/rtc: fix numerically unstable subsecond decoding

         the subsecond encoding function from epic_rtc_set_milliseconds
         and the corresponding decoding function from
         epic_rtc_get_milliseconds are not numerically stable.

         i.e., encoding 5 milliseconds to 20 subsecs and immediately
         afterwards decoding that yields 4 milliseconds.

         Adding a bias of 999 (0.24 milliseconds) to the decoding
         function makes it numerically stable, while never decoding any
         subsecond value to more than 999 milliseconds.

e99e278b epicardium/rtc: only poll time once for calculating monotonic_offset
18936b7e pycardium/modules/utime.c: run clang-format
869ac617 epicardium/rtc: add explanation comment for numerically stable subsecond decode
parent 5773e2e1
No related branches found
No related tags found
No related merge requests found
......@@ -81,6 +81,8 @@ typedef _Bool bool;
#define API_RTC_SCHEDULE_ALARM 0x51
#define API_RTC_SET_MILLISECONDS 0x52
#define API_RTC_GET_MILLISECONDS 0x53
#define API_RTC_GET_MONOTONIC_SECONDS 0x54
#define API_RTC_GET_MONOTONIC_MILLISECONDS 0x55
#define API_LEDS_SET 0x60
#define API_LEDS_SET_HSV 0x61
......@@ -1733,6 +1735,24 @@ API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname));
* ===
*/
/**
* Get the monotonic time in seconds.
*
* :return: monotonic time in seconds
*/
API(API_RTC_GET_MONOTONIC_SECONDS,
uint32_t epic_rtc_get_monotonic_seconds(void)
);
/**
* Get the monotonic time in ms.
*
* :return: monotonic time in milliseconds
*/
API(API_RTC_GET_MONOTONIC_MILLISECONDS,
uint64_t epic_rtc_get_monotonic_milliseconds(void)
);
/**
* Read the current RTC value.
*
......
......@@ -9,6 +9,18 @@
#include <stdint.h>
uint64_t monotonic_offset = 0;
uint32_t epic_rtc_get_monotonic_seconds(void)
{
return epic_rtc_get_seconds() + monotonic_offset / 1000ULL;
}
uint64_t epic_rtc_get_monotonic_milliseconds(void)
{
return epic_rtc_get_milliseconds() + monotonic_offset;
}
uint32_t epic_rtc_get_seconds(void)
{
uint32_t sec, subsec;
......@@ -32,12 +44,31 @@ uint64_t epic_rtc_get_milliseconds(void)
while (RTC_GetTime(&sec, &subsec) == E_BUSY) {
vTaskDelay(pdMS_TO_TICKS(4));
}
return subsec * 1000ULL / 4096 + sec * 1000ULL;
// Without the bias of 999 (0.24 milliseconds), this decoding function is
// numerically unstable:
//
// Encoding 5 milliseconds into 20 subsecs (using the encoding function in
// epic_rtc_set_milliseconds) and decoding it without the bias of 999 yields
// 4 milliseconds.
//
// The following invariants should hold when encoding / decoding from and to
// milliseconds / subseconds:
//
// - 0 <= encode(ms) < 4096 for 0 <= ms < 1000
// - decode(encode(ms)) == ms for 0 <= ms < 1000
// - 0 <= decode(subsec) < 1000 for 0 <= subsec < 4096
//
// These invariants were proven experimentally.
return (subsec * 1000ULL + 999ULL) / 4096 + sec * 1000ULL;
}
void epic_rtc_set_milliseconds(uint64_t milliseconds)
{
uint32_t sec, subsec;
uint64_t old_milliseconds, diff;
old_milliseconds = epic_rtc_get_milliseconds();
sec = milliseconds / 1000;
subsec = (milliseconds % 1000);
......@@ -48,6 +79,9 @@ void epic_rtc_set_milliseconds(uint64_t milliseconds)
;
while (RTC_EnableRTCE(MXC_RTC) == E_BUSY)
;
diff = old_milliseconds - milliseconds;
monotonic_offset += diff;
}
void RTC_IRQHandler(void)
......
......@@ -48,6 +48,8 @@ Q(ticks_add)
Q(ticks_diff)
Q(localtime)
Q(mktime)
Q(monotonic)
Q(monotonic_ms)
Q(time)
Q(time_ms)
Q(set_time)
......
......@@ -51,6 +51,22 @@ static mp_obj_t time_time_ms(void)
}
MP_DEFINE_CONST_FUN_OBJ_0(time_time_ms_obj, time_time_ms);
static mp_obj_t time_monotonic(void)
{
mp_int_t seconds;
seconds = epic_rtc_get_monotonic_seconds();
return mp_obj_new_int(seconds);
}
MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_obj, time_monotonic);
static mp_obj_t time_monotonic_ms(void)
{
uint64_t milliseconds;
milliseconds = epic_rtc_get_monotonic_milliseconds();
return mp_obj_new_int_from_ull(milliseconds);
}
MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_ms_obj, time_monotonic_ms);
static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args)
{
mp_int_t seconds;
......@@ -130,6 +146,9 @@ static const mp_rom_map_elem_t time_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) },
{ MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_time_ms), MP_ROM_PTR(&time_time_ms_obj) },
{ MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&time_monotonic_obj) },
{ MP_ROM_QSTR(MP_QSTR_monotonic_ms),
MP_ROM_PTR(&time_monotonic_ms_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_time), MP_ROM_PTR(&time_set_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_unix_time),
MP_ROM_PTR(&time_set_unix_time_obj) },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment