]> granicus.if.org Git - esp-idf/commitdiff
syscalls: fix wraparound of RTC time
authorIvan Grokhotkov <ivan@espressif.com>
Fri, 19 May 2017 03:37:16 +0000 (11:37 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Mon, 12 Jun 2017 07:21:43 +0000 (15:21 +0800)
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

index d763f839041b797d8534482171283f82fe9ecabd..51d0894da552b072d9431b968d22384c91fdf8f5 100644 (file)
 #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