diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 9a439e58fe..16c4515a18 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -60,8 +60,6 @@ We arrive here after the bootloader finished loading the program from flash. The flash cache is down and the app CPU is in reset. We do have a stack, so we can do the initialization in C. */ -// TODO: make a nice header file for ROM functions instead of adding externs all over the place -extern void Cache_Flush(int); void bootloader_main(); static void unpack_load_app(const esp_partition_pos_t *app_node); @@ -74,6 +72,7 @@ static void set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_size, uint32_t entry_addr); static void update_flash_config(const esp_image_header_t* pfhdr); +static void clock_configure(void); static void uart_console_configure(void); static void wdt_reset_check(void); @@ -238,15 +237,7 @@ static bool ota_select_valid(const esp_ota_select_entry_t *s) void bootloader_main() { - /* Set CPU to 80MHz. Keep other clocks unmodified. */ - uart_tx_wait_idle(0); - rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT(); - clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ; - clk_cfg.cpu_freq = RTC_CPU_FREQ_80M; - clk_cfg.slow_freq = rtc_clk_slow_freq_get(); - clk_cfg.fast_freq = rtc_clk_fast_freq_get(); - rtc_clk_init(clk_cfg); - + clock_configure(); uart_console_configure(); wdt_reset_check(); ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); @@ -704,6 +695,29 @@ void print_flash_info(const esp_image_header_t* phdr) #endif } + +static void clock_configure(void) +{ + /* Set CPU to 80MHz. Keep other clocks unmodified. */ + uart_tx_wait_idle(0); + rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT(); + clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ; + clk_cfg.cpu_freq = RTC_CPU_FREQ_80M; + clk_cfg.slow_freq = rtc_clk_slow_freq_get(); + clk_cfg.fast_freq = rtc_clk_fast_freq_get(); + rtc_clk_init(clk_cfg); + /* As a slight optimization, if 32k XTAL was enabled in sdkconfig, we enable + * it here. Usually it needs some time to start up, so we amortize at least + * part of the start up time by enabling 32k XTAL early. + * App startup code will wait until the oscillator has started up. + */ +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + if (!rtc_clk_32k_enabled()) { + rtc_clk_32k_bootstrap(); + } +#endif +} + static void uart_console_configure(void) { #if CONFIG_CONSOLE_UART_NONE diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 6a73463c58..f9b9151597 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -542,6 +542,23 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL bool "External 32kHz crystal" endchoice +config ESP32_RTC_CLK_CAL_CYCLES + int "Number of cycles for RTC_SLOW_CLK calibration" + default 1024 + range 0 125000 + help + When the startup code initializes RTC_SLOW_CLK, it can perform + calibration by comparing the RTC_SLOW_CLK frequency with main XTAL + frequency. This option sets the number of RTC_SLOW_CLK cycles measured + by the calibration routine. Higher numbers increase calibration + precision, which may be important for applications which spend a lot of + time in deep sleep. Lower numbers reduce startup time. + + When this option is set to 0, clock calibration will not be performed at + startup, and approximate clock frequencies will be assumed: + - 150000 Hz if internal RC oscillator is used as clock source + - 32768 Hz if the 32k crystal oscillator is used + config ESP32_DEEP_SLEEP_WAKEUP_DELAY int "Extra delay in deep sleep wake stub (in us)" default 0 diff --git a/components/esp32/clk.c b/components/esp32/clk.c new file mode 100644 index 0000000000..449ea39321 --- /dev/null +++ b/components/esp32/clk.c @@ -0,0 +1,131 @@ +// Copyright 2015-2017 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 +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "rom/ets_sys.h" +#include "rom/uart.h" +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" + +/* Number of cycles to wait from the 32k XTAL oscillator to consider it running. + * Larger values increase startup delay. Smaller values may cause false positive + * detection (i.e. oscillator runs for a few cycles and then stops). + */ +#define XTAL_32K_DETECT_CYCLES 32 +#define SLOW_CLK_CAL_CYCLES CONFIG_ESP32_RTC_CLK_CAL_CYCLES + +static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk); + +static const char* TAG = "clk"; +/* + * This function is not exposed as an API at this point, + * because FreeRTOS doesn't yet support dynamic changing of + * CPU frequency. Also we need to implement hooks for + * components which want to be notified of CPU frequency + * changes. + */ +void esp_clk_init(void) +{ + rtc_config_t cfg = RTC_CONFIG_DEFAULT(); + rtc_init(cfg); + +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +#else + select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); +#endif + + uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M; + switch(freq_mhz) { + case 240: + freq = RTC_CPU_FREQ_240M; + break; + case 160: + freq = RTC_CPU_FREQ_160M; + break; + default: + freq_mhz = 80; + /* no break */ + case 80: + freq = RTC_CPU_FREQ_80M; + break; + } + + // Wait for UART TX to finish, otherwise some UART output will be lost + // when switching APB frequency + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + rtc_clk_cpu_freq_set(freq); +} + +void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) +{ + extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU + extern uint32_t g_ticks_per_us_app; // same defined for APP CPU + g_ticks_per_us_pro = ticks_per_us; + g_ticks_per_us_app = ticks_per_us; +} + +/* This is a cached value of RTC slow clock period; it is updated by + * the select_rtc_slow_clk function at start up. This cached value is used in + * other places, like time syscalls and deep sleep. + */ +static uint32_t s_rtc_slow_clk_cal = 0; + +static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) +{ + if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { + /* 32k XTAL oscillator needs to be enabled and running before it can + * be used. Hardware doesn't have a direct way of checking if the + * oscillator is running. Here we use rtc_clk_cal function to count + * the number of main XTAL cycles in the given number of 32k XTAL + * oscillator cycles. If the 32k XTAL has not started up, calibration + * will time out, returning 0. + */ + rtc_clk_32k_enable(true); + uint32_t cal_val = 0; + uint32_t wait = 0; + // increment of 'wait' counter equivalent to 3 seconds + const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * XTAL_32K_DETECT_CYCLES); + ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up") + do { + ++wait; + cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, XTAL_32K_DETECT_CYCLES); + if (wait % warning_timeout == 0) { + ESP_EARLY_LOGW(TAG, "still waiting for 32k oscillator to start up"); + } + } while (cal_val == 0); + ESP_EARLY_LOGD(TAG, "32k oscillator ready, wait=%d", wait); + } + rtc_clk_slow_freq_set(slow_clk); + if (SLOW_CLK_CAL_CYCLES > 0) { + /* TODO: 32k XTAL oscillator has some frequency drift at startup. + * Improve calibration routine to wait until the frequency is stable. + */ + s_rtc_slow_clk_cal = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES); + } else { + const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; + s_rtc_slow_clk_cal = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz()); + } + ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", s_rtc_slow_clk_cal); +} + +uint32_t esp_clk_slowclk_cal_get() +{ + return s_rtc_slow_clk_cal; +} diff --git a/components/esp32/cpu_freq.c b/components/esp32/cpu_freq.c deleted file mode 100644 index 4efa825b55..0000000000 --- a/components/esp32/cpu_freq.c +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015-2016 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 -#include "esp_attr.h" -#include "rom/ets_sys.h" -#include "rom/uart.h" -#include "sdkconfig.h" -#include "soc/soc.h" -#include "soc/rtc.h" -#include "soc/rtc_cntl_reg.h" - -/* - * This function is not exposed as an API at this point, - * because FreeRTOS doesn't yet support dynamic changing of - * CPU frequency. Also we need to implement hooks for - * components which want to be notified of CPU frequency - * changes. - */ -void esp_set_cpu_freq(void) -{ - uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; - rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M; - switch(freq_mhz) { - case 240: - freq = RTC_CPU_FREQ_240M; - break; - case 160: - freq = RTC_CPU_FREQ_160M; - break; - default: - freq_mhz = 80; - /* no break */ - case 80: - freq = RTC_CPU_FREQ_80M; - break; - } - - // Wait for UART TX to finish, otherwise some UART output will be lost - // when switching APB frequency - uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); - rtc_config_t cfg = RTC_CONFIG_DEFAULT(); - rtc_init(cfg); - rtc_clk_cpu_freq_set(freq); -#if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL - rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL); -#endif -} - -void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) -{ - extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU - extern uint32_t g_ticks_per_us_app; // same defined for APP CPU - g_ticks_per_us_pro = ticks_per_us; - g_ticks_per_us_app = ticks_per_us; -} diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 7211b8b479..8ff0a7c8db 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -1,9 +1,9 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2017 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 @@ -11,6 +11,7 @@ // 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 #include @@ -60,6 +61,7 @@ #include "esp_panic.h" #include "esp_core_dump.h" #include "esp_app_trace.h" +#include "esp_clk.h" #include "trax.h" #define STRINGIFY(s) STRINGIFY2(s) @@ -202,7 +204,7 @@ void start_cpu0_default(void) #endif trax_start_trace(TRAX_DOWNCOUNT_WORDS); #endif - esp_set_cpu_freq(); // set CPU frequency configured in menuconfig + esp_clk_init(); #ifndef CONFIG_CONSOLE_UART_NONE uart_div_modify(CONFIG_CONSOLE_UART_NUM, (rtc_clk_apb_freq_get() << 4) / CONFIG_CONSOLE_UART_BAUDRATE); #endif diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 23d3f4b1fc..6f7e3b24c7 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -17,6 +17,7 @@ #include "esp_attr.h" #include "esp_deep_sleep.h" #include "esp_log.h" +#include "esp_clk.h" #include "rom/cache.h" #include "rom/rtc.h" #include "rom/uart.h" @@ -183,13 +184,7 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us) static void timer_wakeup_prepare() { - // Do calibration if not using 32k XTAL - uint32_t period; - if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) { - period = rtc_clk_cal(RTC_CAL_RTC_MUX, 128); - } else { - period = (uint32_t) ((1000000ULL /* us*Hz */ << RTC_CLK_CAL_FRACT) / 32768 /* Hz */); - } + uint32_t period = esp_clk_slowclk_cal_get(); uint64_t rtc_count_delta = rtc_time_us_to_slowclk(s_config.sleep_duration, period); uint64_t cur_rtc_count = rtc_time_get(); rtc_sleep_set_wakeup_time(cur_rtc_count + rtc_count_delta); diff --git a/components/esp32/include/esp_clk.h b/components/esp32/include/esp_clk.h new file mode 100644 index 0000000000..0cedf0373e --- /dev/null +++ b/components/esp32/include/esp_clk.h @@ -0,0 +1,44 @@ +// Copyright 2015-2017 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. + +#pragma once + +/** + * @file esp_clk.h + * + * This file contains declarations of clock related functions. + * These functions are used in ESP-IDF components, but should not be considered + * to be part of public API. + */ + +/** + * @brief Initialize clock-related settings + * + * Called from cpu_start.c, not intended to be called from other places. + * This function configures the CPU clock, RTC slow and fast clocks, and + * performs RTC slow clock calibration. + */ +void esp_clk_init(void); + + +/** + * @brief Get the cached calibration value of RTC slow clock + * + * The value is in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * + * @return the calibration value obtained using rtc_clk_cal, at startup time + */ +uint32_t esp_clk_slowclk_cal_get(); + diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 8ec362f9cd..5cedfcabb3 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -317,7 +317,7 @@ static void esp_panic_wdt_start() REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_SYSTEM); // 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data. // @ 115200 UART speed it will take more than 6 sec to print them out. - WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, RTC_CNTL_SLOWCLK_FREQ*7); + WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 7); REG_SET_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN); WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0); } diff --git a/components/newlib/time.c b/components/newlib/time.c index fc7dec6450..d763f83904 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2017 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. @@ -24,7 +24,9 @@ #include #include "esp_attr.h" #include "esp_intr_alloc.h" +#include "esp_clk.h" #include "soc/soc.h" +#include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" #include "soc/frc_timer_reg.h" #include "rom/ets_sys.h" @@ -44,15 +46,8 @@ #ifdef WITH_RTC static uint64_t get_rtc_time_us() { - SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M); - while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) { - ; - } - CLEAR_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M); - uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG); - uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG); - uint64_t ticks = (high << 32) | low; - return ticks * 100 / (RTC_CNTL_SLOWCLK_FREQ / 10000); // scale RTC_CNTL_SLOWCLK_FREQ to avoid overflow + uint64_t ticks = rtc_time_get(); + return (uint32_t) ((ticks * esp_clk_slowclk_cal_get()) >> RTC_CLK_CAL_FRACT); } #endif // WITH_RTC diff --git a/components/soc/esp32/include/soc/cpu.h b/components/soc/esp32/include/soc/cpu.h index 7e1301345b..b56fb3dc84 100644 --- a/components/soc/esp32/include/soc/cpu.h +++ b/components/soc/esp32/include/soc/cpu.h @@ -73,15 +73,6 @@ static inline void cpu_configure_region_protection() cpu_write_itlb(0x20000000, 0); } -/** - * @brief Set CPU frequency to the value defined in menuconfig - * - * Called from cpu_start.c, not intended to be called from other places. - * This is a temporary function which will be replaced once dynamic - * CPU frequency changing is implemented. - */ -void esp_set_cpu_freq(void); - /** * @brief Stall CPU using RTC controller * @param cpu_id ID of the CPU to stall (0 = PRO, 1 = APP) diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index a0813294aa..a85265fdc1 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -168,6 +168,15 @@ void rtc_clk_32k_enable(bool en); */ bool rtc_clk_32k_enabled(); +/** + * @brief Enable 32k oscillator, configuring it for fast startup time. + * Note: to achieve higher frequency stability, rtc_clk_32k_enable function + * must be called one the 32k XTAL oscillator has started up. This function + * will initially disable the 32k XTAL oscillator, so it should not be called + * when the system is using 32k XTAL as RTC_SLOW_CLK. + */ +void rtc_clk_32k_bootstrap(); + /** * @brief Enable or disable 8 MHz internal oscillator * @@ -229,6 +238,20 @@ void rtc_clk_slow_freq_set(rtc_slow_freq_t slow_freq); */ rtc_slow_freq_t rtc_clk_slow_freq_get(); +/** + * @brief Get the approximate frequency of RTC_SLOW_CLK, in Hz + * + * - if RTC_SLOW_FREQ_RTC is selected, returns ~150000 + * - if RTC_SLOW_FREQ_32K_XTAL is selected, returns 32768 + * - if RTC_SLOW_FREQ_8MD256 is selected, returns ~33000 + * + * rtc_clk_cal function can be used to get more precise value by comparing + * RTC_SLOW_CLK frequency to the frequency of main XTAL. + * + * @return RTC_SLOW_CLK frequency, in Hz + */ +uint32_t rtc_clk_slow_freq_get_hz(); + /** * @brief Select source for RTC_FAST_CLK * @param fast_freq clock source (one of rtc_fast_freq_t values) diff --git a/components/soc/esp32/include/soc/rtc_cntl_reg.h b/components/soc/esp32/include/soc/rtc_cntl_reg.h index 067f2296fe..efb53ec147 100644 --- a/components/soc/esp32/include/soc/rtc_cntl_reg.h +++ b/components/soc/esp32/include/soc/rtc_cntl_reg.h @@ -239,9 +239,6 @@ #define RTC_CNTL_TIME_VALID_V 0x1 #define RTC_CNTL_TIME_VALID_S 30 -/* frequency of RTC slow clock, Hz */ -#define RTC_CNTL_SLOWCLK_FREQ 150000 - #define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10) /* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: RTC timer low 32 bits*/ diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index 1397705c21..676b9f33ce 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -32,6 +32,12 @@ #define MHZ (1000000) +/* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */ +#define RTC_FAST_CLK_FREQ_8M 8500000 +#define RTC_SLOW_CLK_FREQ_150K 150000 +#define RTC_SLOW_CLK_FREQ_8MD256 (RTC_FAST_CLK_FREQ_8M / 256) +#define RTC_SLOW_CLK_FREQ_32K 32768 + static const char* TAG = "rtc_clk"; /* Various constants related to the analog internals of the chip. @@ -55,14 +61,21 @@ static const char* TAG = "rtc_clk"; #define XTAL_32K_DRES_VAL 3 #define XTAL_32K_DBIAS_VAL 0 +#define XTAL_32K_BOOTSTRAP_DAC_VAL 3 +#define XTAL_32K_BOOTSTRAP_DRES_VAL 3 +#define XTAL_32K_BOOTSTRAP_DBIAS_VAL 0 +#define XTAL_32K_BOOTSTRAP_TIME_US 7 + /* Delays for various clock sources to be enabled/switched. * All values are in microseconds. * TODO: some of these are excessive, and should be reduced. */ -#define DELAY_CPU_FREQ_SWITCH_TO_XTAL 80 +#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 80 +#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160 #define DELAY_CPU_FREQ_SWITCH_TO_PLL 10 #define DELAY_PLL_DBIAS_RAISE 3 -#define DELAY_PLL_ENABLE 80 +#define DELAY_PLL_ENABLE_WITH_150K 80 +#define DELAY_PLL_ENABLE_WITH_32K 160 #define DELAY_FAST_CLK_SWITCH 3 #define DELAY_SLOW_CLK_SWITCH 300 #define DELAY_8M_ENABLE 50 @@ -73,22 +86,36 @@ static const char* TAG = "rtc_clk"; #define XTAL_FREQ_EST_CYCLES 10 +static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) +{ + SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); + CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, + RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE | + RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, dac); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, dres); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, dbias); + SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); +} + void rtc_clk_32k_enable(bool enable) { if (enable) { - SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); - CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, - RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE | - RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); - REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, XTAL_32K_DAC_VAL); - REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, XTAL_32K_DRES_VAL); - REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, XTAL_32K_DBIAS_VAL); - SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); + rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL); } else { CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); } } +void rtc_clk_32k_bootstrap() +{ + CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); + SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE); + ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US); + rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL, + XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL); +} + bool rtc_clk_32k_enabled() { return GET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K) != 0; @@ -172,6 +199,15 @@ rtc_slow_freq_t rtc_clk_slow_freq_get() return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); } +uint32_t rtc_clk_slow_freq_get_hz() +{ + switch(rtc_clk_slow_freq_get()) { + case RTC_SLOW_FREQ_RTC: return RTC_SLOW_CLK_FREQ_150K; + case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_32K; + case RTC_SLOW_FREQ_8MD256: return RTC_SLOW_CLK_FREQ_8MD256; + } + return 0; +} void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq) { @@ -280,7 +316,9 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_LREF, i2c_bbpll_lref); I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); - ets_delay_us(DELAY_PLL_ENABLE); + uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ? + DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K; + ets_delay_us(delay_pll_en); } void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) @@ -291,7 +329,9 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); ets_update_cpu_frequency(xtal_freq); - ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_XTAL); + uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ? + DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K; + ets_delay_us(delay_xtal_switch); REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | @@ -518,7 +558,7 @@ void rtc_clk_init(rtc_clk_config_t cfg) /* Slow & fast clocks setup */ if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { - rtc_clk_32k_enable(false); + rtc_clk_32k_enable(true); } if (cfg.fast_freq == RTC_FAST_FREQ_8M) { bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256; diff --git a/components/soc/esp32/rtc_time.c b/components/soc/esp32/rtc_time.c index 9e974cc940..07a9337a30 100644 --- a/components/soc/esp32/rtc_time.c +++ b/components/soc/esp32/rtc_time.c @@ -31,7 +31,14 @@ * once, and TIMG_RTC_CALI_RDY bit is set when counting is done. One-off mode is * enabled using TIMG_RTC_CALI_START bit. */ -uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) + +/** + * @brief Clock calibration function used by rtc_clk_cal and rtc_clk_cal_ratio + * @param cal_clk which clock to calibrate + * @param slowclk_cycles number of slow clock cycles to count + * @return number of XTAL clock cycles within the given number of slow clock cycles + */ +static uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) { /* Enable requested clock (150k clock is always on) */ if (cal_clk == RTC_CAL_32K_XTAL) { @@ -56,7 +63,7 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) } else { expected_freq = 150000; /* 150k internal oscillator */ } - uint32_t us_time_estimate = slowclk_cycles * MHZ / expected_freq; + uint32_t us_time_estimate = (uint32_t) (((uint64_t) slowclk_cycles) * MHZ / expected_freq); /* Start calibration */ CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); SET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); @@ -83,8 +90,13 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) return 0; } - uint64_t xtal_cycles = REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE); - uint64_t ratio_64 = (xtal_cycles << RTC_CLK_CAL_FRACT) / slowclk_cycles; + return REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE); +} + +uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles); + uint64_t ratio_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT)) / slowclk_cycles; uint32_t ratio = (uint32_t)(ratio_64 & UINT32_MAX); return ratio; } @@ -92,8 +104,10 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) { rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - uint32_t ratio = rtc_clk_cal_ratio(cal_clk, slowclk_cycles); - uint32_t period = ratio / xtal_freq; + uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles); + uint64_t divider = ((uint64_t)xtal_freq) * slowclk_cycles; + uint64_t period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider; + uint32_t period = (uint32_t)(period_64 & UINT32_MAX); return period; } diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index b653c48a81..97932d89c3 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -27,6 +27,7 @@ #include "esp32/ulp.h" #include "soc/soc.h" +#include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" #include "soc/sens_reg.h" #include "driver/rtc_io.h" @@ -321,15 +322,11 @@ TEST_CASE("ulp timer setting", "[ulp]") uint32_t counter = RTC_SLOW_MEM[offset] & 0xffff; CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); // compare the actual and expected numbers of iterations of ULP program - float expected_period = (cycles_to_test[i] + 16) / (float) RTC_CNTL_SLOWCLK_FREQ + 5 / 8e6f; + float expected_period = (cycles_to_test[i] + 16) / (float) rtc_clk_slow_freq_get_hz() + 5 / 8e6f; float error = 1.0f - counter * expected_period; printf("%u\t%u\t%.01f\t%.04f\n", cycles_to_test[i], counter, 1.0f / expected_period, error); // Should be within 15% TEST_ASSERT_INT_WITHIN(15, 0, (int) error * 100); - // Note: currently RTC_CNTL_SLOWCLK_FREQ is ballpark value — we need to determine it - // Precisely by running calibration similar to the one done in deep sleep. - // This may cause the test to fail on some chips which have the slow clock frequency - // way off. } } diff --git a/examples/system/deep_sleep/main/deep_sleep_example_main.c b/examples/system/deep_sleep/main/deep_sleep_example_main.c index 959fdfbad2..0d0a2fb3ec 100644 --- a/examples/system/deep_sleep/main/deep_sleep_example_main.c +++ b/examples/system/deep_sleep/main/deep_sleep_example_main.c @@ -22,6 +22,7 @@ #include "driver/rtc_io.h" #include "soc/rtc_cntl_reg.h" #include "soc/sens_reg.h" +#include "soc/rtc.h" static RTC_DATA_ATTR struct timeval sleep_enter_time; @@ -286,7 +287,7 @@ static void start_ulp_temperature_monitoring() assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size"); // Set ULP wakeup period - const uint32_t sleep_cycles = RTC_CNTL_SLOWCLK_FREQ / measurements_per_sec; + const uint32_t sleep_cycles = rtc_clk_slow_freq_get_hz() / measurements_per_sec; REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles); // Start ULP