feat(spi_flash): implement dynamic CPU frequency switching workaround for encrypted writes

This commit implements a workaround that allows ESP32-C5 to run at 240MHz CPU frequency
normally, while automatically reducing CPU frequency during encrypted flash writes to
ensure correct operation. The frequency limit is chip revision dependent:
- v1.2 and above: limited to 160MHz during encrypted writes
- v1.0 and below: limited to 80MHz during encrypted writes

Key implementation details:
- Frequency limiting is triggered automatically when esp_flash_write_encrypted() is called
- Uses start() flags (ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) to integrate with OS layer
- Works with both PM enabled and disabled configurations
- Frequency is automatically restored after encrypted write completes
- For ESP32-C5 with 120MHz flash, Flash clock and timing registers are adjusted when
  CPU frequency is reduced to 80MHz
- SPI1 timing registers are configured during frequency switching since encrypted writes
  use SPI1 and must work correctly at reduced CPU frequencies

Code improvements:
- Use SOC_MSPI_FREQ_AXI_CONSTRAINED capability macro instead of hardcoded chip checks
- Control workaround via Kconfig (CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED) instead of
  hardcoded macros
- Add comprehensive test cases covering various PM configurations and edge cases

This workaround enables ESP32-C5 applications to benefit from 240MHz CPU performance
while maintaining reliable encrypted flash write functionality.
This commit is contained in:
Xiao Xufeng
2025-12-17 03:33:29 +08:00
parent 9ab736cbd1
commit faf6cc4f84
48 changed files with 2117 additions and 129 deletions
@@ -15,6 +15,15 @@ components/spi_flash/test_apps/esp_flash:
- esp_driver_spi
- esptool_py # Some flash related kconfigs are listed here.
components/spi_flash/test_apps/esp_flash_freq_limit:
enable:
- if: IDF_TARGET == "esp32c5"
depends_components:
- spi_flash
- esp_pm
- esp_driver_gptimer
- esp_hw_support
components/spi_flash/test_apps/esp_flash_stress:
disable:
- if: IDF_TARGET == "esp32h4"
@@ -0,0 +1,17 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.22)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on
# esptool_py as we set CONFIG_ESPTOOLPY_... options.
set(COMPONENTS main esptool_py)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_esp_flash_freq_limit)
message(STATUS "Checking memspi registers are not read-write by half-word")
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
check_register_rw_half_word(SOC_MODULES "spi_mem*" "spi1_mem*"
HAL_MODULES "spimem_flash")
@@ -0,0 +1,2 @@
| Supported Targets | ESP32-C5 |
| ----------------- | -------- |
@@ -0,0 +1,9 @@
set(srcs "test_app_main.c"
"test_esp_flash_freq_limit.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES unity test_utils spi_flash esp_pm bootloader_support freertos esp_timer
esp_driver_gptimer esp_hw_support esp_psram
WHOLE_ARCHIVE)
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_utils.h"
#define TEST_MEMORY_LEAK_THRESHOLD (700)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
unity_run_menu();
}
@@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
factory, 0, 0, 0x10000, 1M
flash_test, data, fat, , 700K
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 factory, 0, 0, 0x10000, 1M
5 flash_test, data, fat, , 700K
@@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'pm_disabled_240mhz',
'pm_disabled_160mhz',
'pm_enabled_240mhz',
'pm_enabled_160mhz',
'pm_disabled_240mhz_xip_psram',
'pm_enabled_240mhz_xip_psram',
],
indirect=True,
)
@idf_parametrize('target', ['esp32c5'], indirect=['target'])
def test_esp_flash_freq_limit(dut: Dut) -> None:
dut.run_all_single_board_cases(group='esp_flash_freq_limit', timeout=10)
@@ -0,0 +1,2 @@
CONFIG_PM_ENABLE=n
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
@@ -0,0 +1,2 @@
CONFIG_PM_ENABLE=n
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
@@ -0,0 +1,8 @@
CONFIG_PM_ENABLE=n
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_BOOT_HW_INIT=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_SPIRAM_SPEED_120M=y
@@ -0,0 +1,6 @@
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_PM_ENABLE=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
@@ -0,0 +1,6 @@
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_PM_ENABLE=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
@@ -0,0 +1,12 @@
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_PM_ENABLE=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_BOOT_HW_INIT=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_SPIRAM_SPEED_120M=y
@@ -0,0 +1,13 @@
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_PM_ENABLE=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_BOOT_HW_INIT=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_SPIRAM_SPEED_120M=y
CONFIG_SPI_FLASH_ROM_IMPL=y
@@ -0,0 +1,2 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"