From 346cf4430d198aa5a021adebd9de8f5b0e35be12 Mon Sep 17 00:00:00 2001 From: Renz Bagaporo Date: Tue, 23 Jun 2020 16:46:06 +0800 Subject: [PATCH] esp_system: introduce system time functions - Introduce system time function and concept of system time provider. esp_timer is system time provider when present. - Set the reference point for system time, g_startup_time. - Use the system time functions in newlib instead of calling esp_timer functions directly --- .../include/esp_private/system_internal.h | 10 +++++ components/esp_system/CMakeLists.txt | 4 +- .../include/esp_private/startup_internal.h | 4 ++ components/esp_system/include/esp_system.h | 16 +++++++ components/esp_system/port/cpu_start.c | 22 ++++++++- components/esp_system/startup.c | 7 +-- components/esp_system/system_time.c | 41 +++++++++++++++++ components/esp_timer/CMakeLists.txt | 3 +- .../private_include/esp_timer_impl.h | 5 +++ components/esp_timer/src/esp_timer.c | 9 ++-- components/esp_timer/src/timekeeping.c | 45 +++++++++++++++++++ components/newlib/CMakeLists.txt | 2 +- components/newlib/port/esp_time_impl.c | 32 ++++++------- components/newlib/time.c | 3 +- 14 files changed, 172 insertions(+), 31 deletions(-) create mode 100644 components/esp_system/system_time.c create mode 100644 components/esp_timer/src/timekeeping.c diff --git a/components/esp_common/include/esp_private/system_internal.h b/components/esp_common/include/esp_private/system_internal.h index 9b383cd60e..bcc1dad20c 100644 --- a/components/esp_common/include/esp_private/system_internal.h +++ b/components/esp_common/include/esp_private/system_internal.h @@ -61,6 +61,16 @@ void esp_reset_reason_set_hint(esp_reset_reason_t hint); */ esp_reset_reason_t esp_reset_reason_get_hint(void); +/** + * @brief Set function which provides `esp_system_get_time`. + * + * @param time_fn function which provides system time + * @param resolution resolution in microseconds of the time provider function specified + */ +void esp_system_set_time_provider(esp_system_time_fn_t time_fn, uint32_t resolution); + + + #ifdef __cplusplus } #endif diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index 542af29d63..0856648800 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "panic.c" "system_api.c" "startup.c" +idf_component_register(SRCS "panic.c" "system_api.c" "startup.c" "system_time.c" INCLUDE_DIRS include PRIV_REQUIRES spi_flash app_update # requirements due to startup code @@ -21,4 +21,4 @@ endif() set_source_files_properties( startup.c PROPERTIES COMPILE_FLAGS - -fno-stack-protector) \ No newline at end of file + -fno-stack-protector) diff --git a/components/esp_system/include/esp_private/startup_internal.h b/components/esp_system/include/esp_private/startup_internal.h index 8a12ee3673..fc91b0fa85 100644 --- a/components/esp_system/include/esp_private/startup_internal.h +++ b/components/esp_system/include/esp_private/startup_internal.h @@ -64,6 +64,10 @@ static __attribute__((used)) esp_system_init_fn_t _SECTION_ATTR_IMPL(".esp_syste static __attribute__((used)) __VA_ARGS__ void __esp_system_init_fn_##f(void) // [refactor-todo] this can be made public API if we allow components to declare init functions, // instead of calling them explicitly +extern uint64_t g_startup_time; // Startup time that serves as the point of origin for system time. Should be set by the entry + // function in the port layer. May be 0 as well if this is not backed by a persistent counter, in which case + // startup time = system time = 0 at the point the entry function sets this variable. + #ifdef __cplusplus } #endif diff --git a/components/esp_system/include/esp_system.h b/components/esp_system/include/esp_system.h index 3e5b777204..aa4833b4dc 100644 --- a/components/esp_system/include/esp_system.h +++ b/components/esp_system/include/esp_system.h @@ -28,6 +28,8 @@ extern "C" { #endif +typedef int64_t (*esp_system_time_fn_t)(void); + typedef enum { ESP_MAC_WIFI_STA, ESP_MAC_WIFI_SOFTAP, @@ -286,6 +288,20 @@ typedef struct { */ void esp_chip_info(esp_chip_info_t* out_info); +/** + * @brief Get the time in microseconds since startup + * + * @returns time since startup in microseconds + */ +int64_t esp_system_get_time(void); + +/** + * @brief Get the resolution of the time returned by `esp_system_get_time`. + * + * @returns the resolution in microseconds + */ +uint32_t esp_system_get_time_resolution(void); + #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX /** * @brief Cache lock bug exists or not diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index e86be35430..94013103aa 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -55,6 +55,8 @@ #include "soc/dport_reg.h" #include "soc/efuse_reg.h" #include "soc/cpu.h" +#include "soc/rtc.h" +#include "soc/spinlock.h" #include "trax.h" @@ -67,6 +69,7 @@ #endif #include "esp_private/startup_internal.h" +#include "esp_private/system_internal.h" extern int _bss_start; extern int _bss_end; @@ -95,16 +98,27 @@ static volatile bool s_cpu_inited[SOC_CPU_CORES_NUM] = { false }; static volatile bool s_resume_cores; #endif +uint64_t g_startup_time = 0; + // If CONFIG_SPIRAM_IGNORE_NOTFOUND is set and external RAM is not found or errors out on testing, this is set to false. bool g_spiram_ok = true; +static int64_t default_system_time_fn(void) +{ + int64_t t = 0; + static spinlock_t s_time_lock = SPINLOCK_INITIALIZER; + spinlock_acquire(&s_time_lock, SPINLOCK_WAIT_FOREVER); + t = (esp_rtc_get_time_us() - g_startup_time); + spinlock_release(&s_time_lock); + return t; +} + #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE void startup_resume_other_cores(void) { s_resume_cores = true; } - void IRAM_ATTR call_start_cpu1(void) { cpu_hal_set_vecbase(&_init_start); @@ -352,6 +366,12 @@ void IRAM_ATTR call_start_cpu0(void) esp_clk_init(); esp_perip_clk_init(); + + // Now that the clocks have been set-up, set the startup time from RTC + // and default RTC-backed system time provider. + g_startup_time = esp_rtc_get_time_us(); + esp_system_set_time_provider(default_system_time_fn, 1000000L / rtc_clk_slow_freq_get_hz()); + intr_matrix_clear(); #ifdef CONFIG_ESP_CONSOLE_UART diff --git a/components/esp_system/startup.c b/components/esp_system/startup.c index 2fbe46804a..5298c6d0d1 100644 --- a/components/esp_system/startup.c +++ b/components/esp_system/startup.c @@ -183,6 +183,7 @@ static void IRAM_ATTR do_core_init(void) fail initializing it properly. */ heap_caps_init(); esp_setup_syscall_table(); + esp_newlib_time_init(); if (g_spiram_ok) { #if CONFIG_SPIRAM_BOOT_INIT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC) @@ -244,9 +245,6 @@ static void IRAM_ATTR do_core_init(void) esp_efuse_disable_basic_rom_console(); #endif - esp_timer_init(); - esp_set_time_from_rtc(); - // [refactor-todo] move this to secondary init #if CONFIG_APPTRACE_ENABLE err = esp_apptrace_init(); @@ -301,6 +299,7 @@ static void IRAM_ATTR do_secondary_init(void) void IRAM_ATTR start_cpu0_default(void) { + ESP_EARLY_LOGI(TAG, "Pro cpu start user code"); // Display information about the current running image. @@ -353,6 +352,8 @@ void IRAM_ATTR start_cpu0_default(void) IRAM_ATTR ESP_SYSTEM_INIT_FN(init_components0, BIT(0)) { + esp_timer_init(); + #if defined(CONFIG_PM_ENABLE) && defined(CONFIG_ESP_CONSOLE_UART) /* When DFS is enabled, use REFTICK as UART clock source */ uart_ll_set_baudrate(UART_LL_GET_HW(CONFIG_ESP_CONSOLE_UART_NUM), UART_SCLK_REF_TICK, CONFIG_ESP_CONSOLE_UART_BAUDRATE); diff --git a/components/esp_system/system_time.c b/components/esp_system/system_time.c new file mode 100644 index 0000000000..a399c5a05c --- /dev/null +++ b/components/esp_system/system_time.c @@ -0,0 +1,41 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_system.h" +#include "esp_attr.h" + +typedef struct { + esp_system_time_fn_t fn; // time provider function + uint32_t resolution; // resolution in microseconds of the time provider +} system_time_provider_t; + +// This is expected to be modified only on startup, so +// it should be safe to not put locks on it. +static system_time_provider_t s_system_time_provider; + +int64_t IRAM_ATTR esp_system_get_time(void) +{ + return (*s_system_time_provider.fn)(); +} + +uint32_t IRAM_ATTR esp_system_get_time_resolution(void) +{ + return s_system_time_provider.resolution; +} + +void esp_system_set_time_provider(esp_system_time_fn_t time_fn, uint32_t resolution) +{ + s_system_time_provider.fn = time_fn; + s_system_time_provider.resolution = resolution; +} \ No newline at end of file diff --git a/components/esp_timer/CMakeLists.txt b/components/esp_timer/CMakeLists.txt index 68be83b9fa..b8890b3674 100644 --- a/components/esp_timer/CMakeLists.txt +++ b/components/esp_timer/CMakeLists.txt @@ -1,7 +1,8 @@ idf_build_get_property(target IDF_TARGET) set(srcs "src/esp_timer.c" - "src/ets_timer_legacy.c") + "src/ets_timer_legacy.c" + "src/timekeeping.c") if(CONFIG_ESP_TIMER_IMPL_FRC2) list(APPEND srcs "src/esp_timer_impl_frc_legacy.c") diff --git a/components/esp_timer/private_include/esp_timer_impl.h b/components/esp_timer/private_include/esp_timer_impl.h index 75e7e1b0d9..c5300f59ed 100644 --- a/components/esp_timer/private_include/esp_timer_impl.h +++ b/components/esp_timer/private_include/esp_timer_impl.h @@ -117,3 +117,8 @@ uint64_t esp_timer_impl_get_counter_reg(void); * @return the value of the alarm register */ uint64_t esp_timer_impl_get_alarm_reg(void); + +/** + * @brief Initialize and provide system timekeeping functions. + */ +esp_err_t esp_timer_timekeeping_impl_init(void); diff --git a/components/esp_timer/src/esp_timer.c b/components/esp_timer/src/esp_timer.c index 2bbb439e5e..a316a6692f 100644 --- a/components/esp_timer/src/esp_timer.c +++ b/components/esp_timer/src/esp_timer.c @@ -97,7 +97,6 @@ static StaticQueue_t s_timer_semaphore_memory; static portMUX_TYPE s_timer_lock = portMUX_INITIALIZER_UNLOCKED; - esp_err_t esp_timer_create(const esp_timer_create_args_t* args, esp_timer_handle_t* out_handle) { @@ -349,7 +348,6 @@ static IRAM_ATTR bool is_initialized(void) return s_timer_task != NULL; } - esp_err_t esp_timer_init(void) { esp_err_t err; @@ -380,6 +378,11 @@ esp_err_t esp_timer_init(void) goto out; } + err = esp_timer_timekeeping_impl_init(); + if (err != ESP_OK) { + goto out; + } + return ESP_OK; out: @@ -505,4 +508,4 @@ int64_t IRAM_ATTR esp_timer_get_next_alarm(void) } timer_list_unlock(); return next_alarm; -} +} \ No newline at end of file diff --git a/components/esp_timer/src/timekeeping.c b/components/esp_timer/src/timekeeping.c new file mode 100644 index 0000000000..767a85b751 --- /dev/null +++ b/components/esp_timer/src/timekeeping.c @@ -0,0 +1,45 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_timer.h" + +#include "esp_private/esp_timer_private.h" +#include "esp_private/system_internal.h" + +#include "sdkconfig.h" + +#if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || \ + defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) || \ + defined( CONFIG_ESP32S2_TIME_SYSCALL_USE_FRC1 ) || \ + defined( CONFIG_ESP32S2_TIME_SYSCALL_USE_RTC_FRC1 ) +#define WITH_FRC 1 +#endif + +#if WITH_FRC +void esp_timer_timekeeping_impl_init(void) +{ + // esp_system_get_time here calls the previous system time provider. + // This should add the time elapsed from g_startup_time up to esp_timer_init, + // therefore keeping it as the point of reference (g_startup_time, that is). + esp_timer_private_advance(esp_system_get_time()); + + // esp_timer provides microsecond-resolution timers to the system + esp_system_set_time_provider(esp_timer_get_time, 1); +} +#else +void esp_timer_timekeeping_impl_init(void) +{ + // Do not override default system time provider +} +#endif \ No newline at end of file diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 8a3463b63d..055e098551 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -21,7 +21,7 @@ list(APPEND ldfragments newlib.lf) idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" PRIV_INCLUDE_DIRS priv_include - PRIV_REQUIRES soc esp_timer + PRIV_REQUIRES soc LDFRAGMENTS "${ldfragments}") # Toolchain libraries require code defined in this component diff --git a/components/newlib/port/esp_time_impl.c b/components/newlib/port/esp_time_impl.c index 08d3f4130f..35281aadc5 100644 --- a/components/newlib/port/esp_time_impl.c +++ b/components/newlib/port/esp_time_impl.c @@ -15,8 +15,6 @@ #include #include -#include "esp_timer.h" - #include "esp_system.h" #include "soc/spinlock.h" @@ -70,24 +68,31 @@ uint64_t esp_time_impl_get_time_since_boot(void) #ifdef WITH_FRC #ifdef WITH_RTC - microseconds = s_microseconds_offset + esp_timer_get_time(); + microseconds = s_microseconds_offset + esp_system_get_time(); #else - microseconds = esp_timer_get_time(); + microseconds = esp_system_get_time(); #endif // WITH_RTC #elif defined(WITH_RTC) + spinlock_acquire(&s_time_lock, SPINLOCK_WAIT_FOREVER); microseconds = esp_rtc_get_time_us(); + spinlock_release(&s_time_lock); #endif // WITH_FRC return microseconds; } uint64_t esp_time_impl_get_time(void) { + uint64_t microseconds = 0; #if defined( WITH_FRC ) - return esp_timer_get_time(); + microseconds = esp_system_get_time(); #elif defined( WITH_RTC ) - return esp_rtc_get_time_us(); + spinlock_acquire(&s_time_lock, SPINLOCK_WAIT_FOREVER); + microseconds = esp_rtc_get_time_us(); + spinlock_release(&s_time_lock); #endif // WITH_FRC + return microseconds; } + #endif // defined( WITH_FRC ) || defined( WITH_RTC ) @@ -175,7 +180,7 @@ void esp_set_time_from_rtc(void) { #if defined( WITH_FRC ) && defined( WITH_RTC ) // initialize time from RTC clock - s_microseconds_offset = esp_rtc_get_time_us() - esp_timer_get_time(); + s_microseconds_offset = esp_rtc_get_time_us() - esp_system_get_time(); #endif // WITH_FRC && WITH_RTC } @@ -185,7 +190,7 @@ void esp_sync_counters_rtc_and_frc(void) struct timeval tv; gettimeofday(&tv, NULL); settimeofday(&tv, NULL); - int64_t s_microseconds_offset_cur = esp_rtc_get_time_us() - esp_timer_get_time(); + int64_t s_microseconds_offset_cur = esp_rtc_get_time_us() - esp_system_get_time(); esp_time_impl_set_boot_time(esp_time_impl_get_boot_time() + ((int64_t)s_microseconds_offset - s_microseconds_offset_cur)); #endif } @@ -193,15 +198,4 @@ void esp_sync_counters_rtc_and_frc(void) void esp_time_impl_init(void) { esp_set_time_from_rtc(); -} - -uint32_t esp_time_impl_get_time_resolution(void) -{ -#if defined( WITH_FRC ) - return 1L; -#elif defined( WITH_RTC ) - uint32_t rtc_freq = rtc_clk_slow_freq_get_hz(); - assert(rtc_freq != 0); - return 1000000L / rtc_freq; -#endif // WITH_FRC } \ No newline at end of file diff --git a/components/newlib/time.c b/components/newlib/time.c index 91f6bfc6c8..909963277f 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -22,6 +22,7 @@ #include #include +#include "esp_system.h" #include "esp_attr.h" #include "freertos/FreeRTOS.h" @@ -285,7 +286,7 @@ int clock_getres (clockid_t clock_id, struct timespec *res) } res->tv_sec = 0; - res->tv_nsec = esp_time_impl_get_time_resolution() * 1000; + res->tv_nsec = esp_system_get_time_resolution() * 1000; return 0; #else