/* * Copyright 2015 Dius Computing Pty Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * - Neither the name of the copyright holders nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * @author Bernd Meyer * @author Johny Mattsson */ #ifndef RTCTIME_H #define RTCTIME_H #include "rtcaccess.h" // Layout of the RTC storage space: // // 0: Magic. If set to RTC_TIME_MAGIC, the rest is valid. If not, continue to proper boot // // 1: cycle counter offset, lower 32 bit // 2: cycle counter offset, upper 32 bit // // 3: cached result of sleep clock calibration. Has the format of system_rtc_clock_cali_proc(), // or 0 if not available (see 4/5 below) // 4: Number of microseconds we tried to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid // 5: Number of RTC cycles we decided to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid // 6: Number of microseconds which we add to (1/2) to avoid time going backwards // 7: microsecond value returned in the last gettimeofday() to "user space". // // (1:2) set to 0 if no time information is available. // Entries 4-7 are needed because the RTC cycles/second appears quite temperature dependent, // and thus is heavily influenced by what else the chip is doing. As such, any calibration against // the crystal-provided clock (which necessarily would have to happen while the chip is active and // burning a few milliwatts) will be significantly different from the actual frequency during deep // sleep. // Thus, in order to calibrate for deep sleep conditions, we keep track of total sleep microseconds // and total sleep clock cycles between settimeofday() calls (which presumably are NTP driven), and // adjust the calibration accordingly on each settimeofday(). This will also track frequency changes // due to ambient temperature changes. // 6/7 get used when a settimeofday() would result in turning back time. As that can cause all sorts // of ugly issues, we *do* adjust (1/2), but compensate by making the same adjustment to (6). Then each // time gettimeofday() is called, we inspect (7) and determine how much time has passed since the last // call (yes, this gets it wrong if more than a second has passed, but not in a way that causes issues) // and try to take up to 6% of that time away from (6) until (6) reaches 0. Also, whenever we go to // deep sleep, we add (6) to the sleep time, thus catching up all in one go. // Note that for calculating the next sample-aligned wakeup, we need to use the post-adjustment // timeofday(), but for calculating actual sleep time, we use the pre-adjustment one, thus bringing // things back into line. // #define RTC_TIME_BASE 0 // Where the RTC timekeeping block starts in RTC user memory slots #define RTC_TIME_MAGIC 0x44695573 #define RTC_TIME_MAGIC_SLEEP 0x64697573 // What rate we run the CPU at most of the time, and thus the rate at which we keep our time data #define CPU_DEFAULT_MHZ 80 #define CPU_BOOTUP_MHZ 52 #define RTC_TIME_CCOMPARE_INT 6 // Interrupt cause for CCOMPARE0 match // RTCTIME storage #define RTC_TIME_MAGIC_POS (RTC_TIME_BASE+0) #define RTC_CYCLEOFFSETL_POS (RTC_TIME_BASE+1) #define RTC_CYCLEOFFSETH_POS (RTC_TIME_BASE+2) #define RTC_SLEEPTOTALUS_POS (RTC_TIME_BASE+3) #define RTC_SLEEPTOTALCYCLES_POS (RTC_TIME_BASE+4) #define RTC_TODOFFSETUS_POS (RTC_TIME_BASE+5) #define RTC_LASTTODUS_POS (RTC_TIME_BASE+6) #define RTC_CALIBRATION_POS (RTC_TIME_BASE+7) static inline uint64_t rtc_time_get_now_us_adjusted(uint32_t mhz); struct rtc_timeval { uint32_t tv_sec; uint32_t tv_usec; }; static inline bool EARLY_ENTRY_ATTR rtc_time_check_sleep_magic(void) { if (rtc_mem_read(RTC_TIME_MAGIC_POS)==RTC_TIME_MAGIC_SLEEP) return 1; return 0; } static inline bool EARLY_ENTRY_ATTR rtc_time_check_wake_magic(void) { if (rtc_mem_read(RTC_TIME_MAGIC_POS)==RTC_TIME_MAGIC) return 1; return 0; } static inline bool EARLY_ENTRY_ATTR rtc_time_check_magic(void) { return rtc_time_check_wake_magic() || rtc_time_check_sleep_magic(); } static inline void EARLY_ENTRY_ATTR rtc_time_set_wake_magic(void) { rtc_mem_write(RTC_TIME_MAGIC_POS,RTC_TIME_MAGIC); } static inline void rtc_time_set_sleep_magic(void) { rtc_mem_write(RTC_TIME_MAGIC_POS,RTC_TIME_MAGIC_SLEEP); } static inline void rtc_time_unset_magic(void) { rtc_mem_write(RTC_TIME_MAGIC_POS,0); } static inline uint32_t rtc_time_read_raw(void) { return rtc_reg_read(RTC_COUNTER_ADDR); } static inline uint32_t rtc_time_read_raw_ccount(void) { return xthal_get_ccount(); } static inline uint64_t rtc_time_unix_ccount(uint32_t mhz) { // Do *not* cache this, as it can change before the read(s) inside the loop if (!rtc_mem_read64(RTC_CYCLEOFFSETL_POS)) return 0; uint64_t result=0; // Need to be careful here of race conditions while (!result) { uint32_t before=rtc_time_read_raw_ccount(); uint64_t base=rtc_mem_read64(RTC_CYCLEOFFSETL_POS); uint32_t after=rtc_time_read_raw_ccount(); if (before=(4<<12)) && (cali<=(10<<12)); } static inline void rtc_time_settimeofday(const struct rtc_timeval* tv) { if (!rtc_time_check_magic()) return; uint32_t sleep_us=rtc_mem_read(RTC_SLEEPTOTALUS_POS); uint32_t sleep_cycles=rtc_mem_read(RTC_SLEEPTOTALCYCLES_POS); // At this point, the CPU clock will definitely be at the default rate (nodemcu fully booted) uint64_t now_esp_us=rtc_time_get_now_us_adjusted(CPU_DEFAULT_MHZ); uint64_t now_ntp_us=((uint64_t)tv->tv_sec)*1000000+tv->tv_usec; int64_t diff_us=now_esp_us-now_ntp_us; // Store the *actual* time. uint64_t target_ccount=now_ntp_us*CPU_DEFAULT_MHZ; // again, be mindful of race conditions while (1) { uint32_t before=rtc_time_read_raw_ccount(); rtc_mem_write64(RTC_CYCLEOFFSETL_POS,target_ccount-before); uint32_t after=rtc_time_read_raw_ccount(); if (before0 && sleep_us<0xffffffff && sleep_cycles>0 && sleep_cycles<0xffffffff) { uint64_t actual_sleep_us=sleep_us-diff_us; uint32_t cali=(actual_sleep_us<<12)/sleep_cycles; if (rtc_time_calibration_is_sane(cali)) rtc_mem_write(RTC_CALIBRATION_POS,cali); } rtc_mem_write(RTC_SLEEPTOTALUS_POS,0); rtc_mem_write(RTC_SLEEPTOTALCYCLES_POS,0); // Deal with time adjustment if necessary if (diff_us>0) // Time went backwards. Avoid that.... { if (diff_us>0xffffffffULL) diff_us=0xffffffffULL; now_ntp_us+=diff_us; } else diff_us=0; rtc_mem_write(RTC_TODOFFSETUS_POS,diff_us); uint32_t now_s=now_ntp_us/1000000; uint32_t now_us=now_ntp_us%1000000; rtc_time_register_time_reached(now_s,now_us); } static inline uint32_t rtc_time_get_calibration(void) { uint32_t cal=rtc_time_check_magic()?rtc_mem_read(RTC_CALIBRATION_POS):0; if (!cal) { // Make a first guess, most likely to be rather bad, but better then nothing. #ifndef BOOTLOADER_CODE // This will pull in way too much of the system for the bootloader to handle. ets_delay_us(200); cal=system_rtc_clock_cali_proc(); rtc_mem_write(RTC_CALIBRATION_POS,cal); #else cal=6<<12; #endif } return cal; } static inline void rtc_time_invalidate_calibration(void) { rtc_mem_write(RTC_CALIBRATION_POS,0); } static inline uint64_t rtc_time_us_to_ticks(uint64_t us) { uint32_t cal=rtc_time_get_calibration(); return (us<<12)/cal; } static inline uint64_t rtc_time_get_now_us_raw(uint32_t mhz) { if (!rtc_time_check_magic()) return 0; return rtc_time_unix_us(mhz); } static inline uint64_t rtc_time_get_now_us_adjusted(uint32_t mhz) { uint64_t raw=rtc_time_get_now_us_raw(mhz); if (!raw) return 0; return raw+rtc_mem_read(RTC_TODOFFSETUS_POS); } static inline void rtc_time_gettimeofday(struct rtc_timeval* tv, uint32_t mhz) { uint64_t now=rtc_time_get_now_us_adjusted(mhz); uint32_t sec=now/1000000; uint32_t usec=now%1000000; uint32_t to_adjust=rtc_mem_read(RTC_TODOFFSETUS_POS); if (to_adjust) { uint32_t us_passed=rtc_time_us_since_time_reached(sec,usec); uint32_t adjust=us_passed>>4; if (adjust) { if (adjust>to_adjust) adjust=to_adjust; to_adjust-=adjust; now-=adjust; now/1000000; now%1000000; rtc_mem_write(RTC_TODOFFSETUS_POS,to_adjust); } } tv->tv_sec=sec; tv->tv_usec=usec; rtc_time_register_time_reached(sec,usec); } static inline void rtc_time_add_sleep_tracking(uint32_t us, uint32_t cycles) { if (rtc_time_check_magic()) { // us is the one that will grow faster... uint32_t us_before=rtc_mem_read(RTC_SLEEPTOTALUS_POS); uint32_t us_after=us_before+us; uint32_t cycles_after=rtc_mem_read(RTC_SLEEPTOTALCYCLES_POS)+cycles; if (us_after