diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index 0a5d51f66..ace292b8f 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -266,15 +266,10 @@ void IRAM_ATTR esp_restart(void) */ void IRAM_ATTR esp_restart_noos() { - const uint32_t core_id = xPortGetCoreID(); - const uint32_t other_core_id = core_id == 0 ? 1 : 0; - esp_cpu_stall(other_core_id); + // Disable interrupts + xt_ints_off(0xFFFFFFFF); - // other core is now stalled, can access DPORT registers directly - esp_dport_access_int_pause(); - - // We need to disable TG0/TG1 watchdogs - // First enable RTC watchdog for 1 second + // Enable RTC watchdog for 1 second REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); REG_WRITE(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M | @@ -284,6 +279,18 @@ void IRAM_ATTR esp_restart_noos() (1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) ); REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1); + // Reset and stall the other CPU. + // CPU must be reset before stalling, in case it was running a s32c1i + // instruction. This would cause memory pool to be locked by arbiter + // to the stalled CPU, preventing current CPU from accessing this pool. + const uint32_t core_id = xPortGetCoreID(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); + + // Other core is now stalled, can access DPORT registers directly + esp_dport_access_int_abort(); + // Disable TG0/TG1 watchdogs TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; @@ -292,8 +299,10 @@ void IRAM_ATTR esp_restart_noos() TIMERG1.wdt_config0.en = 0; TIMERG1.wdt_wprotect=0; - // Disable all interrupts - xt_ints_off(0xFFFFFFFF); + // Flush any data left in UART FIFOs + uart_tx_wait_idle(0); + uart_tx_wait_idle(1); + uart_tx_wait_idle(2); // Disable cache Cache_Read_Disable(0); @@ -310,11 +319,6 @@ void IRAM_ATTR esp_restart_noos() WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30); #endif - // Flush any data left in UART FIFOs - uart_tx_wait_idle(0); - uart_tx_wait_idle(1); - uart_tx_wait_idle(2); - // Reset wifi/bluetooth/ethernet/sdio (bb/mac) DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | @@ -337,14 +341,14 @@ void IRAM_ATTR esp_restart_noos() // Reset CPUs if (core_id == 0) { // Running on PRO CPU: APP CPU is stalled. Can reset both CPUs. - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_SW_PROCPU_RST_M | RTC_CNTL_SW_APPCPU_RST_M); + esp_cpu_reset(1); + esp_cpu_reset(0); } else { // Running on APP CPU: need to reset PRO CPU and unstall it, // then reset APP CPU - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_PROCPU_RST_M); + esp_cpu_reset(0); esp_cpu_unstall(0); - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_APPCPU_RST_M); + esp_cpu_reset(1); } while(true) { ; diff --git a/components/soc/esp32/cpu_util.c b/components/soc/esp32/cpu_util.c index ecfcab4ba..bc052af98 100644 --- a/components/soc/esp32/cpu_util.c +++ b/components/soc/esp32/cpu_util.c @@ -44,6 +44,12 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id) } } +void IRAM_ATTR esp_cpu_reset(int cpu_id) +{ + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + cpu_id == 0 ? RTC_CNTL_SW_PROCPU_RST_M : RTC_CNTL_SW_APPCPU_RST_M); +} + bool IRAM_ATTR esp_cpu_in_ocd_debug_mode() { #if CONFIG_ESP32_DEBUG_OCDAWARE diff --git a/components/soc/esp32/include/soc/cpu.h b/components/soc/esp32/include/soc/cpu.h index b56fb3dc8..05ec91776 100644 --- a/components/soc/esp32/include/soc/cpu.h +++ b/components/soc/esp32/include/soc/cpu.h @@ -85,6 +85,13 @@ void esp_cpu_stall(int cpu_id); */ void esp_cpu_unstall(int cpu_id); +/** + * @brief Reset CPU using RTC controller + * @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP) + */ +void esp_cpu_reset(int cpu_id); + + /** * @brief Returns true if a JTAG debugger is attached to CPU * OCD (on chip debug) port. diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index e600e39e3..1b64d0073 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -400,6 +400,15 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period); */ uint64_t rtc_time_get(); +/** + * @brief Busy loop until next RTC_SLOW_CLK cycle + * + * This function returns not earlier than the next RTC_SLOW_CLK clock cycle. + * In some cases (e.g. when RTC_SLOW_CLK cycle is very close), it may return + * one RTC_SLOW_CLK cycle later. + */ +void rtc_clk_wait_for_slow_cycle(); + /** * @brief sleep configuration for rtc_sleep_init function */ diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index 6e2b0909d..612476cf8 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -72,9 +72,6 @@ static const char* TAG = "rtc_clk"; * All values are in microseconds. * TODO: some of these are excessive, and should be reduced. */ -#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 20 -#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160 -#define DELAY_CPU_FREQ_SWITCH_TO_PLL 20 #define DELAY_PLL_DBIAS_RAISE 3 #define DELAY_PLL_ENABLE_WITH_150K 80 #define DELAY_PLL_ENABLE_WITH_32K 160 @@ -397,9 +394,12 @@ 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); - 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); + + /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch + * is complete before disabling the PLL. + */ + rtc_clk_wait_for_slow_cycle(); + DPORT_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 | @@ -443,7 +443,7 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) s_pll_freq = 480; } REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); - ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_PLL); + rtc_clk_wait_for_slow_cycle(); rtc_clk_apb_freq_update(80 * MHZ); } s_cur_freq = cpu_freq; @@ -558,6 +558,13 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq) static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() { + /* Enable 8M/256 clock if needed */ + const bool clk_8m_enabled = rtc_clk_8m_enabled(); + const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); + if (!clk_8md256_enabled) { + rtc_clk_8m_enable(true, true); + } + uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); /* cal_val contains period of 8M/256 clock in XTAL clock cycles * (shifted by RTC_CLK_CAL_FRACT bits). @@ -581,6 +588,8 @@ static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); return RTC_XTAL_FREQ_AUTO; } + /* Restore 8M and 8md256 clocks to original state */ + rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); } void rtc_clk_apb_freq_update(uint32_t apb_freq) @@ -634,7 +643,6 @@ void rtc_clk_init(rtc_clk_config_t cfg) CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); /* Estimate XTAL frequency */ - rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; if (xtal_freq == RTC_XTAL_FREQ_AUTO) { if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { @@ -642,7 +650,7 @@ void rtc_clk_init(rtc_clk_config_t cfg) xtal_freq = rtc_clk_xtal_freq_get(); } else { /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ - xtal_freq = est_xtal_freq; + xtal_freq = rtc_clk_xtal_freq_estimate(); if (xtal_freq == RTC_XTAL_FREQ_AUTO) { SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); xtal_freq = RTC_XTAL_FREQ_26M; @@ -653,8 +661,11 @@ void rtc_clk_init(rtc_clk_config_t cfg) * frequency is different. If autodetection failed, worst case we get a * bit of garbage output. */ - SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", - xtal_freq, est_xtal_freq); + rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); + if (est_xtal_freq != xtal_freq) { + SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", + xtal_freq, est_xtal_freq); + } } uart_tx_wait_idle(0); rtc_clk_xtal_freq_update(xtal_freq); diff --git a/components/soc/esp32/rtc_time.c b/components/soc/esp32/rtc_time.c index 07a9337a3..6f354f884 100644 --- a/components/soc/esp32/rtc_time.c +++ b/components/soc/esp32/rtc_time.c @@ -135,3 +135,20 @@ uint64_t rtc_time_get() t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32; return t; } + +void rtc_clk_wait_for_slow_cycle() +{ + REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING | TIMG_RTC_CALI_START); + REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY); + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, RTC_CAL_RTC_MUX); + /* Request to run calibration for 0 slow clock cycles. + * RDY bit will be set on the nearest slow clock cycle. + */ + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, 0); + REG_SET_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + ets_delay_us(1); /* RDY needs some time to go low */ + while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)) { + ets_delay_us(1); + } +} + diff --git a/components/soc/esp32/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index 48905acd0..00fb0040f 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -1,15 +1,17 @@ #include #include "unity.h" #include "rom/ets_sys.h" +#include "rom/uart.h" #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" #include "soc/rtc_io_reg.h" #include "soc/sens_reg.h" #include "soc/io_mux_reg.h" #include "driver/rtc_io.h" - +#include "test_utils.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/semphr.h" #define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk) @@ -89,3 +91,35 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]") SET_PERI_REG_MASK(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_12M_NO_GATING); pull_out_clk(RTC_IO_DEBUG_SEL0_8M); } + +static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) +{ + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + + const int test_duration_sec = 10; + ref_clock_init(); + uint64_t t_start = ref_clock_get(); + + rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get(); + int count = 0; + while (ref_clock_get() - t_start < test_duration_sec * 1000000) { + switch_func(RTC_CPU_FREQ_XTAL); + switch_func(cur_freq); + ++count; + } + uint64_t t_end = ref_clock_get(); + printf("Switch count: %d. Average time to switch PLL -> XTAL -> PLL: %d us\n", count, (int) ((t_end - t_start) / count)); + ref_clock_deinit(); +} + +TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]") +{ + test_clock_switching(rtc_clk_cpu_freq_set); +} + +TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]") +{ + test_clock_switching(rtc_clk_cpu_freq_set_fast); +} + +