From cc8af682444469cf15bdb988ea092c11dc213b14 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 19 May 2017 11:37:16 +0800 Subject: [PATCH] syscalls: fix wraparound of RTC time This change removes the erroneous cast to uint32_t (which caused time to wrap around after 1 hour) and splits the multiplication into two terms to remove the wraparound after 13 days. Ref. https://esp32.com/viewtopic.php?f=13&t=1908 --- components/newlib/time.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/components/newlib/time.c b/components/newlib/time.c index d763f83904..51d0894da5 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -46,8 +46,23 @@ #ifdef WITH_RTC static uint64_t get_rtc_time_us() { - uint64_t ticks = rtc_time_get(); - return (uint32_t) ((ticks * esp_clk_slowclk_cal_get()) >> RTC_CLK_CAL_FRACT); + const uint64_t ticks = rtc_time_get(); + const uint32_t cal = esp_clk_slowclk_cal_get(); + /* RTC counter result is up to 2^48, calibration factor is up to 2^24, + * for a 32kHz clock. We need to calculate (assuming no overflow): + * (ticks * cal) >> RTC_CLK_CAL_FRACT + * + * An overflow in the (ticks * cal) multiplication would cause time to + * wrap around after approximately 13 days, which is probably not enough + * for some applications. + * Therefore multiplication is split into two terms, for the lower 32-bit + * and the upper 16-bit parts of "ticks", i.e.: + * ((ticks_low + 2^32 * ticks_high) * cal) >> RTC_CLK_CAL_FRACT + */ + const uint64_t ticks_low = ticks & UINT32_MAX; + const uint64_t ticks_high = ticks >> 32; + return ((ticks_low * cal) >> RTC_CLK_CAL_FRACT) + + ((ticks_high * cal) << (32 - RTC_CLK_CAL_FRACT)); } #endif // WITH_RTC