diff --git a/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c b/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c index 9a32c922f8a..f36fcd6d7d2 100644 --- a/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c +++ b/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c @@ -22,6 +22,7 @@ #include "hal/cache_hal.h" #endif #include "esp_private/cache_utils.h" +#include "esp_cpu.h" #include "esp_private/mspi_timing_tuning.h" #include "esp_private/mspi_timing_config.h" #include "esp_private/mspi_timing_by_mspi_delay.h" @@ -633,8 +634,21 @@ static void restore_cache(uint32_t cpuid, uint32_t saved_state) } #else // ESP_TEE_BUILD -#define disable_cache(cpuid, saved_state) spi_flash_disable_cache(cpuid, saved_state) -#define restore_cache(cpuid, saved_state) spi_flash_restore_cache(cpuid, saved_state) +static void disable_cache(uint32_t cpuid, uint32_t *saved_state) +{ +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_disable(); +#endif + spi_flash_disable_cache(cpuid, saved_state); +} + +static void restore_cache(uint32_t cpuid, uint32_t saved_state) +{ + spi_flash_restore_cache(cpuid, saved_state); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif +} #endif diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 074e9fd689b..6949ee4fedc 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -11,6 +11,7 @@ #include #include "esp_attr.h" +#include "esp_cpu.h" #include "esp_rom_caps.h" #include "esp_macros.h" #include "esp_memory_utils.h" @@ -573,6 +574,9 @@ static void FORCE_IRAM_ATTR suspend_cache(void) { // If the access to external memory hits in the cache, it will not trigger a cache error. So in order to // fully check the access to external memory, writeback & invalidate is needed here. Cache_WriteBack_Invalidate_All(CACHE_MAP_MASK); +#endif +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_disable(); #endif spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state); } @@ -584,6 +588,9 @@ static void FORCE_IRAM_ATTR resume_cache(void) { assert(s_cache_suspend_cnt >= 0 && DRAM_STR("cache resume doesn't match suspend ops")); if (s_cache_suspend_cnt == 0) { spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif } } diff --git a/components/esp_rom/patches/esp_rom_spiflash.c b/components/esp_rom/patches/esp_rom_spiflash.c index ed06a12e29d..ea1df71a6e1 100644 --- a/components/esp_rom/patches/esp_rom_spiflash.c +++ b/components/esp_rom/patches/esp_rom_spiflash.c @@ -13,9 +13,6 @@ #include "soc/spi_mem_reg.h" #endif -#if SOC_BRANCH_PREDICTOR_SUPPORTED -#include "riscv/rv_utils.h" -#endif #include "esp_rom_spiflash.h" #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/spi_flash.h" @@ -817,10 +814,6 @@ void esp_rom_opiflash_cache_mode_config(esp_rom_spiflash_read_mode_t mode, const extern void rom_spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state); void spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state) { -#if SOC_BRANCH_PREDICTOR_SUPPORTED - //branch predictor will start cache request as well - rv_utils_dis_branch_predictor(); -#endif rom_spi_flash_disable_cache(cpuid, saved_state); } @@ -828,8 +821,5 @@ extern void rom_spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state); void spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state) { rom_spi_flash_restore_cache(cpuid, saved_state); -#if SOC_BRANCH_PREDICTOR_SUPPORTED - rv_utils_en_branch_predictor(); -#endif } #endif diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index e02f7ac0ec4..23518fabd2a 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -30,6 +30,7 @@ #include "esp_private/esp_ipc.h" #endif #include "esp_attr.h" +#include "esp_cpu.h" #include "esp_memory_utils.h" #include "esp_intr_alloc.h" #include "esp_private/esp_cache_private.h" @@ -104,7 +105,7 @@ void IRAM_ATTR spi_flash_op_block_func(void *arg) /* The branch predictor issues speculative cache requests while this core * spins in IRAM. The flash-op core is about to suspend the (shared) cache, * so speculative fetches into flash would raise a cache access-fail on - * this core. spi_flash_restore_cache() below re-enables prediction. */ + * this core. */ esp_cpu_branch_prediction_disable(); #endif // s_flash_op_complete flag is cleared on *this* CPU, otherwise the other @@ -117,6 +118,9 @@ void IRAM_ATTR spi_flash_op_block_func(void *arg) } // Flash operation is complete, re-enable cache spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif // Restore interrupts that aren't located in IRAM esp_intr_noniram_enable(); #if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) ) @@ -187,6 +191,9 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void) // Kill interrupts that aren't located in IRAM esp_intr_noniram_disable(); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_disable(); +#endif // This CPU executes this routine, with non-IRAM interrupts and the scheduler // disabled. The other CPU is spinning in the spi_flash_op_block_func task, also // with non-iram interrupts and the scheduler disabled. None of these CPUs will @@ -223,6 +230,9 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu(void) s_flash_op_complete = true; } +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif // Re-enable non-iram interrupts esp_intr_noniram_enable(); @@ -249,6 +259,12 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_no_os(void) const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; +#if SOC_BRANCH_PREDICTOR_SUPPORTED + /* Disable BP before the first disable_cache(): on shared-cache chips that + * call suspends external memory for all cores, so speculative fetches must + * already be stopped. */ + esp_cpu_branch_prediction_disable(); +#endif // do not care about other CPU, it was halted upon entering panic handler spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]); // Kill interrupts that aren't located in IRAM @@ -263,6 +279,9 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_no_os(void) // Re-enable cache on this CPU spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif // Re-enable non-iram interrupts esp_intr_noniram_enable(); } @@ -302,12 +321,18 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void) { spi_flash_op_lock(); esp_intr_noniram_disable(); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_disable(); +#endif spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu(void) { spi_flash_restore_cache(0, s_flash_op_cache_state[0]); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif esp_intr_noniram_enable(); spi_flash_op_unlock(); } @@ -316,6 +341,9 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_no_os(void) { // Kill interrupts that aren't located in IRAM esp_intr_noniram_disable(); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_disable(); +#endif // Disable cache on this CPU as well spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } @@ -324,6 +352,9 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_no_os(void) { // Re-enable cache on this CPU spi_flash_restore_cache(0, s_flash_op_cache_state[0]); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + esp_cpu_branch_prediction_enable(); +#endif // Re-enable non-iram interrupts esp_intr_noniram_enable(); } @@ -346,19 +377,12 @@ void IRAM_ATTR spi_flash_enable_cache(uint32_t cpuid) #if !CONFIG_SPI_FLASH_ROM_IMPL void IRAM_ATTR spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state) { -#if SOC_BRANCH_PREDICTOR_SUPPORTED - //branch predictor will start cache request as well - esp_cpu_branch_prediction_disable(); -#endif esp_cache_suspend_ext_mem_cache(); } void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state) { esp_cache_resume_ext_mem_cache(); -#if SOC_BRANCH_PREDICTOR_SUPPORTED - esp_cpu_branch_prediction_enable(); -#endif } bool IRAM_ATTR spi_flash_cache_enabled(void) diff --git a/components/spi_flash/include/esp_private/cache_utils.h b/components/spi_flash/include/esp_private/cache_utils.h index f90c3768e68..468cc7ce65f 100644 --- a/components/spi_flash/include/esp_private/cache_utils.h +++ b/components/spi_flash/include/esp_private/cache_utils.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -97,7 +97,11 @@ bool spi_flash_cache_enabled(void); void spi_flash_enable_cache(uint32_t cpuid); /** - * @brief Suspend the Cache access to external memory, will disable branch predictor if supported. + * @brief Suspend the Cache access to external memory. + * + * @note Callers must disable branch prediction around this window when + * SOC_BRANCH_PREDICTOR_SUPPORTED, otherwise speculative fetches can + * raise cache access-fail errors while the cache is suspended. * * @param cpuid the core number to enable the cache for, meaning less on shared cache. * @param saved_state Cache status hold by hal (Used only on ROM impl. in idf, this param unused) @@ -105,7 +109,10 @@ void spi_flash_enable_cache(uint32_t cpuid); void spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state); /** - * @brief Resume the Cache access to external memory, will enable branch predictor if supported. + * @brief Resume the Cache access to external memory. + * + * @note Callers that disabled branch prediction for the suspend window must + * re-enable it after this call when SOC_BRANCH_PREDICTOR_SUPPORTED. * * @param cpuid the core number to enable the cache for, meaning less on shared cache. * @param saved_state Cache status hold by hal (Used only on ROM impl. in idf, this param unused)