refactor(spi_flash): move os layer to esp_mspi and chip configs to spi_flash

Also fixes the implicit dependency on esp_partition.
This commit is contained in:
armando
2026-09-09 14:17:00 +08:00
committed by Armando (Dou Yiwen)
parent c565262ded
commit 6e3abd12a1
197 changed files with 1227 additions and 1139 deletions
+106
View File
@@ -0,0 +1,106 @@
idf_build_get_property(target IDF_TARGET)
idf_build_get_property(non_os_build NON_OS_BUILD)
if(${target} STREQUAL "linux")
idf_component_register(SRCS "linux/cache_utils.c"
INCLUDE_DIRS include
REQUIRES esp_hal_mspi)
return()
endif()
if(non_os_build OR CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
set(priv_requires bootloader_support soc esp_hal_gpio)
set(requires hal esp_hal_mspi)
set(srcs "spi_flash_wrap.c" "spi_flash_os_tee_stub.c")
if(ESP_TEE_BUILD)
if(CONFIG_SECURE_TEE_EXT_FLASH_MEMPROT_SPI1)
list(APPEND srcs "mspi_timing_tuning/mspi_timing_tuning.c")
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY)
list(APPEND srcs "mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_mspi_delay.c")
endif()
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_DQS)
list(APPEND srcs "mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_dqs.c")
endif()
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY)
list(APPEND srcs "mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_flash_delay.c")
endif()
endif()
endif()
else()
set(priv_requires bootloader_support soc esp_hal_gpio esp_mm)
set(requires hal esp_hal_mspi)
set(srcs "flash_brownout_hook.c" "cache_utils.c" "flash_ops.c" "spi_flash_wrap.c")
# MSPI0 Octal flash init
if(CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE)
list(APPEND srcs "${target}/spi_flash_oct_flash_init.c")
endif()
if(CONFIG_SPI_FLASH_HPM_ON)
list(APPEND srcs "spi_flash_hpm_enable.c")
endif()
if(CONFIG_ESP_SLEEP_SET_FLASH_DPD)
list(APPEND srcs "spi_flash_dpd_enable.c")
endif()
# MSPI timing tuning
list(APPEND srcs "mspi_timing_tuning/mspi_timing_tuning.c")
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY AND NOT CONFIG_IDF_TARGET_ESP32S3)
set(mspi_delay_file
"${CMAKE_CURRENT_LIST_DIR}/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_mspi_delay.c"
)
if(EXISTS "${mspi_delay_file}")
list(APPEND srcs "mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_mspi_delay.c")
endif()
endif()
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_DQS)
list(APPEND srcs "mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_dqs.c")
endif()
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY)
list(APPEND srcs "mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_flash_delay.c")
endif()
# MSPI intr
list(APPEND srcs "mspi_intr/mspi_intr.c")
endif()
set(include_dirs include
"mspi_timing_tuning/include"
"mspi_timing_tuning/tuning_scheme_impl/include"
"mspi_intr/include")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${target}")
list(APPEND include_dirs "${target}")
endif()
set(ldfragments linker.lf)
idf_component_register(SRCS "${srcs}"
REQUIRES ${requires}
PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS ${include_dirs}
LDFRAGMENTS ${ldfragments})
# Avoid cache miss by unexpected inlineing when built by -Os
set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS "-fno-inline-functions")
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
# These flags are GCC specific
set_property(SOURCE ${cache_srcs} APPEND_STRING PROPERTY COMPILE_FLAGS
" -fno-inline-small-functions -fno-inline-functions-called-once")
endif()
if(NOT non_os_build AND NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
if(CONFIG_SPIRAM)
# esp_mspi_get_io() queries esp_psram_io_get_cs_io() for the PSRAM CS pin
idf_component_optional_requires(PRIVATE esp_psram)
endif()
# Force linking init_flash_os ESP_SYSTEM_INIT_FN to ensure it's not discarded by linker
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_mspi_flash_ops_include_func")
endif()
idf_build_get_property(target IDF_TARGET)
if(CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING)
add_subdirectory(mspi_timing_tuning/port/${target})
endif()
+85
View File
@@ -0,0 +1,85 @@
menu "MSPI Flash configuration"
depends on !APP_BUILD_TYPE_PURE_RAM_APP
menu "SPI Flash behavior when brownout"
config SPI_FLASH_BROWNOUT_RESET_XMC
bool "Enable sending reset when brownout for XMC flash chips"
default y
select SPI_FLASH_BROWNOUT_RESET
help
When this option is selected, the patch will be enabled for XMC.
Follow the recommended flow by XMC for better stability.
DO NOT DISABLE UNLESS YOU KNOW WHAT YOU ARE DOING.
config SPI_FLASH_BROWNOUT_RESET
bool
default y
select ESP_BROWNOUT_USE_INTR
help
When brownout happens during flash erase/write operations,
send reset command to stop the flash operations to improve stability.
endmenu
menu "Optional and Experimental MSPI Features (READ DOCS FIRST)"
comment "Features here require specific hardware (READ DOCS FIRST!)"
config SPI_FLASH_UNDER_HIGH_FREQ
bool
default y if ESPTOOLPY_FLASHFREQ_120M
help
This is a helper config for HPM. Invisible for users.
choice SPI_FLASH_HPM
prompt "High Performance Mode (READ DOCS FIRST, > 80MHz)"
depends on SOC_SPI_MEM_FLASH_SUPPORT_HPM && !ESPTOOLPY_OCT_FLASH
default SPI_FLASH_HPM_AUTO
help
Whether the High Performance Mode of Flash is enabled. As an optional feature, user needs to manually
enable this option as a confirmation. To be back-compatible with earlier IDF version, this option is
automatically enabled with warning when Flash running > 80Mhz.
config SPI_FLASH_HPM_ENA
# Not using name of SPI_FLASH_HPM_ENABLE because it was used as an invisible option and we don't want
# to inherit the value of that one
bool "Enable"
config SPI_FLASH_HPM_AUTO
bool "Auto (Not recommended)"
config SPI_FLASH_HPM_DIS
bool "Disabled"
endchoice
config SPI_FLASH_HPM_ON
bool
# For ESP32-S3, it's enabled by default. For later chips it should be disabled by default
default y if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && ((SPI_FLASH_HPM_ENA || SPI_FLASH_HPM_AUTO)) || \
(!IDF_TARGET_ESP32S3 && SPI_FLASH_HPM_ENA)
help
This option is invisible, and will be selected automatically
when ``ESPTOOLPY_FLASHFREQ_120M`` is selected.
choice SPI_FLASH_HPM_DC
prompt "Support HPM using DC (READ DOCS FIRST)"
depends on SPI_FLASH_HPM_ON
default SPI_FLASH_HPM_DC_AUTO
help
This feature needs your bootloader to be compiled DC-aware (BOOTLOADER_FLASH_DC_AWARE=y). Otherwise the
chip will not be able to boot after a reset.
config SPI_FLASH_HPM_DC_AUTO
bool "Auto (Enable when bootloader support enabled (BOOTLOADER_FLASH_DC_AWARE))"
config SPI_FLASH_HPM_DC_DISABLE
bool "Disable (READ DOCS FIRST)"
endchoice
config SPI_FLASH_HPM_DC_ON
bool
default y if SPI_FLASH_HPM_DC_AUTO && BOOTLOADER_FLASH_DC_AWARE
help
This is a helper config for HPM. Whether HPM-DC is enabled is also determined by bootloader.
Invisible for users.
endmenu
endmenu
+8
View File
@@ -0,0 +1,8 @@
# `esp_mspi`
The `esp_mspi` component provides shared low-level support for flash and PSRAM connected through the MSPI peripheral.
- Coordinates flash operations, cache handling, and memory access.
- Provides MSPI timing tuning, high-performance mode, and power management support.
- Provides shared alignment and memory-barrier utilities.
- Serves as the common MSPI layer used by `spi_flash` for flash access and by `esp_psram` for external RAM support.
+925
View File
@@ -0,0 +1,925 @@
/*
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "rom/cache.h"
#if CONFIG_IDF_TARGET_ESP32
#include "soc/dport_reg.h"
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6
#include "soc/extmem_reg.h"
#endif
#include "soc/ext_mem_defs.h"
#include "esp_rom_spiflash.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include <soc/soc.h>
#include "sdkconfig.h"
#ifndef CONFIG_FREERTOS_UNICORE
#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"
#include "esp_private/cache_utils.h"
#include "esp_private/spi_flash_os.h"
#include "esp_private/freertos_idf_additions_priv.h"
#include "esp_log.h"
ESP_LOG_ATTR_TAG(TAG, "cache");
// Used only on ROM impl. in idf, this param unused, cache status hold by hal
static uint32_t s_flash_op_cache_state[2];
#ifndef CONFIG_FREERTOS_UNICORE
static SemaphoreHandle_t s_flash_op_mutex;
static volatile bool s_flash_op_can_start = false;
static volatile bool s_flash_op_complete = false;
#ifndef NDEBUG
static volatile int s_flash_op_cpu = -1;
#endif
void spi_flash_init_lock(void)
{
s_flash_op_mutex = xSemaphoreCreateRecursiveMutex();
assert(s_flash_op_mutex != NULL);
}
void spi_flash_op_lock(void)
{
xSemaphoreTakeRecursive(s_flash_op_mutex, portMAX_DELAY);
}
void spi_flash_op_unlock(void)
{
xSemaphoreGiveRecursive(s_flash_op_mutex);
}
/*
If you're going to modify this, keep in mind that while the flash caches of the pro and app
cpu are separate, the psram cache is *not*. If one of the CPUs returns from a flash routine
with its cache enabled but the other CPUs cache is not enabled yet, you will have problems
when accessing psram from the former CPU.
*/
void IRAM_ATTR spi_flash_op_block_func(void *arg)
{
// Disable scheduler on this CPU
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
/*
Note: FreeRTOS SMP has changed the behavior of scheduler suspension. But the vTaskPreemptionDisable() function should
achieve the same affect as before (i.e., prevent the current task from being preempted).
*/
vTaskPreemptionDisable(NULL);
#else
vTaskSuspendAll();
#endif // #if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
// Restore interrupts that aren't located in IRAM
esp_intr_noniram_disable();
uint32_t cpuid = (uint32_t) arg;
#if SOC_BRANCH_PREDICTOR_SUPPORTED
/* 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. */
esp_cpu_branch_prediction_disable();
#endif
// s_flash_op_complete flag is cleared on *this* CPU, otherwise the other
// CPU may reset the flag back to false before IPC task has a chance to check it
// (if it is preempted by an ISR taking non-trivial amount of time)
s_flash_op_complete = false;
s_flash_op_can_start = true;
while (!s_flash_op_complete) {
// busy loop here and wait for the other CPU to finish flash operation
}
// 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 ) )
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionEnable(NULL);
#else
// Re-enable scheduler
xTaskResumeAll();
#endif // #if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
}
void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void)
{
#if CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
assert(esp_task_stack_is_sane_cache_disabled());
#endif
spi_flash_op_lock();
int cpuid = xPortGetCoreID();
uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
#ifndef NDEBUG
// For sanity check later: record the CPU which has started doing flash operation
assert(s_flash_op_cpu == -1);
s_flash_op_cpu = cpuid;
#endif
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
// Scheduler hasn't been started yet, it means that spi_flash API is being
// called from the 2nd stage bootloader or from user_start_cpu0, i.e. from
// PRO CPU. APP CPU is either in reset or spinning inside user_start_cpu1,
// which is in IRAM. So it is safe to disable cache for the other_cpuid after
// esp_intr_noniram_disable.
assert(other_cpuid == 1);
} else {
bool ipc_call_was_send_to_other_cpu;
do {
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionDisable(NULL);
#else
// Disable scheduler on the current CPU
vTaskSuspendAll();
#endif
cpuid = xPortGetCoreID();
other_cpuid = (cpuid == 0) ? 1 : 0;
#ifndef NDEBUG
s_flash_op_cpu = cpuid;
#endif
s_flash_op_can_start = false;
ipc_call_was_send_to_other_cpu = esp_ipc_call_nonblocking(other_cpuid, &spi_flash_op_block_func, (void *) other_cpuid) == ESP_OK;
if (!ipc_call_was_send_to_other_cpu) {
// IPC call was not send to other cpu because another nonblocking API is running now.
// Enable the Scheduler again will not help the IPC to speed it up
// but there is a benefit to schedule to a higher priority task before the nonblocking running IPC call is done.
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionEnable(NULL);
#else
xTaskResumeAll();
#endif
}
} while (!ipc_call_was_send_to_other_cpu);
while (!s_flash_op_can_start) {
// Busy loop and wait for spi_flash_op_block_func to disable cache
// on the other CPU
}
}
// 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
// touch external RAM or flash this way, so we can safely disable caches.
spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]);
#if SOC_IDCACHE_PER_CORE
//only needed if cache(s) is per core
spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]);
#endif
}
void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu(void)
{
const int cpuid = xPortGetCoreID();
#ifndef NDEBUG
// Sanity check: flash operation ends on the same CPU as it has started
assert(cpuid == s_flash_op_cpu);
// More sanity check: if scheduler isn't started, only CPU0 can call this.
assert(!(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED && cpuid != 0));
s_flash_op_cpu = -1;
#endif
// Re-enable cache. After this, cache (flash and external RAM) should work again.
spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]);
#if SOC_IDCACHE_PER_CORE
//only needed if cache(s) is per core
const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
spi_flash_restore_cache(other_cpuid, s_flash_op_cache_state[other_cpuid]);
#endif
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
// Signal to spi_flash_op_block_task that flash operation is complete
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();
// Resume tasks on the current CPU, if the scheduler has started.
// NOTE: enabling non-IRAM interrupts has to happen before this,
// because once the scheduler has started, due to preemption the
// current task can end up being moved to the other CPU.
// But esp_intr_noniram_enable has to be called on the same CPU which
// called esp_intr_noniram_disable
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionEnable(NULL);
#else
xTaskResumeAll();
#endif // #if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
}
// Release API lock
spi_flash_op_unlock();
}
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
esp_intr_noniram_disable();
// Disable cache on this CPU as well
spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]);
}
void IRAM_ATTR spi_flash_enable_interrupts_caches_no_os(void)
{
const uint32_t cpuid = xPortGetCoreID();
// 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();
}
#else // CONFIG_FREERTOS_UNICORE
void spi_flash_init_lock(void)
{
}
void spi_flash_op_lock(void)
{
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionDisable(NULL);
}
#else
vTaskSuspendAll();
#endif // #if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
}
void spi_flash_op_unlock(void)
{
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionEnable(NULL);
}
#else
xTaskResumeAll();
#endif // #if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
}
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();
}
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]);
}
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();
}
#endif // CONFIG_FREERTOS_UNICORE
void IRAM_ATTR spi_flash_enable_cache(uint32_t cpuid)
{
#if CONFIG_IDF_TARGET_ESP32
uint32_t cache_value = cache_ll_l1_get_enabled_bus(cpuid);
// Re-enable cache on this CPU
spi_flash_restore_cache(cpuid, cache_value);
#else
spi_flash_restore_cache(0, 0); // TODO cache_value should be non-zero
#endif
}
#if !CONFIG_SPI_FLASH_ROM_IMPL
void IRAM_ATTR spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state)
{
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();
}
bool IRAM_ATTR spi_flash_cache_enabled(void)
{
return cache_hal_is_cache_enabled(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
}
#endif
#if CONFIG_IDF_TARGET_ESP32S2
IRAM_ATTR void esp_config_instruction_cache_mode(void)
{
cache_size_t cache_size;
cache_ways_t cache_ways;
cache_line_size_t cache_line_size;
#if CONFIG_ESP32S2_INSTRUCTION_CACHE_8KB
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_8KB;
#else
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_ICACHE_HIGH, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_16KB;
#endif
cache_ways = CACHE_4WAYS_ASSOC;
#if CONFIG_ESP32S2_INSTRUCTION_CACHE_LINE_16B
cache_line_size = CACHE_LINE_SIZE_16B;
#else
cache_line_size = CACHE_LINE_SIZE_32B;
#endif
ESP_EARLY_LOGI(TAG, "Instruction cache \t: size %dKB, %dWays, cache line size %dByte", cache_size == CACHE_SIZE_8KB ? 8 : 16, 4, cache_line_size == CACHE_LINE_SIZE_16B ? 16 : 32);
Cache_Suspend_ICache();
Cache_Set_ICache_Mode(cache_size, cache_ways, cache_line_size);
Cache_Invalidate_ICache_All();
Cache_Resume_ICache(0);
}
IRAM_ATTR void esp_config_data_cache_mode(void)
{
#define CACHE_SIZE_0KB 99 //If Cache set to 0 KB, cache is bypassed, the cache size doesn't take into effect. Set this macro to a unique value for log
cache_size_t cache_size;
cache_ways_t cache_ways;
cache_line_size_t cache_line_size;
#if CONFIG_ESP32S2_INSTRUCTION_CACHE_8KB
#if CONFIG_ESP32S2_DATA_CACHE_0KB
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_0KB;
#elif CONFIG_ESP32S2_DATA_CACHE_8KB
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_DCACHE_LOW, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_8KB;
#else
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_DCACHE_LOW, CACHE_MEMORY_DCACHE_HIGH, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_16KB;
#endif
#else
#if CONFIG_ESP32S2_DATA_CACHE_0KB
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_ICACHE_HIGH, CACHE_MEMORY_INVALID, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_0KB;
#elif CONFIG_ESP32S2_DATA_CACHE_8KB
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_ICACHE_HIGH, CACHE_MEMORY_DCACHE_LOW, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_8KB;
#else
Cache_Allocate_SRAM(CACHE_MEMORY_ICACHE_LOW, CACHE_MEMORY_ICACHE_HIGH, CACHE_MEMORY_DCACHE_LOW, CACHE_MEMORY_DCACHE_HIGH);
cache_size = CACHE_SIZE_16KB;
#endif
#endif
cache_ways = CACHE_4WAYS_ASSOC;
#if CONFIG_ESP32S2_DATA_CACHE_LINE_16B
cache_line_size = CACHE_LINE_SIZE_16B;
#else
cache_line_size = CACHE_LINE_SIZE_32B;
#endif
ESP_EARLY_LOGI(TAG, "Data cache \t\t: size %dKB, %dWays, cache line size %dByte", (cache_size == CACHE_SIZE_0KB) ? 0 : ((cache_size == CACHE_SIZE_8KB) ? 8 : 16), 4, cache_line_size == CACHE_LINE_SIZE_16B ? 16 : 32);
Cache_Set_DCache_Mode(cache_size, cache_ways, cache_line_size);
Cache_Invalidate_DCache_All();
}
static IRAM_ATTR void esp_enable_cache_flash_wrap(bool icache, bool dcache)
{
uint32_t i_autoload, d_autoload;
if (icache) {
i_autoload = Cache_Suspend_ICache();
}
if (dcache) {
d_autoload = Cache_Suspend_DCache();
}
REG_SET_BIT(EXTMEM_PRO_CACHE_WRAP_AROUND_CTRL_REG, EXTMEM_PRO_CACHE_FLASH_WRAP_AROUND);
if (icache) {
Cache_Resume_ICache(i_autoload);
}
if (dcache) {
Cache_Resume_DCache(d_autoload);
}
}
#if (CONFIG_IDF_TARGET_ESP32S2 && CONFIG_SPIRAM)
static IRAM_ATTR void esp_enable_cache_spiram_wrap(bool icache, bool dcache)
{
uint32_t i_autoload, d_autoload;
if (icache) {
i_autoload = Cache_Suspend_ICache();
}
if (dcache) {
d_autoload = Cache_Suspend_DCache();
}
REG_SET_BIT(EXTMEM_PRO_CACHE_WRAP_AROUND_CTRL_REG, EXTMEM_PRO_CACHE_SRAM_RD_WRAP_AROUND);
if (icache) {
Cache_Resume_ICache(i_autoload);
}
if (dcache) {
Cache_Resume_DCache(d_autoload);
}
}
#endif
esp_err_t esp_enable_cache_wrap(bool icache_wrap_enable, bool dcache_wrap_enable)
{
int icache_wrap_size = 0, dcache_wrap_size = 0;
int flash_wrap_sizes[2] = {-1, -1}, spiram_wrap_sizes[2] = {-1, -1};
int flash_wrap_size = 0, spiram_wrap_size = 0;
int flash_count = 0, spiram_count = 0;
int i;
bool flash_spiram_wrap_together, flash_support_wrap = true, spiram_support_wrap = true;
uint32_t drom0_in_icache = 1;//always 1 in esp32s2
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C61 //TODO: IDF-4307
drom0_in_icache = 0;
#endif
if (icache_wrap_enable) {
#if CONFIG_ESP32S2_INSTRUCTION_CACHE_LINE_16B || CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B
icache_wrap_size = FLASH_WRAP_SIZE_16B;
#else
icache_wrap_size = FLASH_WRAP_SIZE_32B;
#endif
}
if (dcache_wrap_enable) {
#if CONFIG_ESP32S2_DATA_CACHE_LINE_16B || CONFIG_ESP32S3_DATA_CACHE_LINE_16B
dcache_wrap_size = FLASH_WRAP_SIZE_16B;
#else
dcache_wrap_size = FLASH_WRAP_SIZE_32B;
#endif
}
uint32_t instruction_use_spiram = 0;
uint32_t rodata_use_spiram = 0;
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
extern uint32_t esp_spiram_instruction_access_enabled(void);
instruction_use_spiram = esp_spiram_instruction_access_enabled();
#endif
#if CONFIG_SPIRAM_RODATA
extern uint32_t esp_spiram_rodata_access_enabled(void);
rodata_use_spiram = esp_spiram_rodata_access_enabled();
#endif
if (instruction_use_spiram) {
spiram_wrap_sizes[0] = icache_wrap_size;
} else {
flash_wrap_sizes[0] = icache_wrap_size;
}
if (rodata_use_spiram) {
if (drom0_in_icache) {
spiram_wrap_sizes[0] = icache_wrap_size;
} else {
spiram_wrap_sizes[1] = dcache_wrap_size;
flash_wrap_sizes[1] = dcache_wrap_size;
}
} else {
if (drom0_in_icache) {
flash_wrap_sizes[0] = icache_wrap_size;
} else {
flash_wrap_sizes[1] = dcache_wrap_size;
}
}
#if (CONFIG_IDF_TARGET_ESP32S2 && CONFIG_SPIRAM)
spiram_wrap_sizes[1] = dcache_wrap_size;
#endif
for (i = 0; i < 2; i++) {
if (flash_wrap_sizes[i] != -1) {
flash_count++;
flash_wrap_size = flash_wrap_sizes[i];
}
}
for (i = 0; i < 2; i++) {
if (spiram_wrap_sizes[i] != -1) {
spiram_count++;
spiram_wrap_size = spiram_wrap_sizes[i];
}
}
if (flash_count + spiram_count <= 2) {
flash_spiram_wrap_together = false;
} else {
flash_spiram_wrap_together = true;
}
ESP_EARLY_LOGI(TAG, "flash_count=%d, size=%d, spiram_count=%d, size=%d,together=%d", flash_count, flash_wrap_size, spiram_count, spiram_wrap_size, flash_spiram_wrap_together);
if (flash_count > 1 && flash_wrap_sizes[0] != flash_wrap_sizes[1]) {
ESP_EARLY_LOGW(TAG, "Flash wrap with different length %d and %d, abort wrap.", flash_wrap_sizes[0], flash_wrap_sizes[1]);
if (spiram_wrap_size == 0) {
return ESP_FAIL;
}
if (flash_spiram_wrap_together) {
ESP_EARLY_LOGE(TAG, "Abort spiram wrap because flash wrap length not fixed.");
return ESP_FAIL;
}
}
if (spiram_count > 1 && spiram_wrap_sizes[0] != spiram_wrap_sizes[1]) {
ESP_EARLY_LOGW(TAG, "SPIRAM wrap with different length %d and %d, abort wrap.", spiram_wrap_sizes[0], spiram_wrap_sizes[1]);
if (flash_wrap_size == 0) {
return ESP_FAIL;
}
if (flash_spiram_wrap_together) {
ESP_EARLY_LOGW(TAG, "Abort flash wrap because spiram wrap length not fixed.");
return ESP_FAIL;
}
}
if (flash_spiram_wrap_together && flash_wrap_size != spiram_wrap_size) {
ESP_EARLY_LOGW(TAG, "SPIRAM has different wrap length with flash, %d and %d, abort wrap.", spiram_wrap_size, flash_wrap_size);
return ESP_FAIL;
}
#ifdef CONFIG_ESPTOOLPY_FLASHMODE_QIO
flash_support_wrap = true;
spi_flash_wrap_probe();
if (!spi_flash_support_wrap_size(flash_wrap_size)) {
flash_support_wrap = false;
ESP_EARLY_LOGW(TAG, "Flash do not support wrap size %d.", flash_wrap_size);
}
#else
ESP_EARLY_LOGW(TAG, "Flash is not in QIO mode, do not support wrap.");
#endif
#if (CONFIG_IDF_TARGET_ESP32S2 && CONFIG_SPIRAM)
extern bool psram_support_wrap_size(uint32_t wrap_size);
if (!psram_support_wrap_size(spiram_wrap_size)) {
spiram_support_wrap = false;
ESP_EARLY_LOGW(TAG, "SPIRAM do not support wrap size %d.", spiram_wrap_size);
}
#endif
if (flash_spiram_wrap_together && !(flash_support_wrap && spiram_support_wrap)) {
ESP_EARLY_LOGW(TAG, "Flash and SPIRAM should support wrap together.");
return ESP_FAIL;
}
if (flash_support_wrap && flash_wrap_size > 0) {
ESP_EARLY_LOGI(TAG, "Flash wrap enabled, size = %d.", flash_wrap_size);
spi_flash_wrap_enable(flash_wrap_size);
esp_enable_cache_flash_wrap((flash_wrap_sizes[0] > 0), (flash_wrap_sizes[1] > 0));
}
#if (CONFIG_IDF_TARGET_ESP32S2 && CONFIG_SPIRAM)
extern esp_err_t psram_enable_wrap(uint32_t wrap_size);
if (spiram_support_wrap && spiram_wrap_size > 0) {
ESP_EARLY_LOGI(TAG, "SPIRAM wrap enabled, size = %d.", spiram_wrap_size);
psram_enable_wrap(spiram_wrap_size);
esp_enable_cache_spiram_wrap((spiram_wrap_sizes[0] > 0), (spiram_wrap_sizes[1] > 0));
}
#endif
return ESP_OK;
}
#endif
#if CONFIG_IDF_TARGET_ESP32S3
IRAM_ATTR void esp_config_instruction_cache_mode(void)
{
cache_size_t cache_size;
cache_ways_t cache_ways;
cache_line_size_t cache_line_size;
#if CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB
Cache_Occupy_ICache_MEMORY(CACHE_MEMORY_IBANK0, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_HALF;
#else
Cache_Occupy_ICache_MEMORY(CACHE_MEMORY_IBANK0, CACHE_MEMORY_IBANK1);
cache_size = CACHE_SIZE_FULL;
#endif
#if CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS
cache_ways = CACHE_4WAYS_ASSOC;
#else
cache_ways = CACHE_8WAYS_ASSOC;
#endif
#if CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B
cache_line_size = CACHE_LINE_SIZE_16B;
#elif CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B
cache_line_size = CACHE_LINE_SIZE_32B;
#else
cache_line_size = CACHE_LINE_SIZE_64B;
#endif
ESP_EARLY_LOGI(TAG, "Instruction cache: size %dKB, %dWays, cache line size %dByte", cache_size == CACHE_SIZE_HALF ? 16 : 32, cache_ways == CACHE_4WAYS_ASSOC ? 4 : 8, cache_line_size == CACHE_LINE_SIZE_16B ? 16 : (cache_line_size == CACHE_LINE_SIZE_32B ? 32 : 64));
Cache_Set_ICache_Mode(cache_size, cache_ways, cache_line_size);
Cache_Invalidate_ICache_All();
extern void Cache_Enable_ICache(uint32_t autoload);
Cache_Enable_ICache(0);
}
IRAM_ATTR void esp_config_data_cache_mode(void)
{
cache_size_t cache_size;
cache_ways_t cache_ways;
cache_line_size_t cache_line_size;
#if CONFIG_ESP32S3_DATA_CACHE_32KB
Cache_Occupy_DCache_MEMORY(CACHE_MEMORY_DBANK1, CACHE_MEMORY_INVALID);
cache_size = CACHE_SIZE_HALF;
#else
Cache_Occupy_DCache_MEMORY(CACHE_MEMORY_DBANK0, CACHE_MEMORY_DBANK1);
cache_size = CACHE_SIZE_FULL;
#endif
#if CONFIG_ESP32S3_DATA_CACHE_4WAYS
cache_ways = CACHE_4WAYS_ASSOC;
#else
cache_ways = CACHE_8WAYS_ASSOC;
#endif
#if CONFIG_ESP32S3_DATA_CACHE_LINE_16B
cache_line_size = CACHE_LINE_SIZE_16B;
#elif CONFIG_ESP32S3_DATA_CACHE_LINE_32B
cache_line_size = CACHE_LINE_SIZE_32B;
#else
cache_line_size = CACHE_LINE_SIZE_64B;
#endif
// ESP_EARLY_LOGI(TAG, "Data cache: size %dKB, %dWays, cache line size %dByte", cache_size == CACHE_SIZE_HALF ? 32 : 64, cache_ways == CACHE_4WAYS_ASSOC ? 4 : 8, cache_line_size == CACHE_LINE_SIZE_16B ? 16 : (cache_line_size == CACHE_LINE_SIZE_32B ? 32 : 64));
Cache_Set_DCache_Mode(cache_size, cache_ways, cache_line_size);
Cache_Invalidate_DCache_All();
}
static IRAM_ATTR void esp_enable_cache_flash_wrap(bool icache, bool dcache)
{
uint32_t i_autoload, d_autoload;
if (icache) {
i_autoload = Cache_Suspend_ICache();
}
if (dcache) {
d_autoload = Cache_Suspend_DCache();
}
REG_SET_BIT(EXTMEM_CACHE_WRAP_AROUND_CTRL_REG, EXTMEM_CACHE_FLASH_WRAP_AROUND);
if (icache) {
Cache_Resume_ICache(i_autoload);
}
if (dcache) {
Cache_Resume_DCache(d_autoload);
}
}
#if (CONFIG_IDF_TARGET_ESP32S3 && CONFIG_SPIRAM)
static IRAM_ATTR void esp_enable_cache_spiram_wrap(bool icache, bool dcache)
{
uint32_t i_autoload, d_autoload;
if (icache) {
i_autoload = Cache_Suspend_ICache();
}
if (dcache) {
d_autoload = Cache_Suspend_DCache();
}
REG_SET_BIT(EXTMEM_CACHE_WRAP_AROUND_CTRL_REG, EXTMEM_CACHE_SRAM_RD_WRAP_AROUND);
if (icache) {
Cache_Resume_ICache(i_autoload);
}
if (dcache) {
Cache_Resume_DCache(d_autoload);
}
}
#endif
esp_err_t esp_enable_cache_wrap(bool icache_wrap_enable, bool dcache_wrap_enable)
{
int icache_wrap_size = 0, dcache_wrap_size = 0;
int flash_wrap_sizes[2] = {-1, -1}, spiram_wrap_sizes[2] = {-1, -1};
int flash_wrap_size = 0, spiram_wrap_size = 0;
int flash_count = 0, spiram_count = 0;
int i;
bool flash_spiram_wrap_together, flash_support_wrap = false, spiram_support_wrap = true;
uint32_t drom0_in_icache = 0;//always 0 in chip7.2.4
if (icache_wrap_enable) {
#if CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B
icache_wrap_size = FLASH_WRAP_SIZE_16B;
#elif CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B
icache_wrap_size = FLASH_WRAP_SIZE_32B;
#else
icache_wrap_size = FLASH_WRAP_SIZE_64B;
#endif
}
if (dcache_wrap_enable) {
#if CONFIG_ESP32S3_DATA_CACHE_LINE_16B
dcache_wrap_size = FLASH_WRAP_SIZE_16B;
#elif CONFIG_ESP32S3_DATA_CACHE_LINE_32B
dcache_wrap_size = FLASH_WRAP_SIZE_32B;
#else
dcache_wrap_size = FLASH_WRAP_SIZE_64B;
#endif
}
uint32_t instruction_use_spiram = 0;
uint32_t rodata_use_spiram = 0;
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
extern uint32_t esp_spiram_instruction_access_enabled(void);
instruction_use_spiram = esp_spiram_instruction_access_enabled();
#endif
#if CONFIG_SPIRAM_RODATA
extern uint32_t esp_spiram_rodata_access_enabled(void);
rodata_use_spiram = esp_spiram_rodata_access_enabled();
#endif
if (instruction_use_spiram) {
spiram_wrap_sizes[0] = icache_wrap_size;
} else {
flash_wrap_sizes[0] = icache_wrap_size;
}
if (rodata_use_spiram) {
if (drom0_in_icache) {
spiram_wrap_sizes[0] = icache_wrap_size;
} else {
spiram_wrap_sizes[1] = dcache_wrap_size;
}
} else {
if (drom0_in_icache) {
flash_wrap_sizes[0] = icache_wrap_size;
} else {
flash_wrap_sizes[1] = dcache_wrap_size;
}
}
#if (CONFIG_IDF_TARGET_ESP32S3 && CONFIG_SPIRAM)
spiram_wrap_sizes[1] = dcache_wrap_size;
#endif
for (i = 0; i < 2; i++) {
if (flash_wrap_sizes[i] != -1) {
flash_count++;
flash_wrap_size = flash_wrap_sizes[i];
}
}
for (i = 0; i < 2; i++) {
if (spiram_wrap_sizes[i] != -1) {
spiram_count++;
spiram_wrap_size = spiram_wrap_sizes[i];
}
}
if (flash_count + spiram_count <= 2) {
flash_spiram_wrap_together = false;
} else {
flash_spiram_wrap_together = true;
}
if (flash_count > 1 && flash_wrap_sizes[0] != flash_wrap_sizes[1]) {
ESP_EARLY_LOGW(TAG, "Flash wrap with different length %d and %d, abort wrap.", flash_wrap_sizes[0], flash_wrap_sizes[1]);
if (spiram_wrap_size == 0) {
return ESP_FAIL;
}
if (flash_spiram_wrap_together) {
ESP_EARLY_LOGE(TAG, "Abort spiram wrap because flash wrap length not fixed.");
return ESP_FAIL;
}
}
if (spiram_count > 1 && spiram_wrap_sizes[0] != spiram_wrap_sizes[1]) {
ESP_EARLY_LOGW(TAG, "SPIRAM wrap with different length %d and %d, abort wrap.", spiram_wrap_sizes[0], spiram_wrap_sizes[1]);
if (flash_wrap_size == 0) {
return ESP_FAIL;
}
if (flash_spiram_wrap_together) {
ESP_EARLY_LOGW(TAG, "Abort flash wrap because spiram wrap length not fixed.");
return ESP_FAIL;
}
}
if (flash_spiram_wrap_together && flash_wrap_size != spiram_wrap_size) {
ESP_EARLY_LOGW(TAG, "SPIRAM has different wrap length with flash, %d and %d, abort wrap.", spiram_wrap_size, flash_wrap_size);
return ESP_FAIL;
}
#ifdef CONFIG_ESPTOOLPY_FLASHMODE_QIO
flash_support_wrap = true;
spi_flash_wrap_probe();
if (!spi_flash_support_wrap_size(flash_wrap_size)) {
flash_support_wrap = false;
ESP_EARLY_LOGW(TAG, "Flash do not support wrap size %d.", flash_wrap_size);
}
#else
ESP_EARLY_LOGW(TAG, "Flash is not in QIO mode, do not support wrap.");
#endif
#if (CONFIG_IDF_TARGET_ESP32S3 && CONFIG_SPIRAM)
extern bool psram_support_wrap_size(uint32_t wrap_size);
if (!psram_support_wrap_size(spiram_wrap_size)) {
spiram_support_wrap = false;
ESP_EARLY_LOGW(TAG, "SPIRAM do not support wrap size %d.", spiram_wrap_size);
}
#endif
if (flash_spiram_wrap_together && !(flash_support_wrap && spiram_support_wrap)) {
ESP_EARLY_LOGW(TAG, "Flash and SPIRAM should support wrap together.");
return ESP_FAIL;
}
if (flash_support_wrap && flash_wrap_size > 0) {
ESP_EARLY_LOGI(TAG, "Flash wrap enabled, size = %d.", flash_wrap_size);
spi_flash_wrap_enable(flash_wrap_size);
esp_enable_cache_flash_wrap((flash_wrap_sizes[0] > 0), (flash_wrap_sizes[1] > 0));
}
#if (CONFIG_IDF_TARGET_ESP32S3 && CONFIG_SPIRAM)
extern esp_err_t psram_enable_wrap(uint32_t wrap_size);
if (spiram_support_wrap && spiram_wrap_size > 0) {
ESP_EARLY_LOGI(TAG, "SPIRAM wrap enabled, size = %d.", spiram_wrap_size);
psram_enable_wrap(spiram_wrap_size);
esp_enable_cache_spiram_wrap((spiram_wrap_sizes[0] > 0), (spiram_wrap_sizes[1] > 0));
}
#endif
return ESP_OK;
}
#endif
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
static IRAM_ATTR void esp_enable_cache_flash_wrap(bool icache)
{
uint32_t i_autoload;
if (icache) {
i_autoload = Cache_Suspend_ICache();
}
REG_SET_BIT(EXTMEM_CACHE_WRAP_AROUND_CTRL_REG, EXTMEM_CACHE_FLASH_WRAP_AROUND);
if (icache) {
Cache_Resume_ICache(i_autoload);
}
}
esp_err_t esp_enable_cache_wrap(bool icache_wrap_enable)
{
int flash_wrap_size = 0;
bool flash_support_wrap = false;
if (icache_wrap_enable) {
flash_wrap_size = 32;
}
#ifdef CONFIG_ESPTOOLPY_FLASHMODE_QIO
flash_support_wrap = true;
spi_flash_wrap_probe();
if (!spi_flash_support_wrap_size(flash_wrap_size)) {
flash_support_wrap = false;
ESP_EARLY_LOGW(TAG, "Flash do not support wrap size %d.", flash_wrap_size);
}
#else
ESP_EARLY_LOGW(TAG, "Flash is not in QIO mode, do not support wrap.");
#endif // CONFIG_ESPTOOLPY_FLASHMODE_QIO
if (flash_support_wrap && flash_wrap_size > 0) {
ESP_EARLY_LOGI(TAG, "Flash wrap enabled, size = %d.", flash_wrap_size);
spi_flash_wrap_enable(flash_wrap_size);
esp_enable_cache_flash_wrap((flash_wrap_size > 0));
}
return ESP_OK;
}
#endif // CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* System level OPI Flash APIs (private)
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Register ROM functions and init flash device registers to make use of octal flash
*
* @param chip_id Full device ID read via RDID command
*/
esp_err_t esp_opiflash_init(uint32_t chip_id);
/**
* @brief Set Octal Flash chip specifically required MSPI register settings here
*/
void esp_opiflash_set_required_regs(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,185 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "spi_flash_defs.h"
//MXIC OPI mode needs two bytes of command - 2nd byte is the inversion of the command (1st) byte. S3 HW send LSB first
#define MXIC_CMD16(cmd8) ( (uint8_t)(cmd8) | ((uint8_t)(~(cmd8)) << 8) )
#define OPI_CMD_FORMAT_MXIC_STR() { \
.rdid = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_RDID), \
.addr = 0, \
.addr_bit_len = 4*8, \
.dummy_bit_len = 4, \
.data_bit_len = 4 * 8, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.rdsr = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_RDSR), \
.addr = 0, \
.addr_bit_len = 4*8, \
.dummy_bit_len = 4, \
.data_bit_len = 1 * 8, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.wren = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_WREN), \
.addr = 0, \
.addr_bit_len = 0, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.se = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_SECTOR_ERASE_4B), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 1, \
}, \
.be64k = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_LARGE_BLOCK_ERASE_4B), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 1, \
}, \
.read = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_8READ), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 20, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.pp = { \
.mode = ESP_ROM_SPIFLASH_OPI_STR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_PROGRAM_PAGE_4B), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 1, \
}, \
.cache_rd_cmd = { \
.addr_bit_len = 32, \
.dummy_bit_len = 20, \
.cmd = MXIC_CMD16(CMD_8READ), \
.cmd_bit_len = 16, \
.var_dummy_en = 1, \
} \
}
#define OPI_CMD_FORMAT_MXIC_DTR() { \
.rdid = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_RDID), \
.addr = 0, \
.addr_bit_len = 4*8, \
.dummy_bit_len = 4*2, \
.data_bit_len = 4 * 8, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.rdsr = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_RDSR), \
.addr = 0, \
.addr_bit_len = 4*8, \
.dummy_bit_len = 4*2, \
.data_bit_len = 2 * 8, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.wren = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_WREN), \
.addr = 0, \
.addr_bit_len = 0, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.se = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_SECTOR_ERASE_4B), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 1, \
}, \
.be64k = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_LARGE_BLOCK_ERASE_4B), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 1, \
}, \
.read = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_8DTRD), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 20*2, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 0, \
}, \
.pp = { \
.mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE, \
.cmd_bit_len = 16, \
.cmd = MXIC_CMD16(CMD_PROGRAM_PAGE_4B), \
.addr = 0, \
.addr_bit_len = 32, \
.dummy_bit_len = 0, \
.data_bit_len = 0, \
.cs_sel = 0x1, \
.is_pe = 1, \
}, \
.cache_rd_cmd = { \
.addr_bit_len = 32, \
.dummy_bit_len = 20*2, \
.cmd = MXIC_CMD16(CMD_8DTRD), \
.cmd_bit_len = 16, \
.var_dummy_en = 1, \
} \
}
@@ -0,0 +1,303 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp32s3/rom/spi_flash.h"
#include "esp32s3/rom/opi_flash.h"
#include "esp_private/spi_flash_os.h"
#include "esp_private/opi_flash_private.h"
#include "soc/spi_mem_reg.h"
#include "soc/io_mux_reg.h"
#include "opi_flash_cmd_format_mxic.h"
#define SPI_FLASH_SPI_CMD_WRCR2 0x72
#define SPI_FLASH_SPI_CMD_RDSR 0x05
#define SPI_FLASH_SPI_CMD_RDCR 0x15
#define SPI_FLASH_SPI_CMD_WRSRCR 0x01
#define SPI_FLASH_SPI_CMD_RDSFDP 0x5A
/**
* Supported Flash chip vendor id
*/
#define ESP_FLASH_CHIP_MXIC_OCT 0xC2
const static char *TAG = "Octal Flash";
// default value is rom_default_spiflash_legacy_flash_func
extern const spiflash_legacy_funcs_t *rom_spiflash_legacy_funcs;
static uint32_t s_chip_id;
static void s_register_rom_function(void)
{
static spiflash_legacy_funcs_t rom_func = {
.read_sub_len = 32,
.write_sub_len = 32,
.unlock = esp_rom_opiflash_wait_idle,
.erase_block = esp_rom_opiflash_erase_block_64k,
.erase_sector = esp_rom_opiflash_erase_sector,
.read = esp_rom_opiflash_read,
.write = esp_rom_opiflash_write,
.wait_idle = esp_rom_opiflash_wait_idle,
.wren = esp_rom_opiflash_wren,
.erase_area = esp_rom_opiflash_erase_area,
};
rom_spiflash_legacy_funcs = &rom_func;
}
#if CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP
/*----------------------------------------------------------------------------------------------------
MXIC Specific Functions
-----------------------------------------------------------------------------------------------------*/
static esp_err_t s_probe_mxic_chip(uint32_t chip_id, uint8_t *out_vendor_id)
{
if (chip_id >> 16 != ESP_FLASH_CHIP_MXIC_OCT) {
return ESP_ERR_NOT_FOUND;
}
if (((chip_id >> 8) & 0xf0) != 0x80) {
// We now suppose that middle id of opi flash is 0x8*.
ESP_EARLY_LOGE(TAG, "Detected MXIC Flash, but memory type is not Octal");
return ESP_ERR_NOT_FOUND;
}
*out_vendor_id = ESP_FLASH_CHIP_MXIC_OCT;
return ESP_OK;
}
#if CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
static bool s_mxic_dtr_need_swap(void)
{
// This function is used for judging the data bytes whether need swap.
// For some of opi flash chips, the data bytes are ordered by D1-D0-D3-D2. This kinds of order needs swap.
// On the contrary, some opi flash chips order the data like D0-D1-D2-D3. This kinds of order doesn't need swap.
// Note: this function must be called when flash works under single line mode.
// 1. Send 0x5A to read SFDP regs for getting the first address of JEDEC Flash Parameter table.
// 2. Add offset with first address to get the order in 8D-8D-8D mode.
// 3. Judge whether the BIT(7) is 1, 1 stands for need swap, vice versa.
uint8_t JEDEC_first_address = 0;
uint8_t byte_order_val = 0;
uint8_t dummy = 8;
uint8_t cmd_len = 8;
uint8_t addr_len = 24;
uint8_t miso_bit_len = 8;
esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_FASTRD_MODE,
SPI_FLASH_SPI_CMD_RDSFDP, cmd_len,
0x0C, addr_len,
dummy,
NULL, 0,
(uint8_t*)&JEDEC_first_address, miso_bit_len,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_FASTRD_MODE,
SPI_FLASH_SPI_CMD_RDSFDP, cmd_len,
(JEDEC_first_address + 0x47), addr_len,
dummy,
NULL, 0,
(uint8_t*)&byte_order_val, miso_bit_len,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
return ((byte_order_val & 0x80) == 0x80) ? true : false;
}
#endif // CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
// 0x00: SPI; 0x01: STR OPI; 0x02: DTR OPI
static void s_set_flash_dtr_str_opi_mode(int spi_num, uint8_t val)
{
uint8_t cmd_len = 8;
int addr_bit_len = 32;
int dummy = 0;
int data_bit_len = 8;
esp_rom_spiflash_write_enable(&g_rom_flashchip);
//SPI command, WRCR2
esp_rom_opiflash_exec_cmd(spi_num, ESP_ROM_SPIFLASH_FASTRD_MODE,
SPI_FLASH_SPI_CMD_WRCR2, cmd_len,
0, addr_bit_len,
dummy,
(uint8_t *)&val, data_bit_len,
NULL, 0,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
}
//To set the output driver strength
static void s_set_flash_ouput_driver_strength(int spi_num, uint8_t strength)
{
uint16_t reg_val = 0;
uint8_t sr_reg_val = 0;
uint8_t cr_reg_val = 0;
uint8_t cmd_len = 8;
uint32_t addr = 0;
int addr_bit_len = 0;
int dummy = 0;
int data_bit_len = 8;
//Read
//SPI command, RDSR
esp_rom_opiflash_exec_cmd(spi_num, ESP_ROM_SPIFLASH_FASTRD_MODE,
SPI_FLASH_SPI_CMD_RDSR, cmd_len,
addr, addr_bit_len,
dummy,
NULL, 0,
(uint8_t*)&sr_reg_val, data_bit_len,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
//SPI command, RDCR
esp_rom_opiflash_exec_cmd(spi_num, ESP_ROM_SPIFLASH_FASTRD_MODE,
SPI_FLASH_SPI_CMD_RDCR, cmd_len,
addr, addr_bit_len,
dummy,
NULL, 0,
(uint8_t*)&cr_reg_val, data_bit_len,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
//Modify
reg_val = (((cr_reg_val & 0xf8) | strength) << 8) | sr_reg_val;
//Write
//SPI command, WRSR/WRCR
data_bit_len = 16;
esp_rom_spiflash_write_enable(&g_rom_flashchip);
esp_rom_opiflash_exec_cmd(spi_num, ESP_ROM_SPIFLASH_FASTRD_MODE,
SPI_FLASH_SPI_CMD_WRSRCR, cmd_len,
addr, addr_bit_len,
dummy,
(uint8_t*)&reg_val, data_bit_len,
NULL, 0,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
}
static void s_set_pin_drive_capability(uint8_t drv)
{
//flash clock
REG_SET_FIELD(SPI_MEM_DATE_REG(0), SPI_MEM_SPI_FMEM_SPICLK_FUN_DRV, 3);
//cs0
PIN_SET_DRV(IO_MUX_GPIO29_REG, 3);
}
static void s_flash_init_mxic(esp_rom_spiflash_read_mode_t mode)
{
#if CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR
static const esp_rom_opiflash_def_t opiflash_cmd_def_mxic = OPI_CMD_FORMAT_MXIC_STR();
#elif CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
static const esp_rom_opiflash_def_t opiflash_cmd_def_mxic = OPI_CMD_FORMAT_MXIC_DTR();
#endif
esp_rom_opiflash_legacy_driver_init(&opiflash_cmd_def_mxic);
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
// increase flash output driver strength
s_set_flash_ouput_driver_strength(1, 7);
// STR/DTR specific setting
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
#if CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR
s_set_pin_drive_capability(3);
s_set_flash_dtr_str_opi_mode(1, 0x1);
esp_rom_spiflash_cache_mode_config(mode, &rom_opiflash_cmd_def->cache_rd_cmd);
esp_rom_spi_set_dtr_swap_mode(0, false, false);
esp_rom_spi_set_dtr_swap_mode(1, false, false);
#else //CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
s_set_pin_drive_capability(3);
bool need_swap = s_mxic_dtr_need_swap();
s_set_flash_dtr_str_opi_mode(1, 0x2);
esp_rom_spiflash_cache_mode_config(mode, &rom_opiflash_cmd_def->cache_rd_cmd);
esp_rom_spi_set_dtr_swap_mode(0, need_swap, need_swap);
esp_rom_spi_set_dtr_swap_mode(1, need_swap, need_swap);
#endif
esp_rom_opiflash_wait_idle();
}
#endif // #if CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP
#if CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP
static void s_mxic_set_required_regs(uint32_t chip_id)
{
bool is_swap = false;
#if CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
is_swap = true;
#else
//STR mode does not need to enable ddr_swap registers
#endif
esp_rom_spi_set_dtr_swap_mode(0, is_swap, is_swap);
esp_rom_spi_set_dtr_swap_mode(1, is_swap, is_swap);
}
#endif
/*----------------------------------------------------------------------------------------------------
General Functions
-----------------------------------------------------------------------------------------------------*/
typedef struct opi_flash_func_t {
esp_err_t (*probe)(uint32_t flash_id, uint8_t *out_vendor_id); //Function pointer for detecting Flash chip vendor
void (*init)(esp_rom_spiflash_read_mode_t mode); //Function pointer for initialising certain Flash chips
void (*regs_set)(uint32_t flash_id); //Function pointer for setting required registers, decided by certain flash chips.
} opi_flash_func_t;
#if CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP
static const opi_flash_func_t opi_flash_func_mxic = {
.probe = &s_probe_mxic_chip,
.init = &s_flash_init_mxic,
.regs_set = &s_mxic_set_required_regs,
};
#endif
static const opi_flash_func_t *registered_chip_funcs[] = {
#if CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP
&opi_flash_func_mxic,
#endif
NULL,
};
//To check which Flash chip is used
static const opi_flash_func_t **s_chip_func = NULL;
esp_err_t esp_opiflash_init(uint32_t chip_id)
{
esp_err_t ret = ESP_FAIL;
esp_rom_spiflash_read_mode_t mode;
#if CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR
mode = ESP_ROM_SPIFLASH_OPI_STR_MODE;
#elif CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE;
#else
mode = ESP_ROM_SPIFLASH_FASTRD_MODE;
#endif
const opi_flash_func_t **chip_func = &registered_chip_funcs[0];
uint8_t vendor_id = 0;
while (*chip_func) {
ret = (*chip_func)->probe(chip_id, &vendor_id);
if (ret == ESP_OK) {
// Detect this is the supported chip type
s_chip_id = chip_id;
(*chip_func)->init(mode);
s_register_rom_function();
break;
}
chip_func++;
}
s_chip_func = chip_func;
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "No detected Flash chip, please check the menuconfig to see if the chip is supported");
abort();
}
return ESP_OK;
}
/**
* Add Flash chip specifically required MSPI register settings here
*/
void esp_opiflash_set_required_regs(void)
{
(*s_chip_func)->regs_set(s_chip_id);
}
+39
View File
@@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#include "sdkconfig.h"
#include "esp_rom_spiflash.h"
#if CONFIG_SPI_FLASH_BROWNOUT_RESET
static bool flash_brownout_needs_reset = false;
static bool flash_erasing = false;
// This function could be called in startup
void spi_flash_needs_reset_check(void)
{
// Currently only XMC is suggested to reset when brownout
#if CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC
if ((g_rom_flashchip.device_id >> 16) == 0x20) {
flash_brownout_needs_reset = true;
}
#endif
}
void spi_flash_set_erasing_flag(bool status)
{
flash_erasing = status;
}
bool spi_flash_brownout_need_reset(void)
{
return (flash_brownout_needs_reset && flash_erasing);
}
#endif //CONFIG_SPI_FLASH_BROWNOUT_RESET
+335
View File
@@ -0,0 +1,335 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <sys/param.h> // For MIN/MAX(a, b)
#include <soc/soc.h>
#include <soc/soc_memory_layout.h>
#include "soc/io_mux_reg.h"
#include "soc/spi_pins.h"
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_cpu.h"
#include "esp_log.h"
#include "esp_private/system_internal.h"
#include "esp_private/startup_internal.h"
#include "esp_private/spi_flash_os.h"
#include "esp_private/esp_clk.h"
#include "esp_private/esp_gpio_reserve.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
#include "esp32/rom/spi_flash.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "soc/spi_mem_reg.h"
#include "esp32s3/rom/opi_flash.h"
#include "esp32s3/rom/cache.h"
#include "esp_private/opi_flash_private.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/cache.h"
#endif
#include "esp_rom_spiflash.h"
#include "esp_private/mspi_timing_tuning.h"
#include "esp_private/cache_utils.h"
#include "esp_attr.h"
#include "bootloader_flash.h"
#include "bootloader_flash_config.h"
#include "esp_compiler.h"
#include "esp_rom_efuse.h"
#include "esp_rom_caps.h"
#include "soc/chip_revision.h"
#include "hal/efuse_hal.h"
#if CONFIG_SPIRAM
#include "esp_private/esp_psram_io.h"
#endif
#if SOC_MEMSPI_CLOCK_IS_INDEPENDENT
#include "hal/cache_hal.h"
#endif
/* bytes erased by SPIEraseBlock() ROM function */
#define BLOCK_ERASE_SIZE 65536
/* Limit number of bytes written/read in a single SPI operation,
as these operations disable all higher priority tasks from running.
*/
#ifdef CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE
#define MAX_WRITE_CHUNK CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE
#else
#define MAX_WRITE_CHUNK 8192
#endif // CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE
#define MAX_READ_CHUNK 16384
static const char *TAG __attribute__((unused)) = "spi_flash";
const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = {
.start = spi_flash_disable_interrupts_caches_and_other_cpu,
.end = spi_flash_enable_interrupts_caches_and_other_cpu,
};
const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = {
.start = spi_flash_disable_interrupts_caches_and_other_cpu_no_os,
.end = spi_flash_enable_interrupts_caches_no_os,
};
static const spi_flash_guard_funcs_t *s_flash_guard_ops;
void IRAM_ATTR spi_flash_guard_set(const spi_flash_guard_funcs_t *funcs)
{
s_flash_guard_ops = funcs;
}
const spi_flash_guard_funcs_t *IRAM_ATTR spi_flash_guard_get(void)
{
return s_flash_guard_ops;
}
#if CONFIG_SPI_FLASH_ROM_IMPL
#include "esp_heap_caps.h"
void IRAM_ATTR *spi_flash_malloc_internal(size_t size)
{
return heap_caps_malloc(size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
}
void IRAM_ATTR spi_flash_rom_impl_init(void)
{
spi_flash_guard_set(&g_flash_guard_default_ops);
#if ESP_ROM_HAS_SPI_FLASH_MMAP
/* These two functions are in ROM only */
extern void spi_flash_mmap_os_func_set(void *(*func1)(size_t size), void (*func2)(void *p));
spi_flash_mmap_os_func_set(spi_flash_malloc_internal, heap_caps_free);
extern esp_err_t spi_flash_mmap_page_num_init(uint32_t page_num);
spi_flash_mmap_page_num_init(128);
#endif // ESP_ROM_HAS_SPI_FLASH_MMAP
}
#endif
bool IRAM_ATTR spi_flash_is_octal_mode_enabled(void)
{
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
return efuse_ll_get_flash_type();
#else
return false;
#endif
}
void IRAM_ATTR esp_mspi_pin_init(void)
{
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
bool octal_mspi_required = spi_flash_is_octal_mode_enabled();
#if CONFIG_SPIRAM_MODE_OCT
octal_mspi_required |= true;
#endif
if (octal_mspi_required) {
esp_rom_opiflash_pin_config();
mspi_timing_set_pin_drive_strength();
}
//Set F4R4 board pin drive strength. TODO: IDF-3663
#endif
}
void esp_mspi_pin_reserve(void)
{
uint64_t reserve_pin_mask = 0;
uint8_t mspi_io;
for (esp_mspi_io_t i = 0; i < ESP_MSPI_IO_MAX; i++) {
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
if (!spi_flash_is_octal_mode_enabled()
&& i >= ESP_MSPI_IO_DQS && i <= ESP_MSPI_IO_D7) {
continue;
}
#endif
mspi_io = esp_mspi_get_io(i);
if (mspi_io < 64) { // 'reserve_pin_mask' have 64 bits length
reserve_pin_mask |= BIT64(mspi_io);
}
}
esp_gpio_reserve(reserve_pin_mask);
}
esp_err_t IRAM_ATTR spi_flash_init_chip_state(void)
{
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
if (spi_flash_is_octal_mode_enabled()) {
return esp_opiflash_init(rom_spiflash_legacy_data->chip.device_id);
}
#endif
#if CONFIG_SPI_FLASH_HPM_ON
return spi_flash_enable_high_performance_mode();
#endif // CONFIG_SPI_FLASH_HPM_ON
return ESP_OK;
}
void IRAM_ATTR spi_flash_set_rom_required_regs(void)
{
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
if (spi_flash_is_octal_mode_enabled()) {
//Disable the variable dummy mode when doing timing tuning
CLEAR_PERI_REG_MASK(SPI_MEM_DDR_REG(1), SPI_MEM_SPI_FMEM_VAR_DUMMY);
/**
* STR /DTR mode setting is done every time when `esp_rom_opiflash_exec_cmd` is called
*
* Add any registers that are not set in ROM SPI flash functions here in the future
*/
}
#endif
}
static const uint8_t s_mspi_io_num_default[] = {
MSPI_IOMUX_PIN_NUM_CLK,
MSPI_IOMUX_PIN_NUM_MISO,
MSPI_IOMUX_PIN_NUM_MOSI,
MSPI_IOMUX_PIN_NUM_CS0,
MSPI_IOMUX_PIN_NUM_HD,
MSPI_IOMUX_PIN_NUM_WP,
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
MSPI_IOMUX_PIN_NUM_DQS,
MSPI_IOMUX_PIN_NUM_D4,
MSPI_IOMUX_PIN_NUM_D5,
MSPI_IOMUX_PIN_NUM_D6,
MSPI_IOMUX_PIN_NUM_D7
#endif // SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
};
uint8_t esp_mspi_get_io(esp_mspi_io_t io)
{
#if CONFIG_SPIRAM
if (io == ESP_MSPI_IO_CS1) {
return esp_psram_io_get_cs_io();
}
#endif
assert(io >= ESP_MSPI_IO_CLK);
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
assert(io <= ESP_MSPI_IO_D7);
#else
assert(io <= ESP_MSPI_IO_WP);
#endif
#if SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE
uint8_t mspi_io = 0;
uint32_t spiconfig = 0;
if (io == ESP_MSPI_IO_WP) {
/**
* wp pad is a bit special:
* 1. since 32's efuse does not have enough bits for wp pad, so wp pad config put in flash bin header
* 2. rom code take 0x3f as invalid wp pad num, but take 0 as other invalid mspi pads num
*/
#if CONFIG_IDF_TARGET_ESP32
return bootloader_flash_get_wp_pin();
#else
spiconfig = esp_rom_efuse_get_flash_wp_gpio();
return (spiconfig == 0x3f) ? s_mspi_io_num_default[io] : spiconfig & 0x3f;
#endif
}
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
spiconfig = (io < ESP_MSPI_IO_WP) ? esp_rom_efuse_get_flash_gpio_info() : esp_rom_efuse_get_opiconfig();
#else
spiconfig = esp_rom_efuse_get_flash_gpio_info();
#endif // SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
if (spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI) {
mspi_io = s_mspi_io_num_default[io];
} else if (io < ESP_MSPI_IO_WP) {
/**
* [0 : 5] -- CLK
* [6 :11] -- Q(D1)
* [12:17] -- D(D0)
* [18:23] -- CS
* [24:29] -- HD(D3)
*/
mspi_io = (spiconfig >> io * 6) & 0x3f;
}
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
else {
/**
* [0 : 5] -- DQS
* [6 :11] -- D4
* [12:17] -- D5
* [18:23] -- D6
* [24:29] -- D7
*/
mspi_io = (spiconfig >> (io - ESP_MSPI_IO_DQS) * 6) & 0x3f;
}
#endif // SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
return mspi_io;
#else // SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE
return s_mspi_io_num_default[io];
#endif // SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE
}
#if !CONFIG_IDF_TARGET_ESP32P4 || !CONFIG_APP_BUILD_TYPE_RAM // IDF-10019
esp_err_t IRAM_ATTR esp_mspi_32bit_address_flash_feature_check(void)
{
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
ESP_EARLY_LOGE(TAG, "32bit address (flash over 16MB) has high risk on this chip");
return ESP_ERR_NOT_SUPPORTED;
#elif CONFIG_IDF_TARGET_ESP32P4
// IDF-10019
unsigned chip_version = efuse_hal_chip_revision();
if (unlikely(!ESP_CHIP_REV_ABOVE(chip_version, 1))) {
ESP_EARLY_LOGE(TAG, "32bit address (flash over 16MB) has high risk on ESP32P4 v0.0");
return ESP_ERR_NOT_SUPPORTED;
}
#endif
return ESP_OK;
}
#endif // !CONFIG_IDF_TARGET_ESP32P4 || !CONFIG_APP_BUILD_TYPE_RAM
#if CONFIG_ESP_SLEEP_SET_FLASH_DPD
static esp_err_t dpd_enter_func(void)
{
spi_flash_ll_enter_dpd(&SPIMEM_LL_APB);
while (!spi_flash_ll_cmd_is_done(&SPIMEM_LL_APB)) {
//nop
}
return ESP_OK;
}
static esp_err_t dpd_exit_func(void)
{
spi_flash_ll_exit_dpd(&SPIMEM_LL_APB);
while (!spi_flash_ll_cmd_is_done(&SPIMEM_LL_APB)) {
//nop
}
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(init_flash_dpd, CORE, BIT(0), 125)
{
// Register DPD function pointers
static spi_flash_dpd_funcs_t dpd_funcs = {
.enter_dpd = dpd_enter_func,
.exit_dpd = dpd_exit_func,
};
spi_flash_dpd_register_funcs(&dpd_funcs);
return ESP_OK;
}
#endif // CONFIG_ESP_SLEEP_SET_FLASH_DPD
void esp_mspi_flash_ops_include_func(void)
{
// Linker hook function, exists to make the linker examine this file so that
// the init_flash_dpd ESP_SYSTEM_INIT_FN above is not discarded.
}
@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Structure for flash dummy bits.
* For some flash chips, dummy bits are configurable under different conditions.
*/
typedef struct {
uint8_t dio_dummy;
uint8_t dout_dummy;
uint8_t qio_dummy;
uint8_t qout_dummy;
uint8_t fastrd_dummy;
} spi_flash_hpm_dummy_conf_t;
typedef enum {
SPI_FLASH_HPM_CMD_NEEDED, // Means that in the certain condition, flash needs to enter the high performance mode by command.
SPI_FLASH_HPM_DUMMY_NEEDED, // Means that in the certain condition, flash needs to enter the high performance mode by adjusting dummy.
SPI_FLASH_HPM_WRITE_SR_NEEDED, // Means that in the certain condition, flash needs to enter the high performance mode by writing status register.
SPI_FLASH_HPM_UNNEEDED, // Means that flash doesn't need to enter the high performance mode.
SPI_FLASH_HPM_BEYOND_LIMIT, // Means that flash has no capability to meet that condition.
} spi_flash_requirement_t;
typedef void (*spi_flash_hpm_enable_fn_t)(void);
typedef esp_err_t (*spi_flash_hpf_check_fn_t)(void);
typedef void (*spi_flash_get_chip_dummy_fn_t)(spi_flash_hpm_dummy_conf_t *dummy_conf);
typedef esp_err_t (*spi_flash_hpm_probe_fn_t)(uint32_t flash_id);
typedef spi_flash_requirement_t (*spi_flash_hpm_chip_requirement_check_t)(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperature);
typedef struct __attribute__((packed))
{
const char *method; /* Flash HPM method */
spi_flash_hpm_probe_fn_t probe;
spi_flash_hpm_chip_requirement_check_t chip_hpm_requirement_check;
spi_flash_hpm_enable_fn_t flash_hpm_enable;
spi_flash_hpf_check_fn_t flash_hpf_check;
spi_flash_get_chip_dummy_fn_t flash_get_dummy;
} spi_flash_hpm_info_t;
/**
* @brief Enum for user to select valid wrap size.
*/
typedef enum {
FLASH_WRAP_SIZE_8B = 8,
FLASH_WRAP_SIZE_16B = 16,
FLASH_WRAP_SIZE_32B = 32,
FLASH_WRAP_SIZE_64B = 64,
} spi_flash_wrap_size_t;
/**
* @brief Probe flash wrap method
*
* @param flash_id Flash chip ID
*
* @return ESP_OK: If succeed
*/
typedef esp_err_t (*spi_flash_wrap_probe_fn_t)(uint32_t flash_id);
/**
* @brief Set flash wrap
*
* @param wrap_size: wrap_size
*
* @return ESP_OK: If succeed
*/
typedef esp_err_t (*spi_flash_wrap_set_fn_t)(spi_flash_wrap_size_t wrap_size);
/**
* @brief Clear flash wrap.
*
* @return ESP_OK: If succeed
*/
typedef esp_err_t (*spi_flash_wrap_clr_fn_t)(void);
typedef struct __attribute__((packed))
{
const char *method;
spi_flash_wrap_probe_fn_t probe;
spi_flash_wrap_set_fn_t chip_wrap_set;
spi_flash_wrap_clr_fn_t chip_wrap_clr;
} spi_flash_wrap_info_t;
/**
* Array of known flash chips and method to enable flash high performance mode.
*
* Users can override this array.
*/
extern const spi_flash_hpm_info_t __attribute__((weak)) spi_flash_hpm_enable_list[];
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* This header file contains declarations of cache manipulation functions
* used both in flash_ops.c and flash_mmap.c.
*
* These functions are considered internal and are not designed to be called from applications.
*/
// Init mutex protecting access to spi_flash_* APIs
void spi_flash_init_lock(void);
// Take mutex protecting access to spi_flash_* APIs
void spi_flash_op_lock(void);
// Release said mutex
void spi_flash_op_unlock(void);
// Suspend the scheduler on both CPUs, disable cache.
// Contrary to its name this doesn't do anything with interrupts, yet.
// Interrupt disabling capability will be added once we implement
// interrupt allocation API.
void spi_flash_disable_interrupts_caches_and_other_cpu(void);
// Enable cache, enable interrupts (to be added in future), resume scheduler
void spi_flash_enable_interrupts_caches_and_other_cpu(void);
// Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs.
// This function is implied to be called when other CPU is not running or running code from IRAM.
void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(void);
// Enable cache, enable interrupts on current CPU.
// This function is implied to be called when other CPU is not running or running code from IRAM.
void spi_flash_enable_interrupts_caches_no_os(void);
// Mark the pages containing a flash region as having been
// erased or written to. This means the flash cache needs
// to be evicted before these pages can be flash_mmap()ed again,
// as they may contain stale data
//
// Only call this while holding spi_flash_op_lock()
// Returns true if cache was flushed, false otherwise
bool spi_flash_check_and_flush_cache(size_t start_addr, size_t length);
//config cache mode
#if !CONFIG_IDF_TARGET_ESP32
//config instrcutin cache size and cache block size by menuconfig
void esp_config_instruction_cache_mode(void);
//config data cache size and cache block size by menuconfig
void esp_config_data_cache_mode(void);
#endif
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
/**
* @brief enable cache wrap mode for i/d shared cache
* @param icache_wrap_enable enable cache wrap mode for i/d shared cache
* @return ESP_OK on success, ESP_FAIL otherwise
*/
esp_err_t esp_enable_cache_wrap(bool icache_wrap_enable);
#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2
/**
* @brief enable cache wrap mode for instruction cache and data cache
* @param icache_wrap_enable enable cache wrap mode for i cache
* @param dcache_wrap_enable enable cache wrap mode for d cache
* @return ESP_OK on success, ESP_FAIL otherwise
*/
esp_err_t esp_enable_cache_wrap(bool icache_wrap_enable, bool dcache_wrap_enable);
#endif
/** @brief Check at runtime if flash cache is enabled on both CPUs
*
* @return true if both CPUs have flash cache enabled, false otherwise.
*/
bool spi_flash_cache_enabled(void);
/**
* @brief Re-enable cache for the core defined as cpuid parameter.
*
* @param cpuid the core number to enable instruction cache for
*/
void spi_flash_enable_cache(uint32_t cpuid);
/**
* @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)
*/
void spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state);
/**
* @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)
*/
void spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,318 @@
/*
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* System level MSPI APIs (private)
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_rom_spiflash.h"
#include "esp_err.h"
#include "esp_bit_defs.h"
#include "soc/soc_caps.h"
#include "soc/clk_tree_defs.h"
#include "hal/spi_flash_hal.h"
#include "esp_private/spi_share_hw_ctrl.h"
#include "esp_flash_chips/spi_flash_override.h"
#ifdef __cplusplus
extern "C" {
#endif
//-------------------Chip Related------------------//
// Type of MSPI IO
typedef enum {
ESP_MSPI_IO_CLK = 0,
ESP_MSPI_IO_Q,
ESP_MSPI_IO_D,
ESP_MSPI_IO_CS0, /* cs for spi flash */
ESP_MSPI_IO_HD,
ESP_MSPI_IO_WP,
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
ESP_MSPI_IO_DQS,
ESP_MSPI_IO_D4,
ESP_MSPI_IO_D5,
ESP_MSPI_IO_D6,
ESP_MSPI_IO_D7,
#endif // SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
#if CONFIG_SPIRAM
ESP_MSPI_IO_CS1, /* cs for spi ram */
#endif
ESP_MSPI_IO_MAX, /* Maximum IO MSPI occupied */
} esp_mspi_io_t;
/**
* @brief To setup Flash chip
*/
esp_err_t spi_flash_init_chip_state(void);
/**
* @brief Check if octal flash mode is enabled in eFuse
*
* @return True if flash is in octal mode, false else
*/
bool spi_flash_is_octal_mode_enabled(void);
/**
* @brief To initislize the MSPI pins
*/
void esp_mspi_pin_init(void);
/**
* @brief Reserve MSPI IOs
*/
void esp_mspi_pin_reserve(void);
/**
* @brief Get the number of the GPIO corresponding to the given MSPI io
*
* @param[in] io MSPI io
*
* @return MSPI IO number
*/
uint8_t esp_mspi_get_io(esp_mspi_io_t io);
/**
* @brief Set SPI1 registers to make ROM functions work
* @note This function is used for setting SPI1 registers to the state that ROM SPI functions work
*/
void spi_flash_set_rom_required_regs(void);
/**
* @brief Should be only used by SPI1 Flash driver to know the necessary timing registers
* @param out_timing_config Pointer to timing_tuning parameters.
*/
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config);
/**
* @brief Get the knowledge if the Flash timing is tuned or not
*/
bool spi_flash_timing_is_tuned(void);
/**
* @brief Set Flash chip specifically required MSPI register settings here
*/
void spi_flash_set_vendor_required_regs(void);
/**
* @brief Judge whether need to reset flash when brownout.
* Set` flash_brownout_needs_reset` inside the function if really need reset.
*/
void spi_flash_needs_reset_check(void);
/**
* @brief Set flag to reset flash. set when erase chip or program chip
*
* @param bool status. True if flash is eraing. False if flash is not erasing.
*
* @return None.
*/
void spi_flash_set_erasing_flag(bool status);
/**
* @brief Judge whether need to reset flash when brownout.
*
* @return true if need reset, otherwise false.
*/
bool spi_flash_brownout_need_reset(void);
/**
* @brief Check whether esp-chip supports 32bit address properly
*
* @return ESP_OK for supported, ESP_ERR_NOT_SUPPORTED for not supported
*/
esp_err_t esp_mspi_32bit_address_flash_feature_check(void);
#if CONFIG_SPI_FLASH_HPM_ON
/**
* @brief Enable SPI flash high performance mode.
*
* @note 1. When `CONFIG_SPI_FLASH_HPM_ON` is True, caller can always call this function without taking whether the used
* frequency falls into the HPM range into consideration.
* 2. However, caller shouldn't attempt to call this function on Octal flash. `CONFIG_SPI_FLASH_HPM_ON` may be
* True when `CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT && !CONFIG_ESPTOOLPY_OCT_FLASH`
*
* @return ESP_OK if success.
*/
esp_err_t spi_flash_enable_high_performance_mode(void);
/**
* @brief Get the flash dummy through this function
* This can be used when one flash has several dummy configurations to enable the high performance mode.
* @note Don't forget to subtract one when assign to the register of mspi e.g. if the value you get is 4, (4-1=3) should be assigned to the register.
*
* @return Pointer to spi_flash_hpm_dummy_conf_t.
*/
const spi_flash_hpm_dummy_conf_t *spi_flash_hpm_get_dummy(void);
/**
* @brief Used to judge whether flash works under HPM mode with dummy adjustment.
*
* @return true Yes, and work under HPM with adjusting dummy. Otherwise, false.
*/
bool spi_flash_hpm_dummy_adjust(void);
#endif //CONFIG_SPI_FLASH_HPM_ON
#if CONFIG_ESP_SLEEP_SET_FLASH_DPD
//-------------------DPD Related------------------//
/**
* @brief Structure holding DPD function pointers
*/
typedef struct {
esp_err_t (*enter_dpd)(void);
esp_err_t (*exit_dpd)(void);
} spi_flash_dpd_funcs_t;
/**
* @brief Register DPD function pointers
*
* @param dpd_funcs Pointer to structure holding DPD function pointers
* @return ESP_OK on success
*/
esp_err_t spi_flash_dpd_register_funcs(spi_flash_dpd_funcs_t *dpd_funcs);
/**
* @brief Get the duration of entering deep power-down mode.
*
* @return Entering deep power-down mode time(tDp), in microseconds.
*/
uint32_t spi_flash_dpd_get_enter_duration(void);
/**
* @brief Get the duration of exiting deep power-down mode.
*
* @return Exiting deep power-down mode time(tRES1), in microseconds.
*/
uint32_t spi_flash_dpd_get_exit_duration(void);
/**
* @brief Enable or disable SPI flash deep power-down mode.
*
* @param enable True to enter deep power-down mode, false to exit.
* @param wait_delay If true, wait tDP (enter) or tRES1 (exit) after the command.
* If false, skip the delay; the caller must ensure timing is met elsewhere
*
* @note If using self-provided flash (not the chip's factory-default flash), consult its datasheet to use this API safely.
*
* @return ESP_OK if success.
*/
esp_err_t spi_flash_enable_deep_power_down_mode(bool enable, bool wait_delay);
#endif
#if SOC_SPI_MEM_SUPPORT_WRAP
/**
* @brief set wrap size of flash
*
* @param wrap_size: wrap mode support disable, 16 32, 64 byte
*
* @return esp_err_t : ESP_OK for successful.
*
*/
esp_err_t spi_flash_wrap_enable(spi_flash_wrap_size_t wrap_size);
/**
* @brief Probe flash wrap method
*
* @return esp_err_t: ESP_OK for success
*/
esp_err_t spi_flash_wrap_probe(void);
/**
* @brief disable cache wrap
*/
esp_err_t spi_flash_wrap_disable(void);
/**
* @brief Check whether flash and esp chip supports wrap mode.
*
* @param wrap_size wrap size.
* @return true: wrap support, otherwise, false.
*/
bool spi_flash_support_wrap_size(uint32_t wrap_size);
#endif //SOC_SPI_MEM_SUPPORT_WRAP
/**
* @brief SPI flash critical section enter function.
*
*/
typedef void (*spi_flash_guard_start_func_t)(void);
/**
* @brief SPI flash critical section exit function.
*/
typedef void (*spi_flash_guard_end_func_t)(void);
/**
* Structure holding SPI flash access critical sections management functions.
*
* Flash API uses two types of flash access management functions:
* 1) Functions which prepare/restore flash cache and interrupts before calling
* appropriate ROM functions (SPIWrite, SPIRead and SPIEraseBlock):
* - 'start' function should disables flash cache and non-IRAM interrupts and
* is invoked before the call to one of ROM function above.
* - 'end' function should restore state of flash cache and non-IRAM interrupts and
* is invoked after the call to one of ROM function above.
* These two functions are not recursive.
*
* Different versions of the guarding functions should be used depending on the context of
* execution (with or without functional OS). In normal conditions when flash API is called
* from task the functions use OS primitives. When there is no OS at all or when
* it is not guaranteed that OS is functional (accessing flash from exception handler) these
* functions cannot use OS primitives or even does not need them (multithreaded access is not possible).
*
* @note Structure and corresponding guard functions should not reside in flash.
* For example structure can be placed in DRAM and functions in IRAM sections.
*/
typedef struct {
spi_flash_guard_start_func_t start; /**< critical section start function. */
spi_flash_guard_end_func_t end; /**< critical section end function. */
} spi_flash_guard_funcs_t;
/**
* @brief Sets guard functions to access flash.
*
* @note Pointed structure and corresponding guard functions should not reside in flash.
* For example structure can be placed in DRAM and functions in IRAM sections.
*
* @param funcs pointer to structure holding flash access guard functions.
*/
void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs);
/**
* @brief Get the guard functions used for flash access
*
* @return The guard functions that were set via spi_flash_guard_set(). These functions
* can be called if implementing custom low-level SPI flash operations.
*/
const spi_flash_guard_funcs_t *spi_flash_guard_get(void);
/**
* @brief Default OS-aware flash access guard functions
*/
extern const spi_flash_guard_funcs_t g_flash_guard_default_ops;
/**
* @brief Non-OS flash access guard functions
*
* @note This version of flash guard functions is to be used when no OS is present or from panic handler.
* It does not use any OS primitives and IPC and implies that only calling CPU is active.
*/
extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops;
/**
* @brief This function is used to re-initialize the flash mmap when using ROM flash
* implementations.
*
* @note Only called in startup. User should not call this function.
*/
void spi_flash_rom_impl_init(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#pragma once
// SPI commands
#define CMD_RDID 0x9F
#define CMD_RDUID 0x4B /* Read the flash unique ID*/
#define CMD_WRSR 0x01
#define SR_WIP (1<<0) /* Status register write-in-progress bit */
#define SR_WREN (1<<1) /* Status register write enable bit */
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WRSR3 0x11 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRENVSR 0x50 /* Flash write enable for volatile SR bits */
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
#define CMD_RDSR3 0x15 /* Not all SPI flash uses this command */
#define CMD_OTPEN 0x3A /* Enable OTP mode, not all SPI flash uses this command */
#define CMD_RDSCUR 0x2B /* MXIC-specific, read security register */
#define CMD_8READ 0xEC /* MXIC-specific, 8 I/O read */
#define CMD_8DTRD 0xEE /* MXIC-specific, 8 I/O DTR read */
#define CMD_RDFR 0x48 /* ISSI-specific, read function register */
#define CMD_FASTRD_QIO 0xEB
#define CMD_FASTRD_QIO_4B 0xEC
#define CMD_FASTRD_QUAD 0x6B
#define CMD_FASTRD_QUAD_4B 0x6C
#define CMD_FASTRD_DIO 0xBB
#define CMD_FASTRD_DIO_4B 0xBC
#define CMD_FASTRD_DUAL 0x3B
#define CMD_FASTRD_DUAL_4B 0x3C
#define CMD_FASTRD 0x0B
#define CMD_FASTRD_4B 0x0C
#define CMD_READ 0x03 /* Speed limited */
#define CMD_READ_4B 0x13 /* Speed limited */
#define CMD_SLOWRD_4B 0x13
#define CMD_CHIP_ERASE 0xC7
#define CMD_SECTOR_ERASE 0x20
#define CMD_SECTOR_ERASE_4B 0x21
#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */
#define CMD_LARGE_BLOCK_ERASE_4B 0xDC /* 64KB block erase command */
#define CMD_PROGRAM_PAGE 0x02
#define CMD_PROGRAM_PAGE_4B 0x12
#define CMD_SUSPEND 0x75
#define CMD_RESUME 0x7A
#define CMD_HPMEN 0xA3 /* Enable High Performance mode on flash */
#define CMD_WRAP 0x77
#define CMD_BURST_RD 0xC0 /* wrap(0x77) and burst read are functionally same. But commands and formats is different */
#define CMD_RST_EN 0x66
#define CMD_RST_DEV 0x99
#define CMD_RESETEN 0x66
#define CMD_RESET 0x99
#define CMD_RDSFDP 0x5A /* Read the SFDP of the flash */
// Flash vendors.
#define SPI_FLASH_GD 0xC8
#define SPI_FLASH_ISSI 0x9D
#define SPI_FLASH_MXIC 0xC2
#define SPI_FLASH_XMC_1 0x20
#define SPI_FLASH_XMC_2 0x46
#define SPI_FLASH_WINBOND 0xEF
#define SPI_FLASH_TH 0xCD
#define SPI_FLASH_BY 0x68
#define SPI_FLASH_DIO_DUMMY_BITLEN 4
#define SPI_FLASH_QIO_DUMMY_BITLEN 6
#define SPI_FLASH_DIO_ADDR_BITLEN 24
#define SPI_FLASH_QIO_ADDR_BITLEN 24
#define SPI_FLASH_QOUT_ADDR_BITLEN 24
#define SPI_FLASH_QOUT_DUMMY_BITLEN 8
#define SPI_FLASH_DOUT_ADDR_BITLEN 24
#define SPI_FLASH_DOUT_DUMMY_BITLEN 8
#define SPI_FLASH_FASTRD_ADDR_BITLEN 24
#define SPI_FLASH_FASTRD_DUMMY_BITLEN 8
#define SPI_FLASH_SLOWRD_ADDR_BITLEN 24
#define SPI_FLASH_SLOWRD_DUMMY_BITLEN 0
#define SPI_FLASH_OPISTR_ADDR_BITLEN 32
#define SPI_FLASH_OPISTR_DUMMY_BITLEN 20
#define SPI_FLASH_OPIDTR_ADDR_BITLEN 32
#define SPI_FLASH_OPIDTR_DUMMY_BITLEN 40
#define SPI_FLASH_QIO_HPM_DUMMY_BITLEN 10
#define SPI_FLASH_DIO_HPM_DUMMY_BITLEN 8
+38
View File
@@ -0,0 +1,38 @@
[mapping:esp_mspi]
archive: libesp_mspi.a
entries:
if APP_BUILD_TYPE_PURE_RAM_APP = n:
flash_brownout_hook (noflash)
if SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM = y:
spi_flash_wrap (noflash)
if ESPTOOLPY_OCT_FLASH = y || ESPTOOLPY_FLASH_MODE_AUTO_DETECT = y:
spi_flash_oct_flash_init (noflash)
if SPI_FLASH_HPM_ON = y:
spi_flash_hpm_enable (noflash)
if ESP_SLEEP_SET_FLASH_DPD = y:
spi_flash_dpd_enable (noflash)
flash_ops: dpd_enter_func (noflash)
flash_ops: dpd_exit_func (noflash)
if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD = y:
flash_ops: esp_mspi_get_io (noflash)
flash_ops: s_mspi_io_num_default (noflash)
[mapping:mspi_timing_tuning_driver]
archive: libesp_mspi.a
entries:
if APP_BUILD_TYPE_PURE_RAM_APP = n:
mspi_timing_tuning (noflash)
if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY = y:
mspi_timing_by_mspi_delay (noflash)
mspi_timing_config (noflash)
if SOC_MEMSPI_TIMING_TUNING_BY_DQS = y:
mspi_timing_by_dqs (noflash)
if SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY = y:
mspi_timing_by_flash_delay (noflash)
if SOC_MEMSPI_TIMING_TUNING_BY_DQS = y || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY = y:
mspi_timing_config (noflash)
+12
View File
@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
bool spi_flash_cache_enabled(void)
{
return true;
}
@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief MSPI ISR
*/
typedef struct mspi_isr_s {
/**
* @brief MSPI PSRAM ISR
*
* @param[in] arg Argument to the ISR
* @param[in] intr_events Interrupt events
*/
void (*psram_isr)(void *arg, uint32_t intr_events);
/**
* @brief MSPI Flash ISR
*
* @param[in] arg Argument to the ISR
* @param[in] intr_events Interrupt events
*/
void (*flash_isr)(void *arg, uint32_t intr_events);
} mspi_isr_t;
/**
* @brief Register MSPI interrupt
* @note ISR dispatcher will decide if abort, dispatched ISRs should not abort
*
* This ISR mainly:
* - Report MSPI bus errors, e.g. fifo overflow, underflow.
* - ECC error, which will be useful for a Nand flash replacement mechanism.
* - etc.
*
* @return ESP_OK on success, otherwise an error code
*/
esp_err_t esp_mspi_register_isr(mspi_isr_t *isr);
/**
* @brief Unregister MSPI interrupt
*
* @return ESP_OK on success, otherwise an error code
*/
esp_err_t esp_mspi_unregister_isr(void);
#ifdef __cplusplus
}
#endif
+140
View File
@@ -0,0 +1,140 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <sys/param.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_intr_alloc.h"
#include "hal/mspi_ll.h"
#include "hal/mspi_periph.h"
#include "esp_private/startup_internal.h"
#include "esp_private/mspi_intr.h"
#if MSPI_LL_INTR_EVENT_SUPPORTED && MSPI_LL_INTR_SHARED
#if CONFIG_ESP_PANIC_HANDLER_IRAM
#define MSPI_ISR_ATTR IRAM_ATTR
#define MSPI_ISR_FLAGS ESP_INTR_FLAG_IRAM
#else
#define MSPI_ISR_ATTR
#define MSPI_ISR_FLAGS 0
#endif
ESP_LOG_ATTR_TAG_DRAM(TAG, "mspi_intr");
static intr_handle_t s_intr_handle = NULL;
static volatile mspi_isr_t s_isr = {
NULL,
NULL,
};
static void MSPI_ISR_ATTR mspi_isr_handler(void *arg)
{
uint32_t intr_events = mspi_ll_get_intr_raw(MSPI_TIMING_LL_MSPI_ID_0);
mspi_ll_clear_intr(MSPI_TIMING_LL_MSPI_ID_0, intr_events);
ESP_DRAM_LOGE(TAG, "MSPI error");
ESP_DRAM_LOGD(TAG, "intr_events: 0x%" PRIx32, intr_events);
bool is_ecc_error = false;
#if MSPI_LL_ECC_INT_SUPPORTED
if (intr_events & MSPI_LL_EVENT_ECC_ERR) {
ESP_DRAM_LOGE(TAG, "ecc error");
is_ecc_error = true;
}
#endif
#if MSPI_LL_PMS_INT_SUPPORTED
if (intr_events & MSPI_LL_EVENT_PMS_REJECT) {
ESP_DRAM_LOGE(TAG, "pms reject");
}
#endif
#if MSPI_LL_ADDR_INT_SUPPORTED
if (intr_events & MSPI_LL_EVENT_AXI_RADDR_ERR) {
ESP_DRAM_LOGE(TAG, "read address invalid or misaligned");
}
if (intr_events & MSPI_LL_EVENT_AXI_WADDR_ERR) {
ESP_DRAM_LOGE(TAG, "write addr error");
}
if (intr_events & MSPI_LL_EVENT_AXI_WR_FLASH_ERR) {
ESP_DRAM_LOGE(TAG, "write flash error");
}
#endif
#if MSPI_LL_THRESH_INT_SUPPORTED
if (intr_events & MSPI_LL_EVENT_RX_TRANS_OVF) {
ESP_DRAM_LOGE(TAG, "rx trans overflow");
}
if (intr_events & MSPI_LL_EVENT_TX_TRANS_UDF) {
ESP_DRAM_LOGE(TAG, "tx trans underflow");
}
#endif
if (s_isr.psram_isr) {
s_isr.psram_isr(arg, intr_events);
}
if (s_isr.flash_isr) {
s_isr.flash_isr(arg, intr_events);
}
// For ecc error, will handle in the flash/psram isr
if (!is_ecc_error) {
abort();
}
//no yield for now
}
esp_err_t esp_mspi_register_isr(mspi_isr_t *isr)
{
esp_err_t ret = ESP_FAIL;
if (isr && isr->psram_isr) {
s_isr.psram_isr = isr->psram_isr;
}
if (isr && isr->flash_isr) {
s_isr.flash_isr = isr->flash_isr;
}
if (!s_intr_handle) {
ret = esp_intr_alloc(mspi_hw_info.instances[MSPI_TIMING_LL_MSPI_ID_0].irq,
MSPI_ISR_FLAGS,
mspi_isr_handler,
NULL,
&s_intr_handle);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to allocate MSPI flash interrupt");
mspi_ll_clear_intr(MSPI_TIMING_LL_MSPI_ID_0, MSPI_LL_EVENT_MASK);
mspi_ll_enable_intr(MSPI_TIMING_LL_MSPI_ID_0, MSPI_LL_EVENT_MASK, true);
}
return ESP_OK;
}
esp_err_t esp_mspi_unregister_isr(void)
{
esp_err_t ret = ESP_FAIL;
if (s_intr_handle == NULL) {
ESP_EARLY_LOGE(TAG, "MSPI interrupt not registered");
return ESP_ERR_INVALID_STATE;
}
ret = esp_intr_free(s_intr_handle);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to free MSPI interrupt");
s_isr.psram_isr = NULL;
s_isr.flash_isr = NULL;
return ret;
}
#endif //#if MSPI_LL_INTR_EVENT_SUPPORTED && MSPI_LL_INTR_SHARED
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_private/mspi_timing_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/*-------------------------------------------------------------------------------------------------
* Generic Config APIs
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Set Flash module clock
*
* @param flash_freq_mhz Flash clock frequency in MHz
* @param speed_mode Speed mode
* @param control_both_mspi Control SPI1 as well
*/
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi);
/**
* @brief Set PSRAM module clock
*
* @param psram_freq_mhz PSRAM clock frequency in MHz
* @param speed_mode Speed mode
* @param control_both_mspi Not used, for compatibility
*/
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
* This file is for MSPI timinig tuning private APIs
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Make MSPI work under 20Mhz, remove the timing tuning required delays.
* @param control_spi1 Select whether to control SPI1. For tuning, we need to use SPI1. After tuning (during startup stage), let the flash driver to control SPI1
*/
void mspi_timing_enter_low_speed_mode(bool control_spi1);
/**
* @brief Get PSRAM frequency in low speed mode (MHz)
* @return PSRAM frequency in MHz when in low speed mode
*/
uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void);
/**
* @brief Make MSPI work under the frequency as users set, may add certain delays to MSPI RX direction to meet timing requirements.
* @param control_spi1 Select whether to control SPI1. For tuning, we need to use SPI1. After tuning (during startup stage), let the flash driver to control SPI1
*/
void mspi_timing_enter_high_speed_mode(bool control_spi1);
/**
* @brief Switch MSPI to low speed while suspending external memory cache to avoid in-flight cache line fills across the
* clock change.
*
* @note Early init only. Not safe for general runtime use: does not coordinate with other cores or freeze cache.
*/
void mspi_timing_enter_low_speed_early(void);
/**
* @brief Switch MSPI to high speed while suspending external memory cache.
*
* @note Same usage constraints as @ref mspi_timing_enter_low_speed_early.
*/
void mspi_timing_enter_high_speed_early(void);
/**
* @brief Switch MSPI into low speed mode / high speed mode.
* @note This API is cache safe, it will freeze both D$ and I$ and restore them after MSPI is switched
* @note For some of the MSPI high frequency settings (e.g. 80M DDR mode Flash or PSRAM), timing tuning is required.
* Certain delays will be added to the MSPI RX direction. When CPU clock switches from PLL to XTAL, should call
* this API first to enter MSPI low speed mode to remove the delays, and vice versa.
*/
void mspi_timing_change_speed_mode_cache_safe(bool switch_down);
/**
* @brief Tune MSPI flash timing to make it work under high frequency
*/
void mspi_timing_flash_tuning(void);
/**
* @brief Tune MSPI psram timing to make it work under high frequency
*/
void mspi_timing_psram_tuning(void);
/**
* @brief Set MSPI pin default pin drive
*/
void mspi_timing_set_pin_drive_strength(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
* This file is for MSPI timinig tuning private APIs
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief MSPI Timing speed mode
*/
typedef enum {
MSPI_TIMING_SPEED_MODE_LOW_PERF, /*!< Low performance speed mode, this mode is safe for all the scenarios,
unless the MSPI attached devices (Flash, PSRAM) are powered down.
As a tradeoff, the performance of the MSPI (devices) are switched to a very low speed */
MSPI_TIMING_SPEED_MODE_NORMAL_PERF, /*!< Normal performance speed mode, MSPI speed is the same as you configured in menuconfig */
} mspi_timing_speed_mode_t;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,735 @@
/*
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/io_mux_reg.h"
#include "soc/soc.h"
#include "hal/spi_flash_hal.h"
#include "hal/mspi_ll.h"
#if !ESP_TEE_BUILD
#include "esp_private/esp_cache_private.h"
#include "hal/cache_ll.h"
#else
#include "hal/cache_ll.h"
#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"
#include "esp_private/mspi_timing_by_dqs.h"
#include "esp_private/mspi_timing_by_flash_delay.h"
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY || SOC_MEMSPI_TIMING_TUNING_BY_DQS || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
#include "mspi_timing_tuning_configs.h"
#endif
#if SOC_MEMSPI_CLK_SRC_IS_INDEPENDENT
#include "hal/spimem_flash_ll.h"
#endif
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_ipc_isr.h"
#endif
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define FLASH_FREQUENCY_MHZ 120
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define FLASH_FREQUENCY_MHZ 80
#elif CONFIG_ESPTOOLPY_FLASHFREQ_64M
#define FLASH_FREQUENCY_MHZ 64
#elif CONFIG_ESPTOOLPY_FLASHFREQ_60M
#define FLASH_FREQUENCY_MHZ 60
#elif CONFIG_ESPTOOLPY_FLASHFREQ_48M
#define FLASH_FREQUENCY_MHZ 48
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define FLASH_FREQUENCY_MHZ 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_32M
#define FLASH_FREQUENCY_MHZ 32
#elif CONFIG_ESPTOOLPY_FLASHFREQ_30M
#define FLASH_FREQUENCY_MHZ 30
#elif CONFIG_ESPTOOLPY_FLASHFREQ_26M
#define FLASH_FREQUENCY_MHZ 26
#elif CONFIG_ESPTOOLPY_FLASHFREQ_24M
#define FLASH_FREQUENCY_MHZ 24
#elif CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define FLASH_FREQUENCY_MHZ 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_16M
#define FLASH_FREQUENCY_MHZ 16
#elif CONFIG_ESPTOOLPY_FLASHFREQ_15M
#define FLASH_FREQUENCY_MHZ 15
#endif
/**
* @brief MSPI timing tuning type
*/
typedef enum {
MSPI_TIMING_TUNING_MSPI_DIN_DUMMY, //tune by mspi din and dummy
MSPI_TIMING_TUNING_MSPI_DQS_PHASE, //tune by mspi dqs phase
MSPI_TIMING_TUNING_MSPI_DELAYLINE, //tune by mspi delayline
} mspi_timing_tuning_t;
typedef struct mspi_tuning_cfg_drv_s mspi_tuning_cfg_drv_t;
__attribute__((unused)) const static char *TAG = "MSPI Timing";
struct mspi_tuning_cfg_drv_s {
/**
* @brief Flash tuning scheme type
*/
mspi_timing_tuning_t flash_tuning_type;
/**
* @brief How many test times during sweep
*/
uint32_t sweep_test_nums;
/**
* @brief Init MSPI for Flash timing tuning
*
* @param[in] flash_freq_mhz Flash frequency in MHz
*/
void (*flash_init_mspi)(uint32_t flash_freq_mhz);
/**
* @brief Configure MSPI for Flash timing tuning
*
* @param[in] configs Timing tuning configs
* @param[in] id Config ID
*/
void (*flash_tune_mspi)(const void *configs, uint8_t id);
/**
* @brief Flash read
*
* @param[in] buf Read buffer
* @param[in] addr Read address
* @param[in] len Read length
*/
void (*flash_read)(uint8_t *buf, uint32_t addr, uint32_t len);
/**
* @brief Select best tuning configs for Flash
*
* @param[in] configs Timing tuning configurations
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t (*flash_select_best_tuning_config)(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best Flash tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning configs
* @param[in] best_id Best config ID
*/
void (*flash_set_best_tuning_config)(const void *configs, uint8_t best_id);
/**
* @brief PSRAM tuning scheme type
*/
mspi_timing_tuning_t psram_tuning_type;
/**
* @brief Init MSPI for PSRAM timing tuning
*
* @param[in] flash_freq_mhz PSRAM frequency in MHz
*/
void (*psram_init_mspi)(uint32_t psram_freq_mhz);
/**
* @brief Configure MSPI for PSRAM timing tuning
*
* @param[in] configs Timing tuning configs
* @param[in] id Config ID
*/
void (*psram_tune_mspi)(const void *configs, uint8_t id);
/**
* @brief PSRAM read
*
* @param[in] buf Read buffer
* @param[in] addr Read address
* @param[in] len Read length
*/
void (*psram_read)(uint8_t *buf, uint32_t addr, uint32_t len);
/**
* @brief Select best tuning configs for PSRAM
*
* @param[in] configs Timing tuning configurations
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t (*psram_select_best_tuning_config)(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best PSRAM tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning config
* @param[in] best_id Best config ID
*/
void (*psram_set_best_tuning_config)(const void *configs, uint8_t best_id);
};
static mspi_tuning_cfg_drv_t s_tuning_cfg_drv = {};
void s_register_config_driver(mspi_tuning_cfg_drv_t *cfg_drv, bool is_flash)
{
if (is_flash) {
s_tuning_cfg_drv.flash_tuning_type = cfg_drv->flash_tuning_type;
s_tuning_cfg_drv.flash_init_mspi = cfg_drv->flash_init_mspi;
s_tuning_cfg_drv.flash_tune_mspi = cfg_drv->flash_tune_mspi;
s_tuning_cfg_drv.flash_read = cfg_drv->flash_read;
s_tuning_cfg_drv.flash_select_best_tuning_config = cfg_drv->flash_select_best_tuning_config;
s_tuning_cfg_drv.flash_set_best_tuning_config = cfg_drv->flash_set_best_tuning_config;
} else {
s_tuning_cfg_drv.psram_tuning_type = cfg_drv->psram_tuning_type;
s_tuning_cfg_drv.psram_init_mspi = cfg_drv->psram_init_mspi;
s_tuning_cfg_drv.psram_tune_mspi = cfg_drv->psram_tune_mspi;
s_tuning_cfg_drv.psram_read = cfg_drv->psram_read;
s_tuning_cfg_drv.psram_select_best_tuning_config = cfg_drv->psram_select_best_tuning_config;
s_tuning_cfg_drv.psram_set_best_tuning_config = cfg_drv->psram_set_best_tuning_config;
}
s_tuning_cfg_drv.sweep_test_nums = cfg_drv->sweep_test_nums;
}
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
/**
* We use different MSPI timing tuning config to read data to see if current MSPI sampling is successful.
* The sampling result will be stored in an array. In this array, successful item will be 1, failed item will be 0.
*/
static void s_sweep_for_success_sample_points(uint8_t *reference_data, void *config, bool is_flash, uint32_t *out_array)
{
const mspi_timing_config_t *timing_config = (const mspi_timing_config_t *)config;
uint32_t config_idx = 0;
uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0};
for (config_idx = 0; config_idx < timing_config->available_config_num; config_idx++) {
out_array[config_idx] = 0;
for (int i = 0; i < s_tuning_cfg_drv.sweep_test_nums; i++) {
memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN);
#if MSPI_TIMING_FLASH_NEEDS_TUNING
if (is_flash) {
s_tuning_cfg_drv.flash_tune_mspi(timing_config, config_idx);
s_tuning_cfg_drv.flash_read(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(read_data));
}
#endif
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
if (!is_flash) {
s_tuning_cfg_drv.psram_tune_mspi(timing_config, config_idx);
s_tuning_cfg_drv.psram_read(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN);
}
#endif
if (memcmp(reference_data, read_data, sizeof(read_data)) == 0) {
out_array[config_idx] += 1;
ESP_DRAM_LOGV(TAG, "config_idx: %d, good", config_idx);
} else {
ESP_DRAM_LOGV(TAG, "config_idx: %d, bad", config_idx);
}
}
}
ESP_DRAM_LOGD(TAG, "test nums: %" PRIu32 ", test result: [id][good/bad][good_times]:", s_tuning_cfg_drv.sweep_test_nums);
for (config_idx = 0; config_idx < timing_config->available_config_num; config_idx++) {
ESP_DRAM_LOGD(TAG, "[%"PRIu32"][%s][%" PRIu32 "] ", config_idx, out_array[config_idx] == s_tuning_cfg_drv.sweep_test_nums ? "good" : "bad", out_array[config_idx]);
}
}
/**
* Find consecutive successful sampling points.
* e.g. array: {1, 1, 0, 0, 1, 1, 1, 0}
* out_length: 3
* outout_end_index: 6
*/
static void s_find_max_consecutive_success_points(uint32_t *array, uint32_t size, uint32_t *out_length, uint32_t *out_end_index)
{
uint32_t max = 0;
uint32_t match_num = 0;
uint32_t i = 0;
uint32_t end = 0;
while (i < size) {
if (array[i] == s_tuning_cfg_drv.sweep_test_nums) {
match_num++;
} else {
if (match_num > max) {
max = match_num;
end = i - 1;
}
match_num = 0;
}
i++;
}
/**
* this is to deal with the case when the last points are consecutive 1, e.g.
* {1, 0, 0, 1, 1, 1, 1, 1, 1}
*/
if (match_num > max) {
max = match_num;
end = i - 1;
}
*out_length = max;
*out_end_index = end;
}
static void s_select_best_tuning_config(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash)
{
const mspi_timing_config_t *timing_config = (const mspi_timing_config_t *)config;
uint32_t best_point = 0;
if (is_flash) {
#if MSPI_TIMING_FLASH_DTR_MODE
best_point = s_tuning_cfg_drv.flash_select_best_tuning_config(timing_config, consecutive_length, end, reference_data, IS_DDR);
#elif MSPI_TIMING_FLASH_STR_MODE
best_point = s_tuning_cfg_drv.flash_select_best_tuning_config(timing_config, consecutive_length, end, NULL, IS_SDR);
#endif
s_tuning_cfg_drv.flash_set_best_tuning_config(timing_config, best_point);
} else {
#if MSPI_TIMING_PSRAM_DTR_MODE
best_point = s_tuning_cfg_drv.psram_select_best_tuning_config(timing_config, consecutive_length, end, reference_data, IS_DDR);
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
mspi_timing_setting_temperature_adjustment_best_point(best_point);
#endif
#elif MSPI_TIMING_PSRAM_STR_MODE
best_point = s_tuning_cfg_drv.psram_select_best_tuning_config(timing_config, consecutive_length, end, NULL, IS_SDR);
#endif
s_tuning_cfg_drv.psram_set_best_tuning_config(timing_config, best_point);
}
}
static void s_do_tuning(uint8_t *reference_data, mspi_timing_config_t *config, bool is_flash)
{
/**
* We use MSPI to tune the timing:
* 1. Get all MSPI sampling results.
* 2. Find the longest consecutive successful sampling points from the result above.
* 3. The middle one will be the best sampling point.
*/
uint32_t consecutive_length = 0;
uint32_t last_success_point = 0;
uint32_t sample_result[MSPI_TIMING_CONFIG_NUM_MAX] = {0};
#if MSPI_TIMING_FLASH_NEEDS_TUNING
if (is_flash) {
s_tuning_cfg_drv.flash_init_mspi(FLASH_FREQUENCY_MHZ);
}
#endif
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
if (!is_flash) {
s_tuning_cfg_drv.psram_init_mspi(CONFIG_SPIRAM_SPEED);
}
#endif
s_sweep_for_success_sample_points(reference_data, config, is_flash, sample_result);
s_find_max_consecutive_success_points(sample_result, config->available_config_num, &consecutive_length, &last_success_point);
s_select_best_tuning_config(config, consecutive_length, last_success_point, reference_data, is_flash);
}
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
/*------------------------------------------------------------------------------
* FLASH Timing Tuning
*----------------------------------------------------------------------------*/
#if MSPI_TIMING_FLASH_NEEDS_TUNING
void mspi_timing_flash_tuning(void)
{
/**
* set MSPI related regs to 20mhz configuration, to get reference data from FLASH
* see detailed comments in this function (`mspi_timing_enter_low_speed_early`)
*/
ESP_EARLY_LOGI(TAG, "Enter flash timing tuning");
mspi_timing_enter_low_speed_early();
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
mspi_tuning_cfg_drv_t drv = {
.flash_tuning_type = MSPI_TIMING_TUNING_MSPI_DIN_DUMMY,
.sweep_test_nums = 1,
.flash_init_mspi = mspi_timing_flash_init,
.flash_tune_mspi = mspi_timing_config_flash_set_tuning_regs,
.flash_read = mspi_timing_config_flash_read_data,
.flash_select_best_tuning_config = mspi_timing_flash_select_best_tuning_config,
.flash_set_best_tuning_config = mspi_timing_flash_set_best_tuning_config,
};
bool is_flash = true;
s_register_config_driver(&drv, is_flash);
//Disable the variable dummy mode when doing timing tuning
mspi_timing_ll_enable_flash_variable_dummy(1, false); //GD flash will read error in variable mode with 20MHz
uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN] = {0};
s_tuning_cfg_drv.flash_read(reference_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(reference_data));
mspi_timing_config_t timing_configs = {0};
mspi_timing_get_flash_tuning_configs(&timing_configs);
#endif //SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
s_do_tuning(reference_data, &timing_configs, true);
mspi_timing_enter_high_speed_early();
}
#else
void mspi_timing_flash_tuning(void)
{
//Empty function for compatibility, therefore upper layer won't need to know that FLASH in which operation mode and frequency config needs to be tuned
}
#endif //MSPI_TIMING_FLASH_NEEDS_TUNING
/*------------------------------------------------------------------------------
* PSRAM Timing Tuning
*----------------------------------------------------------------------------*/
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
void mspi_timing_psram_tuning(void)
{
/**
* set MSPI related regs to 20mhz configuration, to write reference data to PSRAM
* see detailed comments in this function (`mspi_timing_enter_low_speed_early`)
*/
ESP_EARLY_LOGI(TAG, "Enter psram timing tuning");
mspi_timing_enter_low_speed_early();
// write data into psram, used to do timing tuning test.
uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN];
mspi_timing_config_psram_prepare_reference_data(reference_data, MSPI_TIMING_TEST_DATA_LEN);
mspi_timing_config_psram_write_data(reference_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN);
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
mspi_tuning_cfg_drv_t drv = {
.psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DIN_DUMMY,
.sweep_test_nums = 1,
.psram_init_mspi = mspi_timing_psram_init,
.psram_tune_mspi = mspi_timing_config_psram_set_tuning_regs,
.psram_read = mspi_timing_config_psram_read_data,
.psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_config,
.psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_config,
};
bool is_flash = false;
s_register_config_driver(&drv, is_flash);
mspi_timing_config_t timing_configs = {};
mspi_timing_get_psram_tuning_configs(&timing_configs);
//Disable the variable dummy mode when doing timing tuning
mspi_timing_ll_enable_flash_variable_dummy(1, false);
//Get required config, and set them to PSRAM related registers
s_do_tuning(reference_data, &timing_configs, false);
#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
#if SOC_MEMSPI_TIMING_TUNING_BY_DQS
bool is_flash = false;
mspi_timing_config_t timing_configs = {};
//Phase
mspi_tuning_cfg_drv_t drv = {
.psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DQS_PHASE,
.sweep_test_nums = 1,
.psram_init_mspi = mspi_timing_psram_init,
.psram_tune_mspi = mspi_timing_config_psram_set_tuning_phase,
.psram_read = mspi_timing_config_psram_read_data,
.psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_phase,
.psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_phase,
};
s_register_config_driver(&drv, is_flash);
mspi_timing_get_psram_tuning_phases(&timing_configs);
s_do_tuning(reference_data, &timing_configs, false);
//Delayline
drv = (mspi_tuning_cfg_drv_t) {
.psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DELAYLINE,
.sweep_test_nums = MSPI_TIMING_DELAYLINE_TEST_NUMS,
.psram_init_mspi = mspi_timing_psram_init,
.psram_tune_mspi = mspi_timing_config_psram_set_tuning_delayline,
.psram_read = mspi_timing_config_psram_read_data,
.psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_delayline,
.psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_delayline,
};
s_register_config_driver(&drv, is_flash);
mspi_timing_get_psram_tuning_delaylines(&timing_configs);
s_do_tuning(reference_data, &timing_configs, false);
#endif
mspi_timing_enter_high_speed_early();
}
#else
void mspi_timing_psram_tuning(void)
{
//Empty function for compatibility, therefore upper layer won't need to know that FLASH in which operation mode and frequency config needs to be tuned
}
#endif //MSPI_TIMING_PSRAM_NEEDS_TUNING
/*------------------------------------------------------------------------------
* APIs to make SPI0 (and SPI1) FLASH work for high/low freq
*----------------------------------------------------------------------------*/
void __attribute__((weak)) mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi)
{
(void)control_both_mspi;
//for compatibility, will be replaced by the actual implementation once flash timing tuning is ready
}
void __attribute__((weak)) mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi)
{
(void)control_both_mspi;
//for compatibility, will be replaced by the actual implementation once flash timing tuning is ready
}
uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void)
{
return 20;
}
void mspi_timing_enter_low_speed_mode(bool control_spi1)
{
#if MSPI_TIMING_LL_FLASH_CLK_SRC_CHANGEABLE
_mspi_timing_ll_set_flash_clk_src(0, FLASH_CLK_SRC_ROM_DEFAULT);
#endif
#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
/**
* Here we are going to slow the SPI1 frequency to 20Mhz, so we need to set SPI1 din_num and din_mode regs.
*
* Because SPI0 and SPI1 share the din_num and din_mode regs, so if we clear SPI1 din_num and din_mode to
* 0, if the SPI0 flash module clock is still in high freq, it may not work correctly.
*
* Therefore, here we need to slow both the SPI0 and SPI1 and related timing tuning regs to 20Mhz configuration.
*
* Currently we only need to change these clocks on chips with timing tuning
* Should be extended to other no-timing-tuning chips if needed. e.g.:
* we still need to turn down Flash / PSRAM clock speed at a certain period of time
*/
uint32_t low_speed_freq_mhz = mspi_timing_get_psram_low_speed_freq_mhz();
mspi_timing_config_set_flash_clock(low_speed_freq_mhz, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
mspi_timing_config_set_psram_clock(low_speed_freq_mhz, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
mspi_timing_flash_config_clear_tuning_regs(control_spi1);
mspi_timing_psram_config_clear_tuning_regs(control_spi1);
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
}
/**
* Set FLASH and PSRAM module clock, din_num, din_mode and extra dummy,
* according to the configuration got from timing tuning function (`calculate_best_flash_tuning_config`).
* iF control_spi1 == 1, will also update SPI1 timing registers. Should only be set to 1 when do tuning.
*
* This function should always be called after `mspi_timing_flash_tuning` or `calculate_best_flash_tuning_config`
*/
void mspi_timing_enter_high_speed_mode(bool control_spi1)
{
#if MSPI_TIMING_LL_FLASH_CLK_SRC_CHANGEABLE
_mspi_timing_ll_set_flash_clk_src(0, FLASH_CLK_SRC_DEFAULT);
#endif
#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
/**
* Currently we only need to change these clocks on chips with timing tuning
* Should be extended to other no-timing-tuning chips if needed. e.g.:
* we still need to turn down Flash / PSRAM clock speed at a certain period of time
*/
mspi_timing_config_set_flash_clock(FLASH_FREQUENCY_MHZ, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, control_spi1);
#if CONFIG_SPIRAM
mspi_timing_config_set_psram_clock(CONFIG_SPIRAM_SPEED, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, control_spi1);
#endif //#if CONFIG_SPIRAM
#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
mspi_timing_flash_config_set_tuning_regs(control_spi1);
mspi_timing_psram_config_set_tuning_regs(control_spi1);
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
}
void mspi_timing_change_speed_mode_cache_safe(bool switch_down)
{
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && !CONFIG_FREERTOS_UNICORE
// For esp chips with two levels of Cache, if another core attempts to access SPI Flash or PSRAM after the
// cache is freeze, the access will fail and will keep retrying. This will completely block the L1 Cache,
// causing the current core to be unable to access the stack and data in the L2 RAM, which will causes a
// deadlock, so we need to stall another core at first.
esp_ipc_isr_stall_other_cpu();
#endif
/**
* If a no-cache-freeze-supported chip needs timing tuning, add a protection way:
* - spinlock
* - or other way
*
* for preventing concurrent from MSPI to external memory
*/
#if SOC_CACHE_FREEZE_SUPPORTED
#if !ESP_TEE_BUILD
esp_cache_freeze_ext_mem_cache();
#else
/* NOTE: [ESP-TEE] Check implementation when SoCs with 2-level cache are supported */
cache_hal_freeze(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#endif
#endif //#if SOC_CACHE_FREEZE_SUPPORTED
if (switch_down) {
//enter MSPI low speed mode, extra delays should be removed
#if CONFIG_IDF_TARGET_ESP32C5
// ESP32-C5 needs to perform encrypted flash writes even when CPU frequency is reduced.
// Since encrypted writes use SPI1, we need to configure SPI1 timing registers as well
// during runtime frequency switching to ensure proper operation.
mspi_timing_enter_low_speed_mode(true);
#else
mspi_timing_enter_low_speed_mode(false);
#endif
} else {
//enter MSPI high speed mode, extra delays should be considered
#if CONFIG_IDF_TARGET_ESP32C5
// ESP32-C5 needs to perform encrypted flash writes even when CPU frequency is reduced.
// Since encrypted writes use SPI1, we need to configure SPI1 timing registers as well
// during runtime frequency switching to ensure proper operation.
mspi_timing_enter_high_speed_mode(true);
#else
mspi_timing_enter_high_speed_mode(false);
#endif
}
#if SOC_CACHE_FREEZE_SUPPORTED
#if !ESP_TEE_BUILD
esp_cache_unfreeze_ext_mem_cache();
#else
cache_hal_unfreeze(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#endif
#endif //#if SOC_CACHE_FREEZE_SUPPORTED
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && !CONFIG_FREERTOS_UNICORE
esp_ipc_isr_release_other_cpu();
#endif
}
/*------------------------------------------------------------------------------
* Early-init MSPI speed switch (see mspi_timing_tuning.h)
*----------------------------------------------------------------------------*/
#if ESP_TEE_BUILD
#include "riscv/rv_utils.h"
extern void rom_spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state);
extern void rom_spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state);
static void disable_cache(uint32_t cpuid, uint32_t *saved_state)
{
#if SOC_BRANCH_PREDICTOR_SUPPORTED
rv_utils_dis_branch_predictor();
#endif
rom_spi_flash_disable_cache(cpuid, saved_state);
}
static void 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
}
#else // ESP_TEE_BUILD
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
void mspi_timing_enter_low_speed_early(void)
{
uint32_t cache_state = 0;
disable_cache(0, &cache_state);
mspi_timing_enter_low_speed_mode(true);
restore_cache(0, cache_state);
}
void mspi_timing_enter_high_speed_early(void)
{
uint32_t cache_state = 0;
disable_cache(0, &cache_state);
mspi_timing_enter_high_speed_mode(true);
restore_cache(0, cache_state);
}
/*------------------------------------------------------------------------------
* APIs to inform SPI1 Flash driver of necessary timing configurations
*----------------------------------------------------------------------------*/
bool spi_flash_timing_is_tuned(void)
{
#if MSPI_TIMING_MSPI1_IS_INVOLVED
//esp flash driver needs to be notified
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
//either flash or psram or both is tuned, needs notify flash driver
return true;
#else
//otherwise no need
return false;
#endif
#else
//if mspi1 is not involved in timing tuning
return false;
#endif //MSPI_TIMING_MSPI1_IS_INVOLVED
}
#if MSPI_TIMING_MSPI1_IS_INVOLVED && (MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING)
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config)
{
// Get clock configuration directly from system.
out_timing_config->clock_config.spimem = mspi_timing_config_get_flash_clock_reg();
// Get extra dummy length here. Therefore, no matter what freq, or mode.
// If it needs tuning, it will return correct extra dummy len. If no tuning, it will return 0.
out_timing_config->extra_dummy = mspi_timing_config_get_flash_extra_dummy();
#if MSPI_TIMING_LL_FLASH_FDUMMY_RIN_SUPPORTED
out_timing_config->fdummy_rin = mspi_timing_config_get_flash_fdummy_rin();
#endif
// Get CS setup/hold value here.
mspi_timing_config_get_cs_timing(&out_timing_config->cs_setup, &out_timing_config->cs_hold);
}
#else
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config)
{
// This function shouldn't be called if timing tuning is not used.
abort();
}
#endif // #if MSPI_TIMING_MSPI1_IS_INVOLVED && (MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING)
/*------------------------------------------------------------------------------
* Common settings
*----------------------------------------------------------------------------*/
void mspi_timing_set_pin_drive_strength(void)
{
#if CONFIG_IDF_TARGET_ESP32S3
//Set default pin drive
mspi_timing_ll_set_all_pin_drive(0, 3);
#endif
}
@@ -0,0 +1,11 @@
target_include_directories(${COMPONENT_LIB} PUBLIC . include)
set(srcs)
if(NOT BOOTLOADER_BUILD)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi_timing_config.c")
endif()
endif()
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
@@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#define FLASH_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT
#define FLASH_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ
#define PSRAM_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT
#define PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ
ESP_LOG_ATTR_TAG(TAG, "MSPI Timing");
//-------------------------------------MSPI Clock Setting-------------------------------------//
static void s_mspi_flash_set_core_clock(uint8_t mspi_id, uint32_t core_clock_mhz)
{
ESP_DRAM_LOGV(TAG, "flash core clock: %d", core_clock_mhz);
mspi_timing_ll_set_core_clock(mspi_id, core_clock_mhz);
}
static void s_mspi_psram_set_core_clock(uint8_t mspi_id, uint32_t core_clock_mhz)
{
ESP_DRAM_LOGV(TAG, "psram core clock: %d", core_clock_mhz);
mspi_timing_ll_set_core_clock(mspi_id, core_clock_mhz);
}
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
uint32_t core_clock_mhz = 0;
if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) {
core_clock_mhz = FLASH_LOW_SPEED_CORE_CLOCK_MHZ;
} else {
core_clock_mhz = FLASH_HIGH_SPEED_CORE_CLOCK_MHZ;
}
//SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
s_mspi_flash_set_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
uint32_t freqdiv = core_clock_mhz / flash_freq_mhz;
ESP_DRAM_LOGV(TAG, "flash freqdiv: %d", freqdiv);
assert(freqdiv > 0);
uint32_t reg_val = mspi_timing_ll_calculate_clock_reg(freqdiv);
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_0, reg_val);
if (control_both_mspi) {
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_1, reg_val);
}
}
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
(void)control_both_mspi; // for compatibility
uint32_t core_clock_mhz = 0;
if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) {
core_clock_mhz = PSRAM_LOW_SPEED_CORE_CLOCK_MHZ;
} else {
core_clock_mhz = PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ;
}
//SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
s_mspi_psram_set_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
uint32_t freqdiv = core_clock_mhz / psram_freq_mhz;
ESP_DRAM_LOGV(TAG, "psram freqdiv: %d", freqdiv);
assert(freqdiv > 0);
uint32_t reg_val = mspi_timing_ll_calculate_clock_reg(freqdiv);
mspi_timing_ll_set_psram_clock(MSPI_TIMING_LL_MSPI_ID_0, reg_val);
}
@@ -0,0 +1,154 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_assert.h"
#include "esp_flash_partitions.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MSPI_TIMING_MSPI1_IS_INVOLVED (CONFIG_ESPTOOLPY_FLASHFREQ_80M || CONFIG_ESPTOOLPY_FLASHFREQ_120M) //This means esp flash driver needs to be notified
#define MSPI_TIMING_CONFIG_NUM_MAX 32 //This should be larger than the max available timing config num
#define MSPI_TIMING_TEST_DATA_LEN 128
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0x100000
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR ESP_BOOTLOADER_OFFSET
//--------------------------------------FLASH Sampling Mode --------------------------------------//
#define MSPI_TIMING_FLASH_STR_MODE 1
//--------------------------------------FLASH Module Clock --------------------------------------//
#if CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 80
#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 120
#endif
//------------------------------------FLASH Needs Tuning or not-------------------------------------//
#if MSPI_TIMING_FLASH_STR_MODE
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 40)
#endif
//--------------------------------------PSRAM Sampling Mode --------------------------------------//
#define MSPI_TIMING_PSRAM_STR_MODE 1
//--------------------------------------PSRAM Module Clock --------------------------------------//
#if CONFIG_SPIRAM
#if CONFIG_SPIRAM_SPEED_40M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 40
#elif CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 80
#else //CONFIG_SPIRAM_SPEED_120M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 120
#endif
#else //Disable PSRAM
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 10 //Define this to 10MHz
#endif
//------------------------------------PSRAM Needs Tuning or not-------------------------------------//
#if MSPI_TIMING_PSRAM_STR_MODE
#define MSPI_TIMING_PSRAM_NEEDS_TUNING (MSPI_TIMING_PSRAM_MODULE_CLOCK > 40)
#endif
///////////////////////////////////// FLASH CORE CLOCK /////////////////////////////////////
//FLASH 80M
#if CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 80
#define MSPI_TIMING_FLASH_CONSECUTIVE_LEN_MAX 6
#endif
//FLASH 120M
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 120
#define MSPI_TIMING_FLASH_CONSECUTIVE_LEN_MAX 4
#endif
///////////////////////////////////// PSRAM CORE CLOCK /////////////////////////////////////
//PSRAM 80M
#if CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 80
#endif
//PSRAM 120M
#if CONFIG_SPIRAM_SPEED_120M
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 120
#endif //PSRAM 120M DTR
//------------------------------------------Determine the Core Clock-----------------------------------------------//
/**
* @note
* Limitation 1:
* MSPI FLASH and PSRAM share the core clock register. Therefore,
* the expected CORE CLOCK frequencies should be the same.
*/
#if MSPI_TIMING_FLASH_NEEDS_TUNING && MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ == MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ
//If only FLASH needs tuning, the core clock COULD be as FLASH expected
#elif MSPI_TIMING_FLASH_NEEDS_TUNING && !MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ % MSPI_TIMING_PSRAM_MODULE_CLOCK == 0, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ
//If only PSRAM needs tuning, the core clock COULD be as PSRAM expected
#elif !MSPI_TIMING_FLASH_NEEDS_TUNING && MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ % MSPI_TIMING_FLASH_MODULE_CLOCK == 0, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ
#else
#define MSPI_TIMING_CORE_CLOCK_MHZ 80
#endif
/**
* @note
* Limitation 2: SDR mode requires the core clock divider (core_clk / div = module_clk) to be even number or 1.
*/
#if MSPI_TIMING_FLASH_STR_MODE
ESP_STATIC_ASSERT((MSPI_TIMING_CORE_CLOCK_MHZ == MSPI_TIMING_FLASH_MODULE_CLOCK) || (MSPI_TIMING_CORE_CLOCK_MHZ % (2 * MSPI_TIMING_FLASH_MODULE_CLOCK) == 0), "FLASH Mode configuration are not supported");
#endif
#if MSPI_TIMING_PSRAM_STR_MODE
ESP_STATIC_ASSERT((MSPI_TIMING_CORE_CLOCK_MHZ == MSPI_TIMING_PSRAM_MODULE_CLOCK) || (MSPI_TIMING_CORE_CLOCK_MHZ % (2 * MSPI_TIMING_PSRAM_MODULE_CLOCK) == 0), "PSRAM Mode configuration are not supported");
#endif
//------------------------------------------Helper Macros to get FLASH/PSRAM tuning configs-----------------------------------------------//
#define __GET_TUNING_CONFIG(type, core_clock, module_clock, mode) \
(mspi_timing_config_t) { .tuning_config_table = MSPI_TIMING_##type##_CONFIG_TABLE_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.available_config_num = MSPI_TIMING_##type##_CONFIG_NUM_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.default_config_id = MSPI_TIMING_##type##_DEFAULT_CONFIG_ID_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode }
#define _GET_TUNING_CONFIG(type, core_clock, module_clock, mode) __GET_TUNING_CONFIG(type, core_clock, module_clock, mode)
#define MSPI_TIMING_FLASH_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(FLASH, core_clock_mhz, module_clock_mhz, mode)
#define MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(PSRAM, core_clock_mhz, module_clock_mhz, mode)
/**
* Timing Tuning Parameters
*/
//FLASH: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {2, 1, 3}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {2, 1, 4}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 4
//FLASH: core clock 80M, module clock 80M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE {{2, 2, 1}, {2, 1, 1}, {2, 0, 1}, {0, 0, 0}, {3, 1, 2}, {2, 3, 2}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {3, 1, 3}, {2, 3, 3}, {2, 2, 3}, {2, 1, 3}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 14
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 4
//PSRAM: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {2, 1, 3}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {2, 1, 4}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 4
//PSRAM: core clock 80M, module clock 80M, STR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE {{2, 2, 1}, {2, 1, 1}, {2, 0, 1}, {0, 0, 0}, {3, 1, 2}, {2, 3, 2}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {3, 1, 3}, {2, 3, 3}, {2, 2, 3}, {2, 1, 3}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 14
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 4
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,11 @@
target_include_directories(${COMPONENT_LIB} PUBLIC . include)
set(srcs)
if(NOT BOOTLOADER_BUILD)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi_timing_config.c")
endif()
endif()
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
@@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#define FLASH_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT
#define FLASH_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ
#define PSRAM_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT
#define PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ
const static char *TAG = "MSPI Timing";
//-------------------------------------MSPI Clock Setting-------------------------------------//
static void s_mspi_flash_set_core_clock(uint8_t mspi_id, uint32_t core_clock_mhz)
{
ESP_DRAM_LOGV(TAG, "flash core clock: %d", core_clock_mhz);
mspi_timing_ll_set_core_clock(mspi_id, core_clock_mhz);
}
static void s_mspi_psram_set_core_clock(uint8_t mspi_id, uint32_t core_clock_mhz)
{
ESP_DRAM_LOGV(TAG, "psram core clock: %d", core_clock_mhz);
mspi_timing_ll_set_core_clock(mspi_id, core_clock_mhz);
}
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
uint32_t core_clock_mhz = 0;
if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) {
core_clock_mhz = FLASH_LOW_SPEED_CORE_CLOCK_MHZ;
} else {
core_clock_mhz = FLASH_HIGH_SPEED_CORE_CLOCK_MHZ;
}
//SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
s_mspi_flash_set_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
uint32_t freqdiv = core_clock_mhz / flash_freq_mhz;
ESP_DRAM_LOGV(TAG, "flash freqdiv: %d", freqdiv);
assert(freqdiv > 0);
uint32_t reg_val = mspi_timing_ll_calculate_clock_reg(freqdiv);
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_0, reg_val);
if (control_both_mspi) {
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_1, reg_val);
}
}
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
(void)control_both_mspi; // for compatibility
uint32_t core_clock_mhz = 0;
if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) {
core_clock_mhz = PSRAM_LOW_SPEED_CORE_CLOCK_MHZ;
} else {
core_clock_mhz = PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ;
}
//SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
s_mspi_psram_set_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
uint32_t freqdiv = core_clock_mhz / psram_freq_mhz;
ESP_DRAM_LOGV(TAG, "psram freqdiv: %d", freqdiv);
assert(freqdiv > 0);
uint32_t reg_val = mspi_timing_ll_calculate_clock_reg(freqdiv);
mspi_timing_ll_set_psram_clock(MSPI_TIMING_LL_MSPI_ID_0, reg_val);
}
@@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_assert.h"
#include "esp_flash_partitions.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MSPI_TIMING_MSPI1_IS_INVOLVED CONFIG_ESPTOOLPY_FLASHFREQ_120M //This means esp flash driver needs to be notified
#define MSPI_TIMING_CONFIG_NUM_MAX 32 //This should be larger than the max available timing config num
#define MSPI_TIMING_TEST_DATA_LEN 128
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0x100000
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR ESP_BOOTLOADER_OFFSET
//--------------------------------------FLASH Sampling Mode --------------------------------------//
#define MSPI_TIMING_FLASH_STR_MODE 1
//--------------------------------------FLASH Module Clock --------------------------------------//
#if CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 80
#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 120
#endif
//------------------------------------FLASH Needs Tuning or not-------------------------------------//
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 80)
//--------------------------------------PSRAM Sampling Mode --------------------------------------//
#define MSPI_TIMING_PSRAM_STR_MODE 1
//--------------------------------------PSRAM Module Clock --------------------------------------//
#if CONFIG_SPIRAM
#if CONFIG_SPIRAM_SPEED_40M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 40
#elif CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 80
#elif CONFIG_SPIRAM_SPEED_120M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 120
#endif
#else //Disable PSRAM
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 10 //Define this to 10MHz
#endif
//------------------------------------PSRAM Needs Tuning or not-------------------------------------//
#if MSPI_TIMING_PSRAM_STR_MODE && !CONFIG_SECURE_ENABLE_TEE
#define MSPI_TIMING_PSRAM_NEEDS_TUNING (MSPI_TIMING_PSRAM_MODULE_CLOCK > 40)
#endif
///////////////////////////////////// FLASH CORE CLOCK /////////////////////////////////////
//FLASH 80M
#if CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 80
#define MSPI_TIMING_FLASH_CONSECUTIVE_LEN_MAX 6
#endif
//FLASH 120M
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 120
#define MSPI_TIMING_FLASH_CONSECUTIVE_LEN_MAX 4
#endif
///////////////////////////////////// PSRAM CORE CLOCK /////////////////////////////////////
//PSRAM 80M
#if CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 80
#endif
//PSRAM 120M
#if CONFIG_SPIRAM_SPEED_120M
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 120
#endif //PSRAM 120M DTR
//------------------------------------------Determine the Core Clock-----------------------------------------------//
/**
* @note
* Limitation 1:
* MSPI FLASH and PSRAM share the core clock register. Therefore,
* the expected CORE CLOCK frequencies should be the same.
*/
#if MSPI_TIMING_FLASH_NEEDS_TUNING && MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ == MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ
//If only FLASH needs tuning, the core clock COULD be as FLASH expected
#elif MSPI_TIMING_FLASH_NEEDS_TUNING && !MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ % MSPI_TIMING_PSRAM_MODULE_CLOCK == 0, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ
//If only PSRAM needs tuning, the core clock COULD be as PSRAM expected
#elif !MSPI_TIMING_FLASH_NEEDS_TUNING && MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ % MSPI_TIMING_FLASH_MODULE_CLOCK == 0, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ
#else
#define MSPI_TIMING_CORE_CLOCK_MHZ 80
#endif
/**
* @note
* Limitation 2: SDR mode requires the core clock divider (core_clk / div = module_clk) to be even number or 1.
*/
#if MSPI_TIMING_FLASH_STR_MODE
ESP_STATIC_ASSERT((MSPI_TIMING_CORE_CLOCK_MHZ == MSPI_TIMING_FLASH_MODULE_CLOCK) || (MSPI_TIMING_CORE_CLOCK_MHZ % (2 * MSPI_TIMING_FLASH_MODULE_CLOCK) == 0), "FLASH Mode configuration are not supported");
#endif
#if MSPI_TIMING_PSRAM_STR_MODE
ESP_STATIC_ASSERT((MSPI_TIMING_CORE_CLOCK_MHZ == MSPI_TIMING_PSRAM_MODULE_CLOCK) || (MSPI_TIMING_CORE_CLOCK_MHZ % (2 * MSPI_TIMING_PSRAM_MODULE_CLOCK) == 0), "PSRAM Mode configuration are not supported");
#endif
//------------------------------------------Helper Macros to get FLASH/PSRAM tuning configs-----------------------------------------------//
#define __GET_TUNING_CONFIG(type, core_clock, module_clock, mode) \
(mspi_timing_config_t) { .tuning_config_table = MSPI_TIMING_##type##_CONFIG_TABLE_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.available_config_num = MSPI_TIMING_##type##_CONFIG_NUM_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.default_config_id = MSPI_TIMING_##type##_DEFAULT_CONFIG_ID_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode }
#define _GET_TUNING_CONFIG(type, core_clock, module_clock, mode) __GET_TUNING_CONFIG(type, core_clock, module_clock, mode)
#define MSPI_TIMING_FLASH_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(FLASH, core_clock_mhz, module_clock_mhz, mode)
#define MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(PSRAM, core_clock_mhz, module_clock_mhz, mode)
/**
* Timing Tuning Parameters
*/
//FLASH: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {2, 1, 3}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {2, 1, 4}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 2
//FLASH: core clock 80M, module clock 80M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE {{2, 2, 1}, {2, 1, 1}, {2, 0, 1}, {0, 0, 0}, {3, 1, 2}, {2, 3, 2}, {2, 2, 2}, {2, 1, 2}, {2, 0, 1}, {0, 0, 1}, {3, 1, 3}, {2, 3, 3}, {2, 2, 3}, {2, 1, 3}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 14
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 4
//PSRAM: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {2, 1, 3}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {2, 1, 4}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 4
//PSRAM: core clock 80M, module clock 80M, STR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE {{2, 2, 1}, {2, 1, 1}, {2, 0, 1}, {0, 0, 0}, {3, 1, 2}, {2, 3, 2}, {2, 2, 2}, {2, 1, 2}, {2, 0, 1}, {0, 0, 1}, {3, 1, 3}, {2, 3, 3}, {2, 2, 3}, {2, 1, 3}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 14
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_80M_MODULE_CLK_80M_STR_MODE 4
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,11 @@
target_include_directories(${COMPONENT_LIB} PUBLIC .)
set(srcs)
if(NOT BOOTLOADER_BUILD)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi_timing_config.c")
endif()
endif()
target_sources(${COMPONENT_LIB} PRIVATE "${srcs}")
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#include "soc/hp_sys_clkrst_struct.h"
ESP_LOG_ATTR_TAG(TAG, "MSPI Timing");
//-------------------------------------MSPI Clock Setting-------------------------------------//
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
uint32_t freqdiv = MSPI_TIMING_MPLL_FREQ_MHZ / MSPI_TIMING_CORE_CLOCK_DIV / psram_freq_mhz;
assert(freqdiv > 0);
ESP_DRAM_LOGD(TAG, "psram_freq_mhz: %" PRIu32 " mhz, bus clock div: %" PRIu32, psram_freq_mhz, freqdiv);
PERIPH_RCC_ATOMIC() {
//MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here.
psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
psram_ctrlr_ll_set_core_clock_div(PSRAM_CTRLR_LL_MSPI_ID_2, MSPI_TIMING_CORE_CLOCK_DIV);
psram_ctrlr_ll_set_bus_clock(PSRAM_CTRLR_LL_MSPI_ID_3, freqdiv);
psram_ctrlr_ll_set_bus_clock(PSRAM_CTRLR_LL_MSPI_ID_2, freqdiv);
}
}
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
#if MSPI_TIMING_FLASH_NEEDS_TUNING
assert(HP_SYS_CLKRST.peri_clk_ctrl00.reg_flash_clk_src_sel == 1);
uint32_t core_clock_mhz = MSPI_TIMING_SPLL_FREQ_MHZ / MSPI_TIMING_LL_HP_FLASH_CORE_CLK_DIV;
assert(core_clock_mhz == 120);
uint32_t freqdiv = core_clock_mhz / flash_freq_mhz;
PERIPH_RCC_ATOMIC() {
//core clock shared among SPI0 / SPI1
mspi_timing_ll_set_flash_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
}
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_0, freqdiv);
if (control_both_mspi) {
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_1, freqdiv);
}
mspi_timing_ll_mask_invalid_dqs(MSPI_TIMING_LL_MSPI_ID_0, true);
mspi_timing_ll_mask_invalid_dqs(MSPI_TIMING_LL_MSPI_ID_1, true);
#endif
}
@@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MSPI_TIMING_MSPI1_IS_INVOLVED CONFIG_ESPTOOLPY_FLASHFREQ_120M //This means esp flash driver needs to be notified
#define MSPI_TIMING_CONFIG_NUM_MAX 32 //This should be larger than the max available timing config num
#define MSPI_TIMING_TEST_DATA_LEN 128
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0x80
#define MSPI_TIMING_DELAYLINE_TEST_NUMS 100
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR CONFIG_BOOTLOADER_OFFSET_IN_FLASH
#define MSPI_TIMING_CORE_CLOCK_DIV 1
#if CONFIG_SPIRAM_SPEED_250M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 500
#elif CONFIG_SPIRAM_SPEED_200M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#elif CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 320
#else
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#endif
#define MSPI_TIMING_SPLL_FREQ_MHZ 480
#define MSPI_TIMING_PSRAM_DTR_MODE CONFIG_SPIRAM_MODE_HEX
#define MSPI_TIMING_FLASH_STR_MODE 1
#if CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 80
#else //CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 120
#endif
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 80)
#if MSPI_TIMING_FLASH_NEEDS_TUNING
#define MSPI_TIMING_FLASH_CORE_CLOCK_MHZ 120
#else
#define MSPI_TIMING_FLASH_CORE_CLOCK_MHZ 80
#endif
/**
* @note
* Limitation 1: SDR mode requires the core clock divider (core_clk / div = module_clk) to be even number or 1.
*/
#if MSPI_TIMING_FLASH_STR_MODE
ESP_STATIC_ASSERT((MSPI_TIMING_FLASH_CORE_CLOCK_MHZ == MSPI_TIMING_FLASH_MODULE_CLOCK) || (MSPI_TIMING_FLASH_CORE_CLOCK_MHZ % (2 * MSPI_TIMING_FLASH_MODULE_CLOCK) == 0), "FLASH Mode configuration are not supported");
#endif
//------------------------------------------Helper Macros to get FLASH/PSRAM tuning configs-----------------------------------------------//
#define __GET_TUNING_CONFIG(type, core_clock, module_clock, mode) \
(mspi_timing_config_t) { .tuning_config_table = MSPI_TIMING_##type##_CONFIG_TABLE_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.available_config_num = MSPI_TIMING_##type##_CONFIG_NUM_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.flash_default_config_id = MSPI_TIMING_##type##_DEFAULT_CONFIG_ID_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode }
#define _GET_TUNING_CONFIG(type, core_clock, module_clock, mode) __GET_TUNING_CONFIG(type, core_clock, module_clock, mode)
#define MSPI_TIMING_FLASH_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(FLASH, core_clock_mhz, module_clock_mhz, mode)
/**
* Timing Tuning Parameters
*/
//FLASH: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {2, 1, 3}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {2, 1, 4}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 2
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,11 @@
target_include_directories(${COMPONENT_LIB} PUBLIC . include)
set(srcs)
if(NOT BOOTLOADER_BUILD)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi_timing_config.c" "mspi_timing_by_mspi_delay.c")
endif()
endif()
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
@@ -0,0 +1,888 @@
/*
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI delay
* This file will only be built, when `SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY == 1`
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "string.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/rtc.h"
#include "hal/mspi_ll.h"
#include "hal/clk_tree_ll.h"
#include "esp_private/mspi_timing_config.h"
#include "esp_private/mspi_timing_by_mspi_delay.h"
#include "esp_private/spi_flash_os.h"
#include "esp32s3/rom/spi_flash.h"
#include "esp32s3/rom/opi_flash.h"
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
#include "mspi_timing_tuning_configs.h"
#include "freertos/FreeRTOS.h"
#include "esp_private/cache_utils.h"
#include "esp_private/sar_periph_ctrl.h"
#include "esp_private/startup_internal.h"
#endif // CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
#define OPI_PSRAM_SYNC_READ 0x0000
#define OPI_PSRAM_SYNC_WRITE 0x8080
#define OCT_PSRAM_RD_DUMMY_NUM (2*(10-1))
#define OCT_PSRAM_WR_DUMMY_NUM (2*(5-1))
#define QPI_PSRAM_FAST_READ 0XEB
#define QPI_PSRAM_WRITE 0X38
#define QPI_PSRAM_FAST_READ_DUMMY 6
#define NOT_INIT_INT 127
/////////////////////////////////////////TIMING TUNING IS NEEDED//////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_LOG_ATTR_TAG(TAG, "MSPI Timing");
//If one of the FLASH / PSRAM or both of them need timing tuning, we should build following code
typedef enum {
PSRAM_CMD_QPI,
PSRAM_CMD_SPI,
} psram_cmd_mode_t;
static uint8_t s_rom_flash_extra_dummy[2] = {NOT_INIT_INT, NOT_INIT_INT};
#if CONFIG_SPIRAM_MODE_QUAD
static uint8_t s_psram_extra_dummy;
extern void psram_exec_cmd(int spi_num, psram_cmd_mode_t mode,
uint32_t cmd, int cmd_bit_len,
uint32_t addr, int addr_bit_len,
int dummy_bits,
uint8_t* mosi_data, int mosi_bit_len,
uint8_t* miso_data, int miso_bit_len,
uint32_t cs_mask,
bool is_write_erase_operation);
#endif
//-------------------------------------FLASH timing tuning register config-------------------------------------//
void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config)
{
#if MSPI_TIMING_FLASH_DTR_MODE
#define FLASH_MODE DTR_MODE
#else //MSPI_TIMING_FLASH_STR_MODE
#define FLASH_MODE STR_MODE
#endif
#if CONFIG_ESPTOOLPY_FLASHFREQ_80M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, FLASH_MODE);
#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, FLASH_MODE);
#else
assert(false && "should never reach here");
#endif
#undef FLASH_MODE
}
void mspi_timing_flash_init(uint32_t flash_freq_mhz)
{
mspi_timing_config_set_flash_clock(flash_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
//Power on HCLK
mspi_timinng_ll_enable_flash_timing_adjust_clk(0);
}
static void s_set_flash_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num)
{
mspi_timing_ll_set_flash_din_mode(spi_num, din_mode);
mspi_timing_ll_set_flash_din_num(spi_num, din_num);
}
static uint32_t spi_timing_config_get_dummy(void)
{
mspi_timing_ll_flash_mode_t mode = mspi_timing_ll_get_flash_mode(0);
if (mode == MSPI_TIMING_LL_FLASH_OPI_MODE) {
abort();
}
#if CONFIG_SPI_FLASH_HPM_DC_ON
if (spi_flash_hpm_dummy_adjust()) { // HPM-DC is enabled
const spi_flash_hpm_dummy_conf_t *hpm_dummy = spi_flash_hpm_get_dummy();
switch (mode) {
case MSPI_TIMING_LL_FLASH_QIO_MODE:
return hpm_dummy->qio_dummy - 1;
case MSPI_TIMING_LL_FLASH_QUAD_MODE:
return hpm_dummy->qout_dummy - 1;
case MSPI_TIMING_LL_FLASH_DIO_MODE:
return hpm_dummy->dio_dummy - 1;
case MSPI_TIMING_LL_FLASH_DUAL_MODE:
return hpm_dummy->dout_dummy - 1;
case MSPI_TIMING_LL_FLASH_FAST_MODE:
return hpm_dummy->fastrd_dummy - 1;
case MSPI_TIMING_LL_FLASH_SLOW_MODE:
return 0;
default:
abort();
}
} else
#endif
{
// HPM-DC is not enabled
switch (mode) {
case MSPI_TIMING_LL_FLASH_QIO_MODE:
return SPI1_R_QIO_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_QUAD_MODE:
return SPI1_R_FAST_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_DIO_MODE:
return SPI1_R_DIO_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_DUAL_MODE:
return SPI1_R_FAST_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_FAST_MODE:
return SPI1_R_FAST_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_SLOW_MODE:
return 0;
default:
abort();
}
}
}
static void s_set_flash_extra_dummy(uint8_t spi_num, uint8_t extra_dummy)
{
if (spi_flash_is_octal_mode_enabled()) {
mspi_timing_ll_set_octal_flash_extra_dummy(spi_num, extra_dummy);
return;
}
/**
* HW workaround:
* The `SPI_MEM_TIMING_CALI_REG` register is only used for OPI on 728
* Here we only need to update this global variable for extra dummy. Since we use the ROM Flash API, which will set the dummy based on this.
* We only initialise the SPI0. And leave the SPI1 for flash driver to configure.
*/
if (s_rom_flash_extra_dummy[spi_num] == NOT_INIT_INT) {
s_rom_flash_extra_dummy[spi_num] = g_rom_spiflash_dummy_len_plus[spi_num];
}
g_rom_spiflash_dummy_len_plus[spi_num] = s_rom_flash_extra_dummy[spi_num] + extra_dummy;
// Only Quad Flash will run into this branch.
uint32_t dummy = spi_timing_config_get_dummy();
mspi_timing_ll_set_quad_flash_dummy(spi_num, dummy + g_rom_spiflash_dummy_len_plus[spi_num]);
}
void mspi_timing_config_flash_set_tuning_regs(const void *configs, uint8_t id)
{
const mspi_timing_tuning_param_t *params = &((mspi_timing_config_t *)configs)->tuning_config_table[id];
/**
* 1. SPI_MEM_DINx_MODE(1), SPI_MEM_DINx_NUM(1) are meaningless
* SPI0 and SPI1 share the SPI_MEM_DINx_MODE(0), SPI_MEM_DINx_NUM(0) for FLASH timing tuning
* 2. We use SPI1 to get the best Flash timing tuning (mode and num) config
*/
s_set_flash_din_mode_num(0, params->spi_din_mode, params->spi_din_num);
s_set_flash_extra_dummy(1, params->extra_dummy_len);
}
//-------------------------------------------FLASH Read/Write------------------------------------------//
void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
if (spi_flash_is_octal_mode_enabled()) {
// note that in spi_flash_read API, there is a wait-idle stage, since flash can only be read in idle state.
// but after we change the timing settings, we might not read correct idle status via RDSR.
// so, here we should use a read API that won't check idle status.
mspi_timing_ll_clear_fifo(1);
esp_rom_opiflash_read_raw(addr, buf, len);
} else {
esp_rom_spiflash_read(addr, (uint32_t *)buf, len);
}
}
//-------------------------------------PSRAM timing tuning register config-------------------------------------//
void mspi_timing_get_psram_tuning_configs(mspi_timing_config_t *config)
{
#if MSPI_TIMING_PSRAM_DTR_MODE
#define PSRAM_MODE DTR_MODE
#else //MSPI_TIMING_PSRAM_STR_MODE
#define PSRAM_MODE STR_MODE
#endif
#if CONFIG_SPIRAM_SPEED_80M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, PSRAM_MODE);
#elif CONFIG_SPIRAM_SPEED_120M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, PSRAM_MODE);
#else
assert(false && "should never reach here");
#endif
#undef PSRAM_MODE
}
void mspi_timing_psram_init(uint32_t psram_freq_mhz)
{
mspi_timing_config_set_flash_clock(psram_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
//Power on HCLK
mspi_timinng_ll_enable_psram_timing_adjust_clk(0);
}
static void s_set_psram_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num)
{
mspi_timing_ll_set_psram_din_mode(spi_num, din_mode);
mspi_timing_ll_set_psram_din_num(spi_num, din_num);
}
static void s_set_psram_extra_dummy(uint8_t spi_num, uint8_t extra_dummy)
{
#if CONFIG_SPIRAM_MODE_OCT
mspi_timing_ll_set_octal_psram_extra_dummy(spi_num, extra_dummy);
#elif CONFIG_SPIRAM_MODE_QUAD
//HW workaround: Use normal dummy register to set extra dummy, the calibration dedicated extra dummy register doesn't work for quad mode
mspi_timing_ll_set_quad_psram_dummy(spi_num, (QPI_PSRAM_FAST_READ_DUMMY + extra_dummy - 1));
#endif
}
void mspi_timing_config_psram_set_tuning_regs(const void *configs, uint8_t id)
{
const mspi_timing_tuning_param_t *params = &((mspi_timing_config_t *)configs)->tuning_config_table[id];
/**
* 1. SPI_MEM_SPI_SMEM_DINx_MODE(1), SPI_MEM_SPI_SMEM_DINx_NUM(1) are meaningless
* SPI0 and SPI1 share the SPI_MEM_SPI_SMEM_DINx_MODE(0), SPI_MEM_SPI_SMEM_DINx_NUM(0) for PSRAM timing tuning
* 2. We use SPI1 to get the best PSRAM timing tuning (mode and num) config
*/
s_set_psram_din_mode_num(0, params->spi_din_mode, params->spi_din_num);
#if CONFIG_SPIRAM_MODE_OCT
//On 728, for SPI1, flash and psram share the extra dummy register
s_set_flash_extra_dummy(1, params->extra_dummy_len);
#elif CONFIG_SPIRAM_MODE_QUAD
//Update this `s_psram_extra_dummy`, the `s_psram_read_data` will set dummy according to this `s_psram_extra_dummy`
s_psram_extra_dummy = params->extra_dummy_len;
mspi_timing_ll_set_quad_flash_dummy(1, params->extra_dummy_len - 1);
#endif
}
//-------------------------------------------PSRAM Read/Write------------------------------------------//
static void s_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#if CONFIG_SPIRAM_MODE_OCT
esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE,
OPI_PSRAM_SYNC_WRITE, 16,
addr, 32,
OCT_PSRAM_WR_DUMMY_NUM,
buf, len * 8,
NULL, 0,
BIT(1),
false);
#elif CONFIG_SPIRAM_MODE_QUAD
psram_exec_cmd(1, 0,
QPI_PSRAM_WRITE, 8,
addr, 24,
0,
buf, len * 8,
NULL, 0,
SPI_MEM_CS1_DIS_M,
false);
#endif
}
static void s_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#if CONFIG_SPIRAM_MODE_OCT
mspi_timing_ll_clear_fifo(1);
esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE,
OPI_PSRAM_SYNC_READ, 16,
addr, 32,
OCT_PSRAM_RD_DUMMY_NUM,
NULL, 0,
buf, len * 8,
BIT(1),
false);
#elif CONFIG_SPIRAM_MODE_QUAD
psram_exec_cmd(1, 0,
QPI_PSRAM_FAST_READ, 8,
addr, 24,
QPI_PSRAM_FAST_READ_DUMMY + s_psram_extra_dummy,
NULL, 0,
buf, len * 8,
SPI_MEM_CS1_DIS_M,
false);
#endif
}
static void s_psram_execution(uint8_t *buf, uint32_t addr, uint32_t len, bool is_read)
{
while (len) {
uint32_t length = MIN(len, 32);
if (is_read) {
s_psram_read_data(buf, addr, length);
} else {
s_psram_write_data(buf, addr, length);
}
addr += length;
buf += length;
len -= length;
}
}
void mspi_timing_config_psram_prepare_reference_data(uint8_t *buf, uint32_t len)
{
assert((len == MSPI_TIMING_TEST_DATA_LEN) && (len % 4 == 0));
for (int i = 0; i < len / 4; i++) {
((uint32_t *)buf)[i] = 0xa5ff005a;
}
}
void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
s_psram_execution(buf, addr, len, false);
}
void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
s_psram_execution(buf, addr, len, true);
}
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Selection
*-------------------------------------------------------------------------------------------------*/
#if (MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE) && (MSPI_TIMING_CORE_CLOCK_MHZ == 240)
static bool get_working_pll_freq(const uint8_t *reference_data, bool is_flash, uint32_t *out_max_freq, uint32_t *out_min_freq)
{
uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0};
rtc_cpu_freq_config_t previous_config;
rtc_clk_cpu_freq_get_config(&previous_config);
uint32_t big_num = MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX * 2; //This number should be larger than MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX, for error handling
uint32_t max_freq = 0;
uint32_t min_freq = big_num;
soc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
for (int pll_mhz_tuning = MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN; pll_mhz_tuning <= MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX; pll_mhz_tuning += 8) {
//bbpll calibration start
clk_ll_bbpll_calibration_start();
/**
* pll_mhz = xtal_mhz * (oc_div + 4) / (oc_ref_div + 1)
*/
clk_ll_bbpll_set_frequency_for_mspi_tuning(xtal_freq, pll_mhz_tuning, ((pll_mhz_tuning / 4) - 4), 9);
//wait calibration done
while (!clk_ll_bbpll_calibration_is_done());
//bbpll calibration stop
clk_ll_bbpll_calibration_stop();
memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN);
if (is_flash) {
mspi_timing_config_flash_read_data(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN);
} else {
mspi_timing_config_psram_read_data(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN);
}
if (memcmp(read_data, reference_data, MSPI_TIMING_TEST_DATA_LEN) == 0) {
max_freq = MAX(pll_mhz_tuning, max_freq);
min_freq = MIN(pll_mhz_tuning, min_freq);
//Continue to find successful cases
continue;
}
if (max_freq != 0) {
//The first fail case after successful case(s) is the end
break;
}
//If no break, no successful case found, continue to find successful cases
}
//restore PLL config
clk_ll_bbpll_set_freq_mhz(previous_config.source_freq_mhz);
//bbpll calibration start
clk_ll_bbpll_calibration_start();
//set pll
clk_ll_bbpll_set_config(previous_config.source_freq_mhz, xtal_freq);
//wait calibration done
while (!clk_ll_bbpll_calibration_is_done());
//bbpll calibration stop
clk_ll_bbpll_calibration_stop();
*out_max_freq = max_freq;
*out_min_freq = min_freq;
return (max_freq != 0);
}
#endif //Frequency Scanning
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
// These arrays store the frequency scan result of the timing points
int psram_pass_freq_min[MSPI_TIMING_CONFIG_NUM_MAX] = {0};
int psram_pass_freq_max[MSPI_TIMING_CONFIG_NUM_MAX] = {0};
#endif // CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
static uint32_t s_select_best_tuning_config_dtr(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash)
{
#if (MSPI_TIMING_CORE_CLOCK_MHZ == 160)
//Core clock 160M DTR best point scheme
uint32_t best_point;
//Define these magic number in macros in `spi_timing_config.h`. TODO: IDF-3663
if (consecutive_length <= 2 || consecutive_length >= 6) {
//tuning is FAIL, select default point, and generate a warning
best_point = configs->default_config_id;
ESP_DRAM_LOGW(TAG, "tuning fail, best point is fallen back to index %"PRIu32"", best_point);
} else if (consecutive_length <= 4) {
//consecutive length : 3 or 4
best_point = end - 1;
ESP_DRAM_LOGD(TAG, "tuning success, best point is index %"PRIu32"", best_point);
} else {
//consecutive point list length equals 5
best_point = end - 2;
ESP_DRAM_LOGD(TAG, "tuning success, best point is index %"PRIu32"", best_point);
}
return best_point;
#elif (MSPI_TIMING_CORE_CLOCK_MHZ == 240)
uint32_t best_point = 0;
uint32_t current_point = end + 1 - consecutive_length;
bool ret = false;
//This `max_freq` is the max pll frequency that per MSPI timing tuning config can work
uint32_t temp_max_freq = 0;
uint32_t temp_min_freq = 0;
#if !CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
uint32_t max_freq = 0;
for (; current_point <= end; current_point++) {
if (is_flash) {
mspi_timing_config_flash_set_tuning_regs(configs, current_point);
} else {
mspi_timing_config_psram_set_tuning_regs(configs, current_point);
}
ret = get_working_pll_freq(reference_data, is_flash, &temp_max_freq, &temp_min_freq);
if (ret && temp_min_freq <= MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_LOW && temp_max_freq >= MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_HIGH && temp_max_freq > max_freq) {
max_freq = temp_max_freq;
best_point = current_point;
}
ESP_DRAM_LOGD(TAG, "sample point %" PRIu32 ", max pll is %" PRIu32 " mhz, min pll is %" PRIu32, current_point, temp_max_freq, temp_min_freq);
}
if (max_freq == 0) {
ESP_DRAM_LOGW(TAG, "freq scan tuning fail, best point is fallen back to index %" PRIu32, end + 1 - consecutive_length);
best_point = end + 1 - consecutive_length;
} else {
ESP_DRAM_LOGD(TAG, "freq scan success, max pll is %" PRIu32 "mhz, best point is index %" PRIu32, max_freq, best_point);
}
#else
uint32_t freq_diff_min = 0xffffffff;
for (; current_point <= end; current_point++) {
if (is_flash) {
mspi_timing_config_flash_set_tuning_regs(configs, current_point);
} else {
mspi_timing_config_psram_set_tuning_regs(configs, current_point);
}
ret = get_working_pll_freq(reference_data, is_flash, &temp_max_freq, &temp_min_freq);
if (ret == true) {
if (temp_min_freq == MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN) {
// divided by 4 for mspi clk frequency
psram_pass_freq_min[current_point] = (temp_max_freq - MSPI_TIMING_PLL_FREQ_SCAN_WIDTH_MHZ) / 4; // use MSPI_TIMING_PLL_FREQ_SCAN_WIDTH_MHZ to calculate the real frequency
psram_pass_freq_max[current_point] = temp_max_freq / 4;
} else if (temp_max_freq == MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX) {
psram_pass_freq_min[current_point] = temp_min_freq / 4;
psram_pass_freq_max[current_point] = (temp_min_freq + MSPI_TIMING_PLL_FREQ_SCAN_WIDTH_MHZ) / 4; // use MSPI_TIMING_PLL_FREQ_SCAN_WIDTH_MHZ to calculate the real frequency
} else {
psram_pass_freq_min[current_point] = temp_min_freq / 4;
psram_pass_freq_max[current_point] = temp_max_freq / 4;
}
ESP_DRAM_LOGD(TAG, "sample point %" PRIu32 ", max pll is %" PRIu32 " mhz, min pll is %" PRIu32 " mhz, max spi is %" PRIu32 " mhz, min spi is %" PRIu32 " mhz", current_point, temp_max_freq, temp_min_freq, psram_pass_freq_max[current_point], psram_pass_freq_min[current_point]);
// calculate the difference to psram_pass_freq and 120MHz
int temp_min_freq_diff = abs(120 - psram_pass_freq_min[current_point]);
int temp_max_freq_diff = abs(psram_pass_freq_max[current_point] - 120);
if (abs(temp_min_freq_diff - temp_max_freq_diff) < freq_diff_min) {
freq_diff_min = abs(temp_min_freq_diff - temp_max_freq_diff);
best_point = current_point;
}
}
}
if (freq_diff_min == 0xffffffff) {
ESP_DRAM_LOGW(TAG, "freq scan tuning fail, best point is fallen back to index %" PRIu32, end + 1 - consecutive_length);
best_point = end + 1 - consecutive_length;
} else {
ESP_DRAM_LOGD(TAG, "freq scan success, best point is index %" PRIu32, best_point);
}
#endif
return best_point;
#else
//won't reach here
abort();
#endif
}
static uint32_t s_select_best_tuning_config_str(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end)
{
#if (MSPI_TIMING_CORE_CLOCK_MHZ == 120 || MSPI_TIMING_CORE_CLOCK_MHZ == 240)
//STR best point scheme
uint32_t best_point;
if (consecutive_length <= 2 || consecutive_length >= 5) {
//tuning is FAIL, select default point, and generate a warning
best_point = configs->default_config_id;
ESP_DRAM_LOGW(TAG, "tuning fail, best point is fallen back to index %"PRIu32"", best_point);
} else {
//consecutive length : 3 or 4
best_point = end - consecutive_length / 2;
ESP_DRAM_LOGD(TAG, "tuning success, best point is index %"PRIu32"", best_point);
}
return best_point;
#else
//won't reach here
abort();
#endif
}
static uint32_t s_select_best_tuning_config(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr, bool is_flash)
{
uint32_t best_point = 0;
if (is_ddr) {
best_point = s_select_best_tuning_config_dtr(configs, consecutive_length, end, reference_data, is_flash);
} else {
best_point = s_select_best_tuning_config_str(configs, consecutive_length, end);
}
return best_point;
}
uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs;
uint32_t best_point = s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, true);
ESP_DRAM_LOGD(TAG, "Flash timing tuning index: %"PRIu32"", best_point);
return best_point;
}
uint32_t mspi_timing_psram_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs;
uint32_t best_point = s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, false);
ESP_DRAM_LOGD(TAG, "PSRAM timing tuning index: %"PRIu32"", best_point);
return best_point;
}
static mspi_timing_tuning_param_t s_flash_best_timing_tuning_config;
static mspi_timing_tuning_param_t s_psram_best_timing_tuning_config;
void mspi_timing_flash_set_best_tuning_config(const void *configs, uint8_t best_id)
{
s_flash_best_timing_tuning_config = ((const mspi_timing_config_t *)configs)->tuning_config_table[best_id];
}
void mspi_timing_psram_set_best_tuning_config(const void *configs, uint8_t best_id)
{
s_psram_best_timing_tuning_config = ((const mspi_timing_config_t *)configs)->tuning_config_table[best_id];
}
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Clear / Set
*-------------------------------------------------------------------------------------------------*/
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi)
{
s_set_flash_din_mode_num(0, 0, 0); //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg
s_set_flash_extra_dummy(0, 0);
//Won't touch SPI1 registers if not control_both_mspi
if (control_both_mspi) {
s_set_flash_extra_dummy(1, 0);
}
}
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi)
{
//SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg
s_set_flash_din_mode_num(0, s_flash_best_timing_tuning_config.spi_din_mode, s_flash_best_timing_tuning_config.spi_din_num);
s_set_flash_extra_dummy(0, s_flash_best_timing_tuning_config.extra_dummy_len);
if (control_both_mspi) {
s_set_flash_extra_dummy(1, s_flash_best_timing_tuning_config.extra_dummy_len);
} else {
//Won't touch SPI1 registers
}
}
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi)
{
(void)control_both_mspi; //for compatibility
s_set_psram_din_mode_num(0, 0, 0);
s_set_psram_extra_dummy(0, 0);
}
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi)
{
(void)control_both_mspi; //for compatibility
s_set_psram_din_mode_num(0, s_psram_best_timing_tuning_config.spi_din_mode, s_psram_best_timing_tuning_config.spi_din_num);
s_set_psram_extra_dummy(0, s_psram_best_timing_tuning_config.extra_dummy_len);
}
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
/*-------------------------------------------------------------------------------------------------
* To let upper lay (spi_flash_timing_tuning.c) to know the necessary timing registers
*-------------------------------------------------------------------------------------------------*/
/**
* Get the SPI1 Flash CS timing setting. The setup time and hold time are both realistic cycles.
* @note On ESP32-S3, SPI0/1 share the Flash CS timing registers. Therefore, we should not change these values.
* @note This function inform `spi_flash_timing_tuning.c` (driver layer) of the cycle,
* and other component (esp_flash driver) should get these cycle and configure the registers accordingly.
*/
void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time)
{
*setup_time = mspi_timing_ll_get_cs_setup_val(0);
*hold_time = mspi_timing_ll_get_cs_hold_val(0);
/**
* The logic here is, if setup_en / hold_en is false, then we return the realistic cycle number,
* which is 0. If true, then the realistic cycle number is (reg_value + 1)
*/
if (mspi_timing_ll_is_cs_setup_enabled(0)) {
*setup_time += 1;
} else {
*setup_time = 0;
}
if (mspi_timing_ll_is_cs_hold_enabled(0)) {
*hold_time += 1;
} else {
*hold_time = 0;
}
}
/**
* Get the SPI1 Flash clock setting.
* @note Similarly, this function inform `spi_flash_timing_tuning.c` (driver layer) of the clock setting,
* and other component (esp_flash driver) should get these and configure the registers accordingly.
*/
uint32_t mspi_timing_config_get_flash_clock_reg(void)
{
return mspi_timing_ll_get_clock_reg(1);
}
uint8_t mspi_timing_config_get_flash_extra_dummy(void)
{
#if MSPI_TIMING_FLASH_NEEDS_TUNING
return s_flash_best_timing_tuning_config.extra_dummy_len;
#else
return 0;
#endif
}
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
#define INTERVAL_IN_SECOND CONFIG_SPIRAM_TIMING_MEASURE_TEMPERATURE_INTERVAL_SECOND
// These arrays store the frequency scan result of the timing points
static int s_point_temp_range_min[MSPI_TIMING_CONFIG_NUM_MAX] = {0};
static int s_point_temp_range_max[MSPI_TIMING_CONFIG_NUM_MAX] = {0};
static uint32_t s_psram_best_point_idx = 0;
void mspi_timing_setting_temperature_adjustment_best_point(uint32_t best_point)
{
s_psram_best_point_idx = best_point;
}
static void mspi_timing_set_psram_point_idx(uint32_t point_idx)
{
mspi_timing_config_t timing_configs = {0};
s_psram_best_point_idx = point_idx;
mspi_timing_get_psram_tuning_configs(&timing_configs);
mspi_timing_psram_set_best_tuning_config(&timing_configs, point_idx);
mspi_timing_config_psram_set_tuning_regs(&timing_configs, point_idx);
}
static void set_timing_point(uint32_t point_idx)
{
// Disable cache in order that cache would not touch psram
spi_flash_disable_interrupts_caches_and_other_cpu();
mspi_timing_set_psram_point_idx(point_idx);
spi_flash_enable_interrupts_caches_and_other_cpu();
}
// A mean filter with window to make the temperature value smoother
static esp_err_t temperature_sensor_get_celsius_filtered(int16_t *temp_filtered)
{
const int filter_window_len = 7;
int16_t temp_arr[filter_window_len];
int16_t temp_sum = 0;
uint8_t temp_min_idx = 0;
uint8_t temp_max_idx = 0;
for (uint8_t idx = 0; idx < filter_window_len; idx++) {
temp_arr[idx] = temp_sensor_get_raw_value(NULL);
// record the index of the max and min temperature value
if (temp_arr[idx] > temp_arr[temp_max_idx]) {
temp_max_idx = idx;
}
if (temp_arr[idx] < temp_arr[temp_min_idx]) {
temp_min_idx = idx;
}
temp_sum += temp_arr[idx];
}
// remove the max and min temperature value
temp_sum -= temp_arr[temp_max_idx];
temp_sum -= temp_arr[temp_min_idx];
*temp_filtered = temp_sum / (filter_window_len - 2); // Don't calculate the temp_max and temp_min
return ESP_OK;
}
/**
* This task will:
* 1. Calculate temperature ranges of timing points
* 2. Monitor current temperature
* 3. Switch timing point if temperature is beyond the range
*/
void adjust_psram_timing_point_task(void *arg)
{
int16_t temp_refer = 0, temp_curr = 0;
(void)arg;
temperature_sensor_power_acquire();
temperature_sensor_get_celsius_filtered(&temp_refer); // get the refer temperature
int temperature_freq_radio = 5; // It means that the frequency will reduce by 1MHz when the temperature rises 5℃
const int temperature_safe_range = 5; // A temperature buffer zone to avoid switching timing points frequently
// make sure the frequency of current timing point is greater than freq_thres_max and less than freq_thres_min
const int freq_thres_max = 128; // threshold for maximum threshold frequency in specific temperature, should not change
const int freq_thres_min = 112; // threshold for minimum threshold frequency in specific temperature, should not change
int point_curr = s_psram_best_point_idx;
bool valid_point[MSPI_TIMING_CONFIG_NUM_MAX] = {false};
// 1. Get the delta of frequency we get at the specific temperature (freq_min) with the frequency min threshold (freq_diff = freq_thres_min - freq_min)
// 2. Convert frequency difference to temperature difference by radio (5), temperature_diff = freq_diff * radio
// 3. Calculate the range of temperature: temperature_thre_min = freq_min - temperature_diff
// Same for the temperature_thre_max.
// calculate temperature ranges of the every timing point
for (uint32_t point = 0; point < MSPI_TIMING_CONFIG_NUM_MAX; point++) {
if (psram_pass_freq_min[point] == 0 && psram_pass_freq_max[point] == 0) {
continue;
}
valid_point[point] = true;
uint32_t pass_freq_min = psram_pass_freq_min[point];
uint32_t pass_freq_max = psram_pass_freq_max[point];
if (pass_freq_max >= freq_thres_max) {
// frequency pass_freq_max greater than freq_thres_max, it will decrease to freq_thres_max until temperature rise to s_point_temp_range_max
s_point_temp_range_max[point] = temp_refer + (pass_freq_max - freq_thres_max) * temperature_freq_radio;
} else {
// frequency pass_freq_max less than freq_thres_max, it will increase to freq_thres_max until temperature drop to s_point_temp_range_max
s_point_temp_range_max[point] = temp_refer - (freq_thres_max - pass_freq_max) * temperature_freq_radio;
}
if (pass_freq_min <= freq_thres_min) {
// frequency pass_freq_min less than freq_thres_min, it will increase to freq_thres_min until temperature drop to s_point_temp_range_min
s_point_temp_range_min[point] = temp_refer - (freq_thres_min - pass_freq_min) * temperature_freq_radio;
} else {
// frequency pass_freq_min greater than freq_thres_min, it will decrease to freq_thres_min until temperature rise to s_point_temp_range_min
s_point_temp_range_min[point] = temp_refer + (pass_freq_min - freq_thres_min) * temperature_freq_radio;
}
}
for (uint32_t point = 0; point < MSPI_TIMING_CONFIG_NUM_MAX - 1; point++) {
if (valid_point[point] && valid_point[point + 1]) {
// check temperature intersection
if (s_point_temp_range_max[point] <= s_point_temp_range_min[point + 1]) {
ESP_DRAM_LOGE(TAG, "no temperature intersection of neighboring phase points");
abort();
}
}
}
while (1) {
vTaskDelay(INTERVAL_IN_SECOND * 1000 / portTICK_PERIOD_MS);
point_curr = s_psram_best_point_idx;
temperature_sensor_get_celsius_filtered(&temp_curr);
ESP_DRAM_LOGD(TAG, "Getting current temperature value is: %d", temp_curr);
// Switch timing point if temperature is beyond the range
if (s_point_temp_range_max[point_curr] == 0 && s_point_temp_range_min[point_curr] == 0) {
// The current timing point has no frequency scan result (psram_pass_freq_min and psram_pass_freq_min equal to 0),
// use the previous or next timing point's temperature range to decide what temperature to switch timing point.
// Use previous timing point's temperature range
if (point_curr - 1 >= 0) {
if (s_point_temp_range_max[point_curr - 1] != 0 && s_point_temp_range_min[point_curr - 1] != 0) {
if (temp_curr < (s_point_temp_range_max[point_curr - 1] - temperature_safe_range)) {
int point_next = point_curr - 1;
set_timing_point(point_next);
ESP_DRAM_LOGD(TAG, "PSRAM set timing point from %d to %ld\n", point_curr, point_next);
continue;
}
}
}
// Use next timing point's temperature range
if (point_curr + 1 < MSPI_TIMING_CONFIG_NUM_MAX) {
if (s_point_temp_range_max[point_curr + 1] != 0 && s_point_temp_range_min[point_curr + 1] != 0) {
if (temp_curr > s_point_temp_range_min[point_curr + 1] + temperature_safe_range) {
int point_next = point_curr + 1;
set_timing_point(point_next);
ESP_DRAM_LOGD(TAG, "PSRAM set timing point from %d to %ld\n", point_curr, point_next);
continue;
}
}
}
} else {
// Current temperature is greater than the range, switch to next timing point
if (point_curr + 1 < MSPI_TIMING_CONFIG_NUM_MAX) {
if (temp_curr > s_point_temp_range_max[point_curr]) {
int point_next = point_curr + 1;
set_timing_point(point_next);
ESP_DRAM_LOGD(TAG, "PSRAM set timing point from %d to %ld\n", point_curr, point_next);
continue;
}
}
// Current temperature is less than the range, switch to previous timing point
if (point_curr - 1 >= 0) {
if (temp_curr < s_point_temp_range_min[point_curr]) {
int point_next = point_curr - 1;
set_timing_point(point_next);
ESP_DRAM_LOGD(TAG, "PSRAM set timing point from %d to %ld\n", point_curr, point_next);
continue;
}
}
}
}
}
static esp_err_t psram_adjust_timing_point_via_tsens(void)
{
esp_rom_spiflash_chip_t *chip = &rom_spiflash_legacy_data->chip;
uint8_t vender_id = (chip->device_id >> 16) & 0xff;
if (vender_id == 0xC8 || vender_id == 0x20) {
xTaskCreatePinnedToCore(adjust_psram_timing_point_task, "adjust_psram_timing_point_task", 1024 * 5, NULL, configMAX_PRIORITIES - 2, NULL, 0);
} else {
ESP_DRAM_LOGE(TAG, "The flash model has not been verified support this feature, please contact espressif business support");
return ESP_ERR_NOT_SUPPORTED;
}
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(psram_adjust_timing_point_via_temperature, SECONDARY, BIT(0), 240)
{
return psram_adjust_timing_point_via_tsens();
}
#endif //CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
@@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/mspi_ll.h"
#define FLASH_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT
#define FLASH_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ
#define PSRAM_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT
#define PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ
/**
* Currently we only need these on chips with timing tuning
*/
//-------------------------------------MSPI Clock Setting-------------------------------------//
static void s_mspi_flash_set_core_clock(uint8_t spi_num, uint32_t core_clock_mhz)
{
mspi_timing_ll_set_core_clock(spi_num, core_clock_mhz);
}
static void s_mspi_psram_set_core_clock(uint8_t spi_num, uint32_t core_clock_mhz)
{
mspi_timing_ll_set_core_clock(spi_num, core_clock_mhz);
}
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
uint32_t core_clock_mhz = 0;
if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) {
core_clock_mhz = FLASH_LOW_SPEED_CORE_CLOCK_MHZ;
} else {
core_clock_mhz = FLASH_HIGH_SPEED_CORE_CLOCK_MHZ;
}
//SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
s_mspi_flash_set_core_clock(0, core_clock_mhz);
uint32_t freqdiv = core_clock_mhz / flash_freq_mhz;
assert(freqdiv > 0);
mspi_timing_ll_set_flash_clock(0, freqdiv);
if (control_both_mspi) {
mspi_timing_ll_set_flash_clock(1, freqdiv);
}
}
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
(void)control_both_mspi; // for compatibility
uint32_t core_clock_mhz = 0;
if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) {
core_clock_mhz = PSRAM_LOW_SPEED_CORE_CLOCK_MHZ;
} else {
core_clock_mhz = PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ;
}
//SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
s_mspi_psram_set_core_clock(0, core_clock_mhz);
uint32_t freqdiv = core_clock_mhz / psram_freq_mhz;
assert(freqdiv > 0);
mspi_timing_ll_set_psram_clock(0, freqdiv);
}
@@ -0,0 +1,266 @@
/*
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_assert.h"
#include "esp_flash_partitions.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MSPI_TIMING_MSPI1_IS_INVOLVED 1 //This means esp flash driver needs to be notified
#define MSPI_TIMING_CONFIG_NUM_MAX 20 //This should be larger than the max available timing config num
#define MSPI_TIMING_TEST_DATA_LEN 64
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR ESP_BOOTLOADER_OFFSET
/**
* @note BACKGROUND:
*
* The SPI FLASH module clock and SPI PSRAM module clock is divided from the SPI core clock, core clock is from system clock:
*
* PLL ----| |---- FLASH Module Clock
* XTAL ----|----> Core Clock ---->|
* RTC8M ----| |---- PSRAM Module Clock
*
*
* DDR stands for double data rate, MSPI samples at both posedge and negedge. So the real speed will be doubled.
* Speed from high to low: 120M DDR > 80M DDR > 120 SDR > 80M SDR > ...
*
* Module with speed lower than 120M SDR doesn't need to be tuned
*
* @note LIMITATION:
* How to determine the core clock on 728. There are 2 limitations.
*
* 1. MSPI FLASH and PSRAM share the core clock register. Therefore:
* MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ == MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ
*
* 2. DDR mode requires the core clock divider (core_clk / div = module_clk) to be power of 2.
*/
//--------------------------------------FLASH Sampling Mode --------------------------------------//
#define MSPI_TIMING_FLASH_DTR_MODE CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
#define MSPI_TIMING_FLASH_STR_MODE CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR
//--------------------------------------FLASH Module Clock --------------------------------------//
#if CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 80
#else //CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 120
#endif
//------------------------------------FLASH Needs Tuning or not-------------------------------------//
#if MSPI_TIMING_FLASH_DTR_MODE
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 40)
#elif MSPI_TIMING_FLASH_STR_MODE
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 80)
#endif
//--------------------------------------PSRAM Sampling Mode --------------------------------------//
#define MSPI_TIMING_PSRAM_DTR_MODE CONFIG_SPIRAM_MODE_OCT
#define MSPI_TIMING_PSRAM_STR_MODE !CONFIG_SPIRAM_MODE_OCT
//--------------------------------------PSRAM Module Clock --------------------------------------//
#if CONFIG_SPIRAM
#if CONFIG_SPIRAM_SPEED_40M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 40
#elif CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 80
#else //CONFIG_SPIRAM_SPEED_120M
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 120
#endif
#else //Disable PSRAM
#define MSPI_TIMING_PSRAM_MODULE_CLOCK 10 //Define this to 10MHz, because we rely on `MSPI_TIMING_PSRAM_MODULE_CLOCK` macro for calculation and check below, see `Determine the Core Clock` chapter
#endif
//------------------------------------PSRAM Needs Tuning or not-------------------------------------//
#if MSPI_TIMING_PSRAM_DTR_MODE
#define MSPI_TIMING_PSRAM_NEEDS_TUNING (MSPI_TIMING_PSRAM_MODULE_CLOCK > 40)
#elif MSPI_TIMING_PSRAM_STR_MODE
#define MSPI_TIMING_PSRAM_NEEDS_TUNING (MSPI_TIMING_PSRAM_MODULE_CLOCK > 80)
#endif
/**
* @note Define A feasible core clock below: MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ and MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ
*/
/**
* Due to MSPI core clock is used by both MSPI Flash and PSRAM clock,
* define the STR/DTR mode here for selecting the core clock:
* @note If either Flash or PSRAM, or both of them are set to DTR mode, then we use DIV 2
*/
#if (MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE)
#define MSPI_TIMING_CORE_CLOCK_DIV 2
#else //#if (MSPI_TIMING_FLASH_STR_MODE && (MSPI_TIMING_PSRAM_STR_MODE))
#define MSPI_TIMING_CORE_CLOCK_DIV 1
#endif
///////////////////////////////////// FLASH CORE CLOCK /////////////////////////////////////
//FLASH 80M DTR
#if MSPI_TIMING_FLASH_DTR_MODE && CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 160
#endif
//FLASH 120M DTR
#if MSPI_TIMING_FLASH_DTR_MODE && CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 240
#endif
//FLASH 120M STR
#if MSPI_TIMING_FLASH_STR_MODE && CONFIG_ESPTOOLPY_FLASHFREQ_120M
#if (MSPI_TIMING_CORE_CLOCK_DIV == 2)
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 240
#elif (MSPI_TIMING_CORE_CLOCK_DIV == 1)
#define MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ 120
#endif
#endif //FLASH 120M STR
///////////////////////////////////// PSRAM CORE CLOCK /////////////////////////////////////
//PSRAM 80M DTR
#if MSPI_TIMING_PSRAM_DTR_MODE && CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 160
#endif
//PSRAM 120M STR
#if MSPI_TIMING_PSRAM_STR_MODE && CONFIG_SPIRAM_SPEED_120M
#if (MSPI_TIMING_CORE_CLOCK_DIV == 2)
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 240
#elif (MSPI_TIMING_CORE_CLOCK_DIV == 1)
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 120
#endif
#endif //PSRAM 120M STR
//PSRAM 120M STR
#if MSPI_TIMING_PSRAM_DTR_MODE && CONFIG_SPIRAM_SPEED_120M
#define MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ 240
#endif //PSRAM 120M DTR
//------------------------------------------Determine the Core Clock-----------------------------------------------//
/**
* @note
* Limitation 1:
* On 728, MSPI FLASH and PSRAM share the core clock register. Therefore,
* the expected CORE CLOCK frequencies should be the same.
*/
#if MSPI_TIMING_FLASH_NEEDS_TUNING && MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ == MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ
//If only FLASH needs tuning, the core clock COULD be as FLASH expected
#elif MSPI_TIMING_FLASH_NEEDS_TUNING && !MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ % MSPI_TIMING_PSRAM_MODULE_CLOCK == 0, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_FLASH_EXPECTED_CORE_CLK_MHZ
//If only PSRAM needs tuning, the core clock COULD be as PSRAM expected
#elif !MSPI_TIMING_FLASH_NEEDS_TUNING && MSPI_TIMING_PSRAM_NEEDS_TUNING
ESP_STATIC_ASSERT(MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ % MSPI_TIMING_FLASH_MODULE_CLOCK == 0, "FLASH and PSRAM Mode configuration are not supported");
#define MSPI_TIMING_CORE_CLOCK_MHZ MSPI_TIMING_PSRAM_EXPECTED_CORE_CLK_MHZ
#else
#define MSPI_TIMING_CORE_CLOCK_MHZ 80
#endif
/**
* @note
* Limitation 2: DDR mode requires the core clock divider (core_clk / div = module_clk) to be power of 2.
*/
#define CHECK_POWER_OF_2(n) ((((n) & ((~(n)) + 1))) == (n))
#if MSPI_TIMING_FLASH_DTR_MODE
ESP_STATIC_ASSERT(CHECK_POWER_OF_2(MSPI_TIMING_CORE_CLOCK_MHZ / MSPI_TIMING_FLASH_MODULE_CLOCK), "FLASH and PSRAM Mode configuration are not supported");
#endif
#if MSPI_TIMING_PSRAM_DTR_MODE
ESP_STATIC_ASSERT(CHECK_POWER_OF_2(MSPI_TIMING_CORE_CLOCK_MHZ / MSPI_TIMING_PSRAM_MODULE_CLOCK), "FLASH and PSRAM Mode configuration are not supported");
#endif
//------------------------------------------Helper Macros to get FLASH/PSRAM tuning configs-----------------------------------------------//
#define __GET_TUNING_CONFIG(type, core_clock, module_clock, mode) \
(mspi_timing_config_t) { .tuning_config_table = MSPI_TIMING_##type##_CONFIG_TABLE_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.available_config_num = MSPI_TIMING_##type##_CONFIG_NUM_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.default_config_id = MSPI_TIMING_##type##_DEFAULT_CONFIG_ID_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode }
#define _GET_TUNING_CONFIG(type, core_clock, module_clock, mode) __GET_TUNING_CONFIG(type, core_clock, module_clock, mode)
#define MSPI_TIMING_FLASH_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(FLASH, core_clock_mhz, module_clock_mhz, mode)
#define MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(PSRAM, core_clock_mhz, module_clock_mhz, mode)
/**
* Timing Tuning Parameters
*/
//FLASH: core clock 160M, module clock 40M, DTR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_160M_MODULE_CLK_40M_DTR_MODE {{1, 0, 0}, {0, 0, 0}, {2, 1, 1}, {2, 0, 1}, {2, 2, 2}, {2, 1, 2}, {1, 0, 1}, {0, 0, 1}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_160M_MODULE_CLK_40M_DTR_MODE 8
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_160M_MODULE_CLK_40M_DTR_MODE 2
//FLASH: core clock 160M, module clock 80M, DTR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_160M_MODULE_CLK_80M_DTR_MODE {{0, 0, 0}, {4, 2, 2}, {2, 1, 2}, {4, 1, 2}, {1, 0, 1}, {4, 0, 2}, {0, 0, 1}, {4, 2, 3}, {2, 1, 3}, {4, 1, 3}, {1, 0, 2}, {4, 0, 3}, {0, 0, 2}, {4, 2, 4}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_160M_MODULE_CLK_80M_DTR_MODE 14
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_160M_MODULE_CLK_80M_DTR_MODE 1
//FLASH: core clock 240M, module clock 120M, DTR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE {{0, 0, 0}, {4, 1, 2}, {1, 0, 1}, {4, 0, 2}, {0, 0, 1}, {4, 1, 3}, {1, 0, 2}, {4, 0, 3}, {0, 0, 2}, {4, 1, 4}, {1, 0, 3}, {4, 0, 4}, {0, 0, 3}, {4, 1, 5}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE 14
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE 1
//FLASH: core clock 160M, module clock 80M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_160M_MODULE_CLK_80M_STR_MODE {{1, 0, 0}, {0, 0, 0}, {2, 1, 1}, {2, 0, 1}, {2, 2, 2}, {2, 1, 2}, {1, 0, 1}, {0, 0, 1}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_160M_MODULE_CLK_80M_STR_MODE 8
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_160M_MODULE_CLK_80M_STR_MODE 2
//FLASH: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {1, 0, 1}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {1, 0, 2}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {1, 0, 3}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 2
//FLASH: core clock 240M, module clock 120M, STR mode
#define MSPI_TIMING_FLASH_CONFIG_TABLE_CORE_CLK_240M_MODULE_CLK_120M_STR_MODE {{1, 0, 0}, {0, 0, 0}, {1, 1, 1}, {2, 3, 2}, {1, 0, 1}, {0, 0, 1}, {1, 1, 2}, {2, 3, 3}, {1, 0, 2}, {0, 0, 2}, {1, 1, 3}, {2, 3, 4}}
#define MSPI_TIMING_FLASH_CONFIG_NUM_CORE_CLK_240M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_FLASH_DEFAULT_CONFIG_ID_CORE_CLK_240M_MODULE_CLK_120M_STR_MODE 2
//PSRAM: core clock 80M, module clock 40M, DTR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_80M_MODULE_CLK_40M_DTR_MODE {{1, 0, 0}, {2, 1, 1}, {2, 0, 1}, {0, 0, 0}, {3, 1, 1}, {3, 0, 1}, {1, 0, 1}, {2, 1, 2}, {2, 0, 2}, {0, 0, 1}, {3, 1, 2}, {3, 0, 2}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_80M_MODULE_CLK_40M_DTR_MODE 12
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_80M_MODULE_CLK_40M_DTR_MODE 4
//PSRAM: core clock 160M, module clock 80M, DTR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_160M_MODULE_CLK_80M_DTR_MODE {{0, 0, 0}, {4, 2, 2}, {2, 1, 2}, {4, 1, 2}, {1, 0, 1}, {4, 0, 2}, {0, 0, 1}, {4, 2, 3}, {2, 1, 3}, {4, 1, 3}, {1, 0, 2}, {4, 0, 3}, {0, 0, 2}, {4, 2, 4}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_160M_MODULE_CLK_80M_DTR_MODE 14
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_160M_MODULE_CLK_80M_DTR_MODE 5
//PSRAM: core clock 240M, module clock 120M, STR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_240M_MODULE_CLK_120M_STR_MODE {{1, 0, 0}, {0, 0, 0}, {1, 1, 1}, {2, 3, 2}, {1, 0, 1}, {0, 0, 1}, {1, 1, 2}, {2, 3, 3}, {1, 0, 2}, {0, 0, 2}, {1, 1, 3}, {2, 3, 4}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_240M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_240M_MODULE_CLK_120M_STR_MODE 2
//PSRAM: core clock 120M, module clock 120M, STR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE {{2, 0, 1}, {0, 0, 0}, {2, 2, 2}, {1, 0, 1}, {2, 0, 2}, {0, 0, 1}, {2, 2, 3}, {1, 0, 2}, {2, 0, 3}, {0, 0, 2}, {2, 2, 4}, {1, 0, 3}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 2
//PSRAM: core clock 240M, module clock 120M, DTR mode
#define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE {{0, 0, 0}, {4, 1, 2}, {1, 0, 1}, {4, 0, 2}, {0, 0, 1}, {4, 1, 3}, {1, 0, 2}, {4, 0, 3}, {0, 0, 2}, {4, 1, 4}, {1, 0, 3}, {4, 0, 4}, {0, 0, 3}, {4, 1, 5}}
#define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE 14
#define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE 1
//------------------------------------------Frequency Scanning Related-----------------------------------------------//
/**
* On ESP32S3, only module clock 120M, DDR mode needs frequency scan. Frequency scanning is to get the max workable PLL
* frequency under each successful timing tuning configuration. PLL frequency may fluctuate under high temperature,
* this method is to get the tuning configuration that can work under higher PLL frequency.
*/
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
#define MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN 424
#else
#define MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN 440
#endif
#define MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX 600
#define MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_LOW 448
#define MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_HIGH 520
#define MSPI_TIMING_PLL_FREQ_SCAN_WIDTH_MHZ 160
#define MSPI_TIMING_PLL_FREQ_SCAN_STEP_MHZ_MODULE_CLK_120M 8
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,11 @@
target_include_directories(${COMPONENT_LIB} PUBLIC .)
set(srcs)
if(NOT BOOTLOADER_BUILD)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi_timing_config.c")
endif()
endif()
target_sources(${COMPONENT_LIB} PRIVATE "${srcs}")
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#include "soc/hp_sys_clkrst_struct.h"
ESP_LOG_ATTR_TAG(TAG, "MSPI Timing");
//-------------------------------------MSPI Clock Setting-------------------------------------//
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
uint32_t freqdiv = MSPI_TIMING_MPLL_FREQ_MHZ / MSPI_TIMING_CORE_CLOCK_DIV / psram_freq_mhz;
assert(freqdiv > 0);
ESP_DRAM_LOGD(TAG, "psram_freq_mhz: %" PRIu32 " mhz, bus clock div: %" PRIu32, psram_freq_mhz, freqdiv);
PERIPH_RCC_ATOMIC() {
//MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here.
psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
psram_ctrlr_ll_set_core_clock_div(PSRAM_CTRLR_LL_MSPI_ID_2, MSPI_TIMING_CORE_CLOCK_DIV);
psram_ctrlr_ll_set_bus_clock(PSRAM_CTRLR_LL_MSPI_ID_3, freqdiv);
psram_ctrlr_ll_set_bus_clock(PSRAM_CTRLR_LL_MSPI_ID_2, freqdiv);
}
}
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
#if MSPI_TIMING_FLASH_NEEDS_TUNING
assert(HP_SYS_CLKRST.peri_clk_ctrl00.reg_flash_clk_src_sel == 1);
uint32_t core_clock_mhz = MSPI_TIMING_SPLL_FREQ_MHZ / MSPI_TIMING_LL_HP_FLASH_CORE_CLK_DIV;
assert(core_clock_mhz == 120);
uint32_t freqdiv = core_clock_mhz / flash_freq_mhz;
PERIPH_RCC_ATOMIC() {
//core clock shared among SPI0 / SPI1
mspi_timing_ll_set_flash_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
}
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_0, freqdiv);
if (control_both_mspi) {
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_1, freqdiv);
}
mspi_timing_ll_mask_invalid_dqs(MSPI_TIMING_LL_MSPI_ID_0, true);
mspi_timing_ll_mask_invalid_dqs(MSPI_TIMING_LL_MSPI_ID_1, true);
#endif
}
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#define MSPI_TIMING_MSPI1_IS_INVOLVED CONFIG_ESPTOOLPY_FLASHFREQ_120M //This means esp flash driver needs to be notified
#define MSPI_TIMING_CONFIG_NUM_MAX 32 //This should be larger than the max available timing config num
#define MSPI_TIMING_TEST_DATA_LEN 128
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0x80
#define MSPI_TIMING_DELAYLINE_TEST_NUMS 100
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR CONFIG_BOOTLOADER_OFFSET_IN_FLASH
#define MSPI_TIMING_CORE_CLOCK_DIV 1
#if CONFIG_SPIRAM_SPEED_250M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 500
#elif CONFIG_SPIRAM_SPEED_200M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#elif CONFIG_SPIRAM_SPEED_100M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#else
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#endif
#define MSPI_TIMING_SPLL_FREQ_MHZ 480
#define MSPI_TIMING_PSRAM_DTR_MODE CONFIG_SPIRAM_MODE_OCT
#define MSPI_TIMING_FLASH_STR_MODE 1
#if CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 80
#else //CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 120
#endif
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 80)
#if MSPI_TIMING_FLASH_NEEDS_TUNING
#define MSPI_TIMING_FLASH_CORE_CLOCK_MHZ 120
#else
#define MSPI_TIMING_FLASH_CORE_CLOCK_MHZ 80
#endif
//------------------------------------------Helper Macros to get FLASH/PSRAM tuning configs-----------------------------------------------//
#define __GET_TUNING_CONFIG(type, core_clock, module_clock, mode) \
(mspi_timing_config_t) { .tuning_config_table = MSPI_TIMING_##type##_CONFIG_TABLE_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.available_config_num = MSPI_TIMING_##type##_CONFIG_NUM_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.flash_default_config_id = MSPI_TIMING_##type##_DEFAULT_CONFIG_ID_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode }
#define _GET_TUNING_CONFIG(type, core_clock, module_clock, mode) __GET_TUNING_CONFIG(type, core_clock, module_clock, mode)
#define MSPI_TIMING_FLASH_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(FLASH, core_clock_mhz, module_clock_mhz, mode)
@@ -0,0 +1,163 @@
/*
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI DQS
*/
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#if SOC_MEMSPI_TIMING_TUNING_BY_DQS
#include "esp_private/mspi_timing_impl_types.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/mspi_ll.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_MEMSPI_TIMING_TUNING_BY_DQS
#define IS_DDR 1
#define IS_SDR (!IS_DDR)
/*-------------------------------------------------------------------------------------------------
* Timing Required APIs
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Init MSPI for PSRAM timing tuning
*
* @param[in] psram_freq_mhz PSRAM frequency in MHz
*/
void mspi_timing_psram_init(uint32_t psram_freq_mhz);
/**
* @brief Prepare reference data buffer
*/
void mspi_timing_config_psram_prepare_reference_data(uint8_t *buf, uint32_t len);
/**
* @brief Configure PSRAM to write data via MSPI3
*
* @param[in] buf buffer
* @param[in] addr address
* @param[in] len length
*/
void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len);
/**
* @brief Configure PSRAM to read data via MSPI3
*
* @param[out] buf buffer
* @param[in] addr address
* @param[in] len length
*/
void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len);
/**
* @brief Get PSRAM tuning configurations for phase
*
* @param[out] config Pointer to PSRAM tuning configurations
*/
void mspi_timing_get_psram_tuning_phases(mspi_timing_config_t *configs);
/**
* @brief Tune PSRAM timing registers for MSPI3 accessing PSRAM
*
* @param[in] configs Timing configs
* @param[in] id Config ID
*/
void mspi_timing_config_psram_set_tuning_phase(const void *configs, uint8_t id);
/**
* @brief Select PSRAM best tuning configuration
*
* @param[in] configs Timing tuning configuration table
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t mspi_timing_psram_select_best_tuning_phase(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best PSRAM tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning configs
* @param[in] best_id Best config ID
*/
void mspi_timing_psram_set_best_tuning_phase(const void *configs, uint8_t best_id);
/**
* @brief Get PSRAM tuning configurations for delayline
*
* @param[out] config Pointer to PSRAM tuning configurations
*/
void mspi_timing_get_psram_tuning_delaylines(mspi_timing_config_t *configs);
/**
* @brief Tune PSRAM timing registers for MSPI3 accessing PSRAM
*
* @param[in] configs Timing configs
* @param[in] id Config ID
*/
void mspi_timing_config_psram_set_tuning_delayline(const void *configs, uint8_t id);
/**
* @brief Select PSRAM best tuning configuration
*
* @param[in] configs Timing tuning configuration table
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t mspi_timing_psram_select_best_tuning_delayline(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best PSRAM tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning configs
* @param[in] best_id Best config ID
*/
void mspi_timing_psram_set_best_tuning_delayline(const void *configs, uint8_t best_id);
/*-------------------------------------------------------------------------------------------------
* General Timing APIs
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Set PSRAM timing tuning settings
*
* This is used when the system is going to high speed mode / MSPI needs to be run in high speed
*
* @param[in] control_both_mspi Control MSPI3 as well
*/
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi);
/**
* @brief Clear PSRAM timing tuning settings
*
* This is used when the system is going into low speed mode / MSPI doesn't need to be run in high speed
*
* @param[in] control_both_mspi Control MSPI3 as well
*/
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi);
#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_DQS
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI delay
*/
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#if SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
#include "esp_private/mspi_timing_impl_types.h"
#include "mspi_timing_tuning_configs.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
#define IS_DDR 1
#define IS_SDR (!IS_DDR)
/*-------------------------------------------------------------------------------------------------
* Timing Required APIs
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Get Flash tuning all configurations
*
* @param[out] config Pointer to Flash tuning configurations
*/
void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config);
/**
* @brief Init MSPI for Flash timing tuning
*
* @param[in] flash_freq_mhz Flash frequency in MHz
*/
void mspi_timing_flash_init(uint32_t flash_freq_mhz);
/**
* @brief Tune Flash timing registers for SPI1 accessing Flash
*
* @param[in] configs Timing configs
* @param[in] id Config ID
*/
void mspi_timing_config_flash_set_tuning_regs(const void *configs, uint8_t id);
/**
* @brief Configure Flash to read data via SPI1
*
* @param[out] buf buffer
* @param[in] addr address
* @param[in] len length
*/
void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len);
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Selection
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Select Flash best tuning configuration
*
* @param[in] configs Timing tuning configuration table
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best Flash tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning configs
* @param[in] best_id Best config ID
*/
void mspi_timing_flash_set_best_tuning_config(const void *configs, uint8_t best_id);
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Clear / Set
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Clear Flash timing tuning settings
*
* This is used when the system is going into low speed mode / MSPI doesn't need to be run in high speed
*
* @param[in] control_both_mspi Control SPI1 as well
*/
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi);
/**
* @brief Set Flash timing tuning settings
*
* This is used when the system is going to high speed mode / MSPI needs to be run in high speed
*
* @param[in] control_both_mspi Control SPI1 as well
*/
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi);
/*-------------------------------------------------------------------------------------------------
* APIs for coordination with ESP Flash driver
*-------------------------------------------------------------------------------------------------*/
/**
* SPI1 register info get APIs. These APIs inform `spi_flash_timing_tuning.c` (driver layer) of the SPI1 flash settings.
* In this way, other components (e.g.: esp_flash driver) can get the info from it (`spi_flash_timing_tuning.c`).
*/
/**
* @brief Get CS timing
*
* @param[out] setup_time Setup time
* @param[out] hold_time Hold time
*/
void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time);
/**
* @brief Get Flash clock reg val
*
* @return Flash clock reg val
*/
uint32_t mspi_timing_config_get_flash_clock_reg(void);
/**
* @brief Get Flash extra dummy len
*
* @return Flash extra dummy
*/
uint8_t mspi_timing_config_get_flash_extra_dummy(void);
/**
* @brief Get Flash dummy rin_reg
*
* @return Flash dummy rin_reg
*/
uint32_t mspi_timing_config_get_flash_fdummy_rin(void);
#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,239 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI delay
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
#include "esp_private/mspi_timing_impl_types.h"
#include "mspi_timing_tuning_configs.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
#define IS_DDR 1
#define IS_SDR (!IS_DDR)
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
/*-------------------------------------------------------------------------------------------------
* Timing Required APIs
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Get Flash tuning all configurations
*
* @param[out] config Pointer to Flash tuning configurations
*/
void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config);
/**
* @brief Init MSPI for Flash timing tuning
*
* @param[in] flash_freq_mhz Flash frequency in MHz
*/
void mspi_timing_flash_init(uint32_t flash_freq_mhz);
/**
* @brief Tune Flash timing registers for SPI1 accessing Flash
*
* @param[in] configs Timing configs
* @param[in] id Config ID
*/
void mspi_timing_config_flash_set_tuning_regs(const void *configs, uint8_t id);
/**
* @brief Configure Flash to read data via SPI1
*
* @param[out] buf buffer
* @param[in] addr address
* @param[in] len length
*/
void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len);
/**
* @brief Get PSRAM tuning all configurations
*
* @param[out] config Pointer to PSRAM tuning configurations
*/
void mspi_timing_get_psram_tuning_configs(mspi_timing_config_t *config);
/**
* @brief Init MSPI for PSRAM timing tuning
*
* @param[in] psram_freq_mhz PSRAM frequency in MHz
*/
void mspi_timing_psram_init(uint32_t psram_freq_mhz);
/**
* @brief Tune PSRAM timing registers for SPI1 accessing PSRAM
*
* @param[in] configs Timing configs
* @param[in] id Config ID
*/
void mspi_timing_config_psram_set_tuning_regs(const void *configs, uint8_t id);
/**
* @brief Prepare reference data buffer
*/
void mspi_timing_config_psram_prepare_reference_data(uint8_t *buf, uint32_t len);
/**
* @brief Configure PSRAM to write data via SPI1
*
* @param[in] buf buffer
* @param[in] addr address
* @param[in] len length
*/
void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len);
/**
* @brief Configure PSRAM to read data via SPI1
*
* @param[out] buf buffer
* @param[in] addr address
* @param[in] len length
*/
void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len);
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Selection
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Select Flash best tuning configuration
*
* @param[in] configs Timing tuning configuration table
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best Flash tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning configs
* @param[in] best_id Best config ID
*/
void mspi_timing_flash_set_best_tuning_config(const void *configs, uint8_t best_id);
/**
* @brief Select PSRAM best tuning configuration
*
* @param[in] configs Timing tuning configuration table
* @param[in] consecutive_length Length of the consecutive successful sample results
* @param[in] end End of the consecutive successful sample results
* @param[in] reference_data Reference data
* @param[in] is_ddr DDR or SDR
*
* @return Best config ID
*/
uint32_t mspi_timing_psram_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/**
* @brief Set best PSRAM tuning configs.
* After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly
*
* @param[in] configs Timing tuning configs
* @param[in] best_id Best config ID
*/
void mspi_timing_psram_set_best_tuning_config(const void *configs, uint8_t best_id);
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Clear / Set
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Clear Flash timing tuning settings
*
* This is used when the system is going into low speed mode / MSPI doesn't need to be run in high speed
*
* @param[in] control_both_mspi Control SPI1 as well
*/
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi);
/**
* @brief Set Flash timing tuning settings
*
* This is used when the system is going to high speed mode / MSPI needs to be run in high speed
*
* @param[in] control_both_mspi Control SPI1 as well
*/
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi);
/**
* @brief Clear PSRAM timing tuning settings
*
* This is used when the system is going into low speed mode / MSPI doesn't need to be run in high speed
*
* @param[in] control_both_mspi Control SPI1 as well
*/
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi);
/**
* @brief Set PSRAM timing tuning settings
*
* This is used when the system is going to high speed mode / MSPI needs to be run in high speed
*
* @param[in] control_both_mspi Control SPI1 as well
*/
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi);
/*-------------------------------------------------------------------------------------------------
* APIs for coordination with ESP Flash driver
*-------------------------------------------------------------------------------------------------*/
/**
* SPI1 register info get APIs. These APIs inform `spi_flash_timing_tuning.c` (driver layer) of the SPI1 flash settings.
* In this way, other components (e.g.: esp_flash driver) can get the info from it (`spi_flash_timing_tuning.c`).
*/
/**
* @brief Get CS timing
*
* @param[out] setup_time Setup time
* @param[out] hold_time Hold time
*/
void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time);
/**
* @brief Get Flash clock reg val
*
* @return Flash clock reg val
*/
uint32_t mspi_timing_config_get_flash_clock_reg(void);
/**
* @brief Get Flash extra dummy len
*
* @return Flash extra dummy
*/
uint8_t mspi_timing_config_get_flash_extra_dummy(void);
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
/**
* @brief Set best point for psram timing tuning dynamic temperature scheme
*/
void mspi_timing_setting_temperature_adjustment_best_point(uint32_t best_point);
#endif // CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains types for MSPI timing tuning
*/
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#if SOC_MEMSPI_TIMING_TUNING_BY_DQS || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY || SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
#include "hal/mspi_ll.h"
#include "mspi_timing_tuning_configs.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_MEMSPI_TIMING_TUNING_BY_DQS || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
/**
* Delayline
*/
typedef struct {
uint8_t data_delayline;
uint8_t dqs_delayline;
} __attribute__((packed)) delayline_config_t;
/**
* MSPI timing tuning registers.
*/
typedef struct {
uint8_t spi_din_mode; // input signal delay mode
uint8_t spi_din_num; // input signal delay number
uint8_t extra_dummy_len; // extra dummy length
} mspi_timing_tuning_param_t;
/**
* MSPI timing tuning configurations
*/
typedef struct {
//for psram
mspi_ll_dqs_phase_t phase[MSPI_LL_DQS_PHASE_MAX];
delayline_config_t delayline_table[MSPI_TIMING_CONFIG_NUM_MAX];
//for flash
mspi_timing_tuning_param_t tuning_config_table[MSPI_TIMING_CONFIG_NUM_MAX];
uint32_t flash_default_config_id;
//common
union {
uint32_t available_config_num;
uint32_t available_phase_num;
};
} mspi_timing_config_t;
#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_DQS || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
/**
* MSPI timing tuning registers.
* Upper layer rely on these 3 registers to tune the timing.
*/
typedef struct {
uint8_t spi_din_mode; // input signal delay mode
uint8_t spi_din_num; // input signal delay number
uint8_t extra_dummy_len; // extra dummy length
} mspi_timing_tuning_param_t;
/**
* MSPI timing tuning configurations
*/
typedef struct {
mspi_timing_tuning_param_t tuning_config_table[MSPI_TIMING_CONFIG_NUM_MAX]; // Available timing tuning configs
uint32_t available_config_num; // Available timing tuning config numbers
uint32_t default_config_id; // If tuning fails, we use this one as default
} mspi_timing_config_t;
#endif
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,242 @@
/*
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI dqs
* This file will only be built, when `SOC_MEMSPI_TIMING_TUNING_BY_DQS == 1`
*/
#include <stdint.h>
#include <string.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "esp_private/mspi_timing_by_dqs.h"
#include "mspi_timing_tuning_configs.h"
#include "esp_private/mspi_timing_config.h"
#include "esp_private/rtc_clk.h"
#include "esp_private/periph_ctrl.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#define AP_HEX_PSRAM_SYNC_READ 0x0000
#define AP_HEX_PSRAM_SYNC_WRITE 0x8080
#define AP_HEX_PSRAM_RD_CMD_BITLEN 16
#define AP_HEX_PSRAM_WR_CMD_BITLEN 16
#define AP_HEX_PSRAM_ADDR_BITLEN 32
#if CONFIG_SPIRAM_SPEED_250M
#define AP_HEX_PSRAM_RD_DUMMY_BITLEN (2*(18-1))
#define AP_HEX_PSRAM_WR_DUMMY_BITLEN (2*(9-1))
#elif CONFIG_SPIRAM_SPEED_200M
#define AP_HEX_PSRAM_RD_DUMMY_BITLEN (2*(14-1))
#define AP_HEX_PSRAM_WR_DUMMY_BITLEN (2*(7-1))
#else
#define AP_HEX_PSRAM_RD_DUMMY_BITLEN (2*(10-1))
#define AP_HEX_PSRAM_WR_DUMMY_BITLEN (2*(5-1))
#endif
#define WRONG_DELAYLINE 16
ESP_LOG_ATTR_TAG(TAG, "MSPI DQS");
const static uint32_t s_test_data[MSPI_TIMING_TEST_DATA_LEN] = {0x7f786655, 0xa5ff005a, 0x3f3c33aa, 0xa5ff5a00, 0x1f1e9955, 0xa5005aff, 0x0f0fccaa, 0xa55a00ff,
0x07876655, 0xffa55a00, 0x03c333aa, 0xff00a55a, 0x01e19955, 0xff005aa5, 0x00f0ccaa, 0xff5a00a5,
0x80786655, 0x00a5ff5a, 0xc03c33aa, 0x00a55aff, 0xe01e9355, 0x00ff5aa5, 0xf00fccaa, 0x005affa5,
0xf8876655, 0x5aa5ff00, 0xfcc333aa, 0x5affa500, 0xfee19955, 0x5a00a5ff, 0x11f0ccaa, 0x5a00ffa5
};
const static mspi_timing_config_t s_test_delayline_config = {
.delayline_table = {{0, 15}, {0, 14}, {0, 13}, {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}, {0, 2}, {0, 1},
{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}
},
.available_config_num = 31,
};
static mspi_ll_dqs_phase_t s_psram_best_phase = MSPI_LL_DQS_PHASE_MAX;
static delayline_config_t s_psram_best_delayline = {WRONG_DELAYLINE, WRONG_DELAYLINE};
void mspi_timing_psram_init(uint32_t psram_freq_mhz)
{
psram_ctrlr_ll_enable_variable_dummy(PSRAM_CTRLR_LL_MSPI_ID_3, false);
mspi_timing_config_set_psram_clock(psram_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
}
void mspi_timing_config_psram_prepare_reference_data(uint8_t *buf, uint32_t len)
{
assert(len == MSPI_TIMING_TEST_DATA_LEN);
memcpy(buf, &s_test_data, len);
}
void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
uint8_t *w_ptr = buf;
while (len) {
int len_to_send = MIN(len, PSRAM_CTRLR_LL_FIFO_MAX_BYTES);
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_SYNC_WRITE, AP_HEX_PSRAM_WR_CMD_BITLEN,
addr, AP_HEX_PSRAM_ADDR_BITLEN,
AP_HEX_PSRAM_WR_DUMMY_BITLEN,
w_ptr, len_to_send * 8,
NULL, 0,
false);
w_ptr += len_to_send;
addr += len_to_send;
len -= len_to_send;
}
}
void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
uint8_t *r_ptr = buf;
while (len) {
int len_to_recv = MIN(len, PSRAM_CTRLR_LL_FIFO_MAX_BYTES);
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_SYNC_READ, AP_HEX_PSRAM_RD_CMD_BITLEN,
addr, AP_HEX_PSRAM_ADDR_BITLEN,
AP_HEX_PSRAM_RD_DUMMY_BITLEN,
NULL, 0,
r_ptr, len_to_recv * 8,
false);
r_ptr += len_to_recv;
addr += len_to_recv;
len -= len_to_recv;
}
}
void mspi_timing_get_psram_tuning_phases(mspi_timing_config_t *configs)
{
*configs = (mspi_timing_config_t) {
.phase = {MSPI_LL_DQS_PHASE_67_5, MSPI_LL_DQS_PHASE_78_75, MSPI_LL_DQS_PHASE_90, MSPI_LL_DQS_PHASE_101_25},
.available_phase_num = 4,
};
}
void mspi_timing_config_psram_set_tuning_phase(const void *configs, uint8_t id)
{
mspi_ll_dqs_phase_t phase = ((mspi_timing_config_t *)configs)->phase[id];
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, phase);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, phase);
#endif
ESP_DRAM_LOGD(TAG, "set to phase: %d", phase);
}
uint32_t mspi_timing_psram_select_best_tuning_phase(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
assert(consecutive_length < 5);
uint32_t best_phase_id = 0;
bool success = true;
if (consecutive_length == 0) {
best_phase_id = 0;
success = false;
} else {
best_phase_id = (end - consecutive_length + 1);
}
if (success) {
ESP_DRAM_LOGD(TAG, "tuning success, best phase id is %"PRIu32, best_phase_id);
} else {
ESP_DRAM_LOGW(TAG, "tuning fail, best phase id is fallen back to index %"PRIu32"", best_phase_id);
}
return best_phase_id;
}
void mspi_timing_psram_set_best_tuning_phase(const void *configs, uint8_t best_id)
{
s_psram_best_phase = ((const mspi_timing_config_t *)configs)->phase[best_id];
}
void mspi_timing_get_psram_tuning_delaylines(mspi_timing_config_t *configs)
{
ESP_DRAM_LOGD(TAG, "sizeof(delayline_config_t): %d, sizeof(test_config): %d", sizeof(delayline_config_t), sizeof(s_test_delayline_config));
memcpy(configs, &s_test_delayline_config, sizeof(s_test_delayline_config));
}
void mspi_timing_config_psram_set_tuning_delayline(const void *configs, uint8_t id)
{
assert(s_psram_best_phase != MSPI_LL_DQS_PHASE_MAX);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, s_psram_best_phase);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, s_psram_best_phase);
#endif
ESP_DRAM_LOGD(TAG, "set to best phase: %d", s_psram_best_phase);
const delayline_config_t *delayline_config = &((mspi_timing_config_t *)configs)->delayline_table[id];
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
if (i == MSPI_LL_PIN_DQS0) {
mspi_timing_ll_set_delayline(i, delayline_config->dqs_delayline);
}
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
else if (i == MSPI_LL_PIN_DQS1) {
mspi_timing_ll_set_delayline(i, delayline_config->dqs_delayline);
}
#endif
else {
mspi_timing_ll_set_delayline(i, delayline_config->data_delayline);
}
}
}
uint32_t mspi_timing_psram_select_best_tuning_delayline(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
assert(consecutive_length <= 32);
uint32_t bset_delayline_id = 0;
if (consecutive_length <= 1) {
bset_delayline_id = 0;
ESP_DRAM_LOGW(TAG, "tuning fail, best delayline id is fallen back to index %"PRIu32"", bset_delayline_id);
} else {
bset_delayline_id = end - consecutive_length / 2;
ESP_DRAM_LOGD(TAG, "tuning success, best delayline id is %"PRIu32, bset_delayline_id);
}
return bset_delayline_id;
}
void mspi_timing_psram_set_best_tuning_delayline(const void *configs, uint8_t best_id)
{
s_psram_best_delayline = ((mspi_timing_config_t *)configs)->delayline_table[best_id];
}
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi)
{
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, 0);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, 0);
#endif
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
mspi_timing_ll_set_delayline(i, 0);
}
}
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi)
{
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, s_psram_best_phase);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, s_psram_best_phase);
#endif
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
if (i == MSPI_LL_PIN_DQS0) {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.dqs_delayline);
}
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
else if (i == MSPI_LL_PIN_DQS1) {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.dqs_delayline);
}
#endif
else {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.data_delayline);
}
}
}
@@ -0,0 +1,282 @@
/*
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by Flash delay
* This file will only be built when `SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY == 1`
*/
#include <stdint.h>
#include <string.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "esp_private/mspi_timing_by_flash_delay.h"
#include "mspi_timing_tuning_configs.h"
#include "esp_private/mspi_timing_config.h"
#include "hal/mspi_ll.h"
#include "rom/spi_flash.h"
#include "esp_private/spi_flash_os.h"
ESP_LOG_ATTR_TAG(TAG, "Flash Delay");
void mspi_timing_flash_init(uint32_t flash_freq_mhz)
{
mspi_timing_config_set_flash_clock(flash_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
//Power on HCLK
mspi_timinng_ll_enable_flash_timing_adjust_clk(MSPI_TIMING_LL_MSPI_ID_0);
ESP_DRAM_LOGD(TAG, "init rom dummy val: %d", g_rom_spiflash_dummy_len_plus[1]);
}
//-------------------------------------FLASH timing tuning register config-------------------------------------//
void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config)
{
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_FLASH_CORE_CLOCK_MHZ, 120, STR_MODE);
#else
assert(false && "should never reach here");
#endif
}
static void s_set_flash_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num)
{
mspi_timing_ll_set_flash_din_mode(spi_num, din_mode);
mspi_timing_ll_set_flash_din_num(spi_num, din_num);
}
static void s_set_flash_extra_dummy(uint8_t spi_num, uint8_t extra_dummy)
{
mspi_timing_ll_set_flash_extra_dummy(spi_num, extra_dummy);
}
void mspi_timing_config_flash_set_tuning_regs(const void *configs, uint8_t id)
{
const mspi_timing_tuning_param_t *params = &((mspi_timing_config_t *)configs)->tuning_config_table[id];
/**
* 1. SPI_MEM_DINx_MODE(1), SPI_MEM_DINx_NUM(1) are meaningless
* SPI0 and SPI1 share the SPI_MEM_DINx_MODE(0), SPI_MEM_DINx_NUM(0) for FLASH timing tuning
* 2. We use SPI1 to get the best Flash timing tuning (mode and num) config
*/
s_set_flash_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, params->spi_din_mode, params->spi_din_num);
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, params->extra_dummy_len);
}
//-------------------------------------------FLASH Read/Write------------------------------------------//
void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO && CONFIG_SPI_FLASH_HPM_ON
g_rom_spiflash_dummy_len_plus[1] = 4;
#endif
esp_rom_spiflash_read(addr, (uint32_t *)buf, len);
int spi1_usr_dummy = 0;
int spi1_extra_dummy = 0;
int spi0_usr_dummy = 0;
int spi0_extra_dummy = 0;
mspi_timing_ll_get_flash_dummy(MSPI_TIMING_LL_MSPI_ID_0, &spi0_usr_dummy, &spi0_extra_dummy);
mspi_timing_ll_get_flash_dummy(MSPI_TIMING_LL_MSPI_ID_1, &spi1_usr_dummy, &spi1_extra_dummy);
ESP_DRAM_LOGD(TAG, "spi0_usr_dummy: %d, spi0_extra_dummy: %d, spi1_usr_dummy: %d, spi1_extra_dummy: %d", spi0_usr_dummy, spi0_extra_dummy, spi1_usr_dummy, spi1_extra_dummy);
}
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Selection
*-------------------------------------------------------------------------------------------------*/
static uint32_t s_select_best_tuning_config_str(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end)
{
//STR best point scheme
uint32_t best_point;
if (consecutive_length < 3) {
//tuning fails, select default point, and generate a warning
best_point = configs->flash_default_config_id;
ESP_DRAM_LOGW(TAG, "tuning fail, best point is fallen back to index %"PRIu32"", best_point);
} else {
best_point = end - consecutive_length / 2;
ESP_DRAM_LOGD(TAG, "tuning success, best point is index %"PRIu32"", best_point);
}
return best_point;
}
static uint32_t s_select_best_tuning_config(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr, bool is_flash)
{
uint32_t best_point = 0;
assert(!is_ddr);
best_point = s_select_best_tuning_config_str(configs, consecutive_length, end);
return best_point;
}
uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs;
uint32_t best_point = s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, true);
ESP_DRAM_LOGD(TAG, "Flash timing tuning index: %"PRIu32"", best_point);
return best_point;
}
static mspi_timing_tuning_param_t s_flash_best_timing_tuning_config;
static mspi_timing_tuning_param_t s_psram_best_timing_tuning_config;
void mspi_timing_flash_set_best_tuning_config(const void *configs, uint8_t best_id)
{
s_flash_best_timing_tuning_config = ((const mspi_timing_config_t *)configs)->tuning_config_table[best_id];
}
void mspi_timing_psram_set_best_tuning_config(const void *configs, uint8_t best_id)
{
s_psram_best_timing_tuning_config = ((const mspi_timing_config_t *)configs)->tuning_config_table[best_id];
}
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Clear / Set
*-------------------------------------------------------------------------------------------------*/
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi)
{
s_set_flash_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, 0, 0); //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_0, 0);
//Won't touch SPI1 registers if not control_both_mspi
if (control_both_mspi) {
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, 0);
}
}
#if MSPI_TIMING_FLASH_NEEDS_TUNING && CONFIG_SPI_FLASH_HPM_ON
static uint32_t spi_timing_config_get_dummy(void)
{
mspi_timing_ll_flash_mode_t mode = mspi_timing_ll_get_flash_mode(0);
if (spi_flash_hpm_dummy_adjust()) { // HPM-DC is enabled
const spi_flash_hpm_dummy_conf_t *hpm_dummy = spi_flash_hpm_get_dummy();
switch (mode) {
case MSPI_TIMING_LL_FLASH_QIO_MODE:
return hpm_dummy->qio_dummy - 1;
case MSPI_TIMING_LL_FLASH_QUAD_MODE:
return hpm_dummy->qout_dummy - 1;
case MSPI_TIMING_LL_FLASH_DIO_MODE:
return hpm_dummy->dio_dummy - 1;
case MSPI_TIMING_LL_FLASH_DUAL_MODE:
return hpm_dummy->dout_dummy - 1;
case MSPI_TIMING_LL_FLASH_FAST_MODE:
return hpm_dummy->fastrd_dummy - 1;
case MSPI_TIMING_LL_FLASH_SLOW_MODE:
return 0;
default:
abort();
}
} else { // HPM-DC is not enabled
switch (mode) {
case MSPI_TIMING_LL_FLASH_QIO_MODE:
return SPI1_R_QIO_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_QUAD_MODE:
return SPI1_R_FAST_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_DIO_MODE:
return SPI1_R_DIO_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_DUAL_MODE:
return SPI1_R_FAST_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_FAST_MODE:
return SPI1_R_FAST_DUMMY_CYCLELEN;
case MSPI_TIMING_LL_FLASH_SLOW_MODE:
return 0;
default:
abort();
}
}
}
#endif // MSPI_TIMING_FLASH_NEEDS_TUNING && CONFIG_SPI_FLASH_HPM_ON
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi)
{
//SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg
s_set_flash_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, s_flash_best_timing_tuning_config.spi_din_mode, s_flash_best_timing_tuning_config.spi_din_num);
s_set_flash_extra_dummy(0, s_flash_best_timing_tuning_config.extra_dummy_len);
if (control_both_mspi) {
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, s_flash_best_timing_tuning_config.extra_dummy_len);
} else {
//Won't touch SPI1 registers
}
#if MSPI_TIMING_FLASH_NEEDS_TUNING && CONFIG_SPI_FLASH_HPM_ON
uint32_t dummy_len = spi_timing_config_get_dummy();
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO && !CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
// In the ROM configuration, when esp_rom_spi_set_rd_cmd_bit_len issues the read command in QIO mode, it automatically inserts two dummy cycles.
// Therefore, when validating the dummy cycles at 120 MHz, the effective value should be 10 − 2, i.e. 8 dummy cycles.
// However, when the 32 MHz cache access configuration is enabled, the corresponding register will be overwritten,
// so the extra two dummy cycles no longer exist. In that case, the dummy cycle setting should simply be 10.
mspi_timing_ll_set_flash_user_dummy(MSPI_TIMING_LL_MSPI_ID_0, dummy_len - 2);
#else
mspi_timing_ll_set_flash_user_dummy(MSPI_TIMING_LL_MSPI_ID_0, dummy_len);
#endif // CONFIG_ESPTOOLPY_FLASHMODE_QIO && !CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
#endif
int spi1_usr_dummy = 0;
int spi1_extra_dummy = 0;
int spi0_usr_dummy = 0;
int spi0_extra_dummy = 0;
mspi_timing_ll_get_flash_dummy(MSPI_TIMING_LL_MSPI_ID_0, &spi0_usr_dummy, &spi0_extra_dummy);
mspi_timing_ll_get_flash_dummy(MSPI_TIMING_LL_MSPI_ID_1, &spi1_usr_dummy, &spi1_extra_dummy);
ESP_DRAM_LOGD(TAG, "spi0_usr_dummy: %d, spi0_extra_dummy: %d, spi1_usr_dummy: %d, spi1_extra_dummy: %d", spi0_usr_dummy, spi0_extra_dummy, spi1_usr_dummy, spi1_extra_dummy);
}
/*-------------------------------------------------------------------------------------------------
* To let upper lay (spi_flash_timing_tuning.c) to know the necessary timing registers
*-------------------------------------------------------------------------------------------------*/
/**
* Get the SPI1 Flash CS timing setting. The setup time and hold time are both realistic cycles.
* @note On ESP32-P4, SPI0/1 share the Flash CS timing registers. Therefore, we should not change these values.
* @note This function inform `spi_flash_timing_tuning.c` (driver layer) of the cycle,
* and other component (esp_flash driver) should get these cycle and configure the registers accordingly.
*/
void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time)
{
*setup_time = mspi_timing_ll_get_cs_setup_val(MSPI_TIMING_LL_MSPI_ID_0);
*hold_time = mspi_timing_ll_get_cs_hold_val(MSPI_TIMING_LL_MSPI_ID_0);
/**
* The logic here is, if setup_en / hold_en is false, then we return the realistic cycle number,
* which is 0. If true, then the realistic cycle number is (reg_value + 1)
*/
if (mspi_timing_ll_is_cs_setup_enabled(MSPI_TIMING_LL_MSPI_ID_0)) {
*setup_time += 1;
} else {
*setup_time = 0;
}
if (mspi_timing_ll_is_cs_hold_enabled(MSPI_TIMING_LL_MSPI_ID_0)) {
*hold_time += 1;
} else {
*hold_time = 0;
}
}
/**
* Get the SPI1 Flash clock setting.
* @note Similarly, this function inform `spi_flash_timing_tuning.c` (driver layer) of the clock setting,
* and other component (esp_flash driver) should get these and configure the registers accordingly.
*/
uint32_t mspi_timing_config_get_flash_clock_reg(void)
{
return mspi_timing_ll_get_clock_reg(MSPI_TIMING_LL_MSPI_ID_1);
}
uint8_t mspi_timing_config_get_flash_extra_dummy(void)
{
//use hw extra dummy
return 0;
}
uint32_t mspi_timing_config_get_flash_fdummy_rin(void)
{
return mspi_timing_ll_get_invalid_dqs_mask(MSPI_TIMING_LL_MSPI_ID_1);
}
@@ -0,0 +1,434 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief
*
* This file contains configuration APIs doing MSPI timing tuning by MSPI delay
* This file will only be built, when `SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY == 1`
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "string.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "rom/spi_flash.h"
#include "hal/mspi_ll.h"
#include "hal/psram_types.h"
#include "hal/psram_ctrlr_ll.h"
#include "esp_private/mspi_timing_config.h"
#include "esp_private/mspi_timing_by_mspi_delay.h"
#include "esp_private/spi_flash_os.h"
#include "mspi_timing_tuning_configs.h"
#define QPI_PSRAM_FAST_READ 0XEB
#define QPI_PSRAM_WRITE 0X38
#define QPI_PSRAM_FAST_READ_DUMMY 6
ESP_LOG_ATTR_TAG(TAG, "MSPI Timing");
//-------------------------------------FLASH timing tuning register config-------------------------------------//
void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config)
{
#if MSPI_TIMING_FLASH_DTR_MODE
#define FLASH_MODE DTR_MODE
#else //MSPI_TIMING_FLASH_STR_MODE
#define FLASH_MODE STR_MODE
#endif
#if CONFIG_ESPTOOLPY_FLASHFREQ_80M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, FLASH_MODE);
#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M
*config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, FLASH_MODE);
#else
assert(false && "should never reach here");
#endif
#undef FLASH_MODE
}
void mspi_timing_flash_init(uint32_t flash_freq_mhz)
{
mspi_timing_config_set_flash_clock(flash_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
//Power on HCLK
mspi_timinng_ll_enable_flash_timing_adjust_clk(MSPI_TIMING_LL_MSPI_ID_0);
}
static void s_set_flash_din_mode_num(uint8_t mspi_id, uint8_t din_mode, uint8_t din_num)
{
mspi_timing_ll_set_flash_din_mode(mspi_id, din_mode);
mspi_timing_ll_set_flash_din_num(mspi_id, din_num);
}
static void s_set_flash_extra_dummy(uint8_t mspi_id, uint8_t extra_dummy)
{
mspi_timing_ll_set_flash_extra_dummy(mspi_id, extra_dummy);
}
void mspi_timing_config_flash_set_tuning_regs(const void *configs, uint8_t id)
{
const mspi_timing_tuning_param_t *params = &((mspi_timing_config_t *)configs)->tuning_config_table[id];
/**
* 1. SPI_MEM_DINx_MODE(1), SPI_MEM_DINx_NUM(1) are meaningless
* SPI0 and SPI1 share the SPI_MEM_DINx_MODE(0), SPI_MEM_DINx_NUM(0) for FLASH timing tuning
* 2. We use SPI1 to get the best Flash timing tuning (mode and num) config
*/
s_set_flash_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, params->spi_din_mode, params->spi_din_num);
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, params->extra_dummy_len);
}
//-------------------------------------------FLASH Read/Write------------------------------------------//
void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO && CONFIG_SPI_FLASH_HPM_ON
g_rom_spiflash_dummy_len_plus[1] = 4;
#endif
if (spi_flash_is_octal_mode_enabled()) {
// note that in spi_flash_read API, there is a wait-idle stage, since flash can only be read in idle state.
// but after we change the timing settings, we might not read correct idle status via RDSR.
// so, here we should use a read API that won't check idle status.
mspi_timing_ll_clear_fifo(MSPI_TIMING_LL_MSPI_ID_1);
// to add opi read api here when octal flash is used
assert(false);
} else {
esp_rom_spiflash_read(addr, (uint32_t *)buf, len);
}
}
//-------------------------------------PSRAM timing tuning register config-------------------------------------//
void mspi_timing_psram_init(uint32_t psram_freq_mhz)
{
mspi_timing_config_set_flash_clock(psram_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true);
//Power on HCLK
mspi_timinng_ll_enable_psram_timing_adjust_clk(MSPI_TIMING_LL_MSPI_ID_0);
}
void mspi_timing_get_psram_tuning_configs(mspi_timing_config_t *config)
{
#if MSPI_TIMING_PSRAM_DTR_MODE
#define PSRAM_MODE DTR_MODE
#else //MSPI_TIMING_PSRAM_STR_MODE
#define PSRAM_MODE STR_MODE
#endif
#if CONFIG_SPIRAM_SPEED_80M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, STR_MODE);
#elif CONFIG_SPIRAM_SPEED_120M
*config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, PSRAM_MODE);
#else
assert(false && "should never reach here");
#endif
#undef PSRAM_MODE
}
static void s_set_psram_din_mode_num(uint8_t mspi_id, uint8_t din_mode, uint8_t din_num)
{
mspi_timing_ll_set_psram_din_mode(mspi_id, din_mode);
mspi_timing_ll_set_psram_din_num(mspi_id, din_num);
}
static void s_set_psram_extra_dummy(uint8_t mspi_id, uint8_t extra_dummy)
{
mspi_timing_ll_set_psram_extra_dummy(mspi_id, extra_dummy);
}
void mspi_timing_config_psram_set_tuning_regs(const void *configs, uint8_t id)
{
const mspi_timing_tuning_param_t *params = &((mspi_timing_config_t *)configs)->tuning_config_table[id];
/**
* 1. SPI_MEM_SPI_SMEM_DINx_MODE(1), SPI_MEM_SPI_SMEM_DINx_NUM(1) are meaningless
* SPI0 and SPI1 share the SPI_MEM_SPI_SMEM_DINx_MODE(0), SPI_MEM_SPI_SMEM_DINx_NUM(0) for PSRAM timing tuning
* 2. We use SPI1 to get the best PSRAM timing tuning (mode and num) config
*/
s_set_psram_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, params->spi_din_mode, params->spi_din_num);
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, params->extra_dummy_len);
}
//-------------------------------------------PSRAM Read/Write------------------------------------------//
static void psram_exec_cmd(int mspi_id, psram_cmd_mode_t mode,
uint32_t cmd, int cmd_bit_len,
uint32_t addr, int addr_bit_len,
int dummy_bits,
uint8_t* mosi_data, int mosi_bit_len,
uint8_t* miso_data, int miso_bit_len,
uint32_t cs_mask,
bool is_write_erase_operation)
{
esp_rom_spiflash_read_mode_t rd_mode = (mode == PSRAM_HAL_CMD_QPI) ? ESP_ROM_SPIFLASH_QIO_MODE : ESP_ROM_SPIFLASH_SLOWRD_MODE;
esp_rom_spi_set_op_mode(mspi_id, rd_mode);
if (mode == PSRAM_HAL_CMD_QPI) {
psram_ctrlr_ll_enable_quad_command(PSRAM_CTRLR_LL_MSPI_ID_1, true);
}
psram_ctrlr_ll_common_transaction_base(mspi_id, rd_mode,
cmd, cmd_bit_len,
addr, addr_bit_len,
dummy_bits,
mosi_data, mosi_bit_len,
miso_data, miso_bit_len,
cs_mask,
is_write_erase_operation);
}
#define FIFO_SIZE_BYTE 32
static void s_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
if (len % FIFO_SIZE_BYTE != 0) {
ESP_DRAM_LOGE(TAG, "wrong length %d", len);
assert(false);
}
uint8_t mspi_id = MSPI_TIMING_LL_MSPI_ID_1;
uint8_t cmd = QPI_PSRAM_WRITE;
uint8_t cmd_len = 8;
uint8_t dummy_len = 0;
uint8_t addr_len = 24;
psram_cmd_mode_t spi_mode = PSRAM_HAL_CMD_QPI;
for (uint32_t idx = 0; idx < len / FIFO_SIZE_BYTE; idx++) {
psram_exec_cmd(mspi_id, spi_mode,
cmd, cmd_len,
addr + idx * FIFO_SIZE_BYTE, addr_len,
dummy_len,
buf + idx * FIFO_SIZE_BYTE, FIFO_SIZE_BYTE * 8,
NULL, 0,
PSRAM_LL_CS_SEL,
false);
}
}
static void s_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
if (len % FIFO_SIZE_BYTE != 0) {
ESP_DRAM_LOGE(TAG, "wrong length %d", len);
assert(false);
}
uint8_t mspi_id = MSPI_TIMING_LL_MSPI_ID_1;
uint8_t cmd = QPI_PSRAM_FAST_READ;
uint8_t cmd_len = 8;
uint8_t dummy_len = QPI_PSRAM_FAST_READ_DUMMY;
uint8_t addr_len = 24;
psram_cmd_mode_t spi_mode = PSRAM_HAL_CMD_QPI;
for (uint32_t idx = 0; idx < len / FIFO_SIZE_BYTE; idx++) {
psram_exec_cmd(mspi_id, spi_mode,
cmd, cmd_len,
addr + idx * FIFO_SIZE_BYTE, addr_len,
dummy_len,
NULL, 0,
buf + idx * FIFO_SIZE_BYTE, FIFO_SIZE_BYTE * 8,
PSRAM_LL_CS_SEL,
false);
}
}
static void s_psram_execution(uint8_t *buf, uint32_t addr, uint32_t len, bool is_read)
{
while (len) {
uint32_t length = MIN(len, 32);
if (is_read) {
s_psram_read_data(buf, addr, length);
} else {
s_psram_write_data(buf, addr, length);
}
addr += length;
buf += length;
len -= length;
}
}
void mspi_timing_config_psram_prepare_reference_data(uint8_t *buf, uint32_t len)
{
assert((len == MSPI_TIMING_TEST_DATA_LEN) && (len % 4 == 0));
for (int i = 0; i < len / 4; i++) {
((uint32_t *)buf)[i] = 0xa5ff005a;
}
}
void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
s_psram_execution(buf, addr, len, false);
}
void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len)
{
s_psram_execution(buf, addr, len, true);
}
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Selection
*-------------------------------------------------------------------------------------------------*/
static uint32_t s_select_best_tuning_config_str(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end)
{
//STR best point scheme
uint32_t best_point = 0;
if (consecutive_length < 3) {
//tuning is FAIL, select default point, and generate a warning
best_point = configs->default_config_id;
ESP_DRAM_LOGW(TAG, "tuning fail, best point is fallen back to index %"PRIu32"", best_point);
}
#if MSPI_TIMING_FLASH_CONSECUTIVE_LEN_MAX
else if (consecutive_length > MSPI_TIMING_FLASH_CONSECUTIVE_LEN_MAX) {
best_point = configs->default_config_id;
ESP_DRAM_LOGW(TAG, "tuning fail, best point is fallen back to index %"PRIu32"", best_point);
}
#endif
else {
best_point = end - consecutive_length / 2;
ESP_DRAM_LOGD(TAG, "tuning success, best point is index %"PRIu32"", best_point);
}
return best_point;
}
static uint32_t s_select_best_tuning_config(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr, bool is_flash)
{
uint32_t best_point = 0;
assert(!is_ddr);
best_point = s_select_best_tuning_config_str(configs, consecutive_length, end);
return best_point;
}
uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs;
uint32_t best_point = s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, true);
ESP_DRAM_LOGD(TAG, "Flash timing tuning index: %"PRIu32"", best_point);
return best_point;
}
uint32_t mspi_timing_psram_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr)
{
const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs;
uint32_t best_point = s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, false);
ESP_DRAM_LOGD(TAG, "PSRAM timing tuning index: %"PRIu32"", best_point);
return best_point;
}
static mspi_timing_tuning_param_t s_flash_best_timing_tuning_config;
static mspi_timing_tuning_param_t s_psram_best_timing_tuning_config;
void mspi_timing_flash_set_best_tuning_config(const void *configs, uint8_t best_id)
{
s_flash_best_timing_tuning_config = ((const mspi_timing_config_t *)configs)->tuning_config_table[best_id];
}
void mspi_timing_psram_set_best_tuning_config(const void *configs, uint8_t best_id)
{
s_psram_best_timing_tuning_config = ((const mspi_timing_config_t *)configs)->tuning_config_table[best_id];
}
/*-------------------------------------------------------------------------------------------------
* Best Timing Tuning Params Clear / Set
*-------------------------------------------------------------------------------------------------*/
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi)
{
s_set_flash_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, 0, 0); //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_0, 0);
//Won't touch SPI1 registers if not control_both_mspi
if (control_both_mspi) {
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, 0);
}
}
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi)
{
//SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg
s_set_flash_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, s_flash_best_timing_tuning_config.spi_din_mode, s_flash_best_timing_tuning_config.spi_din_num);
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_0, s_flash_best_timing_tuning_config.extra_dummy_len);
if (control_both_mspi) {
s_set_flash_extra_dummy(MSPI_TIMING_LL_MSPI_ID_1, s_flash_best_timing_tuning_config.extra_dummy_len);
} else {
//Won't touch SPI1 registers
}
#if MSPI_TIMING_FLASH_NEEDS_TUNING && CONFIG_ESPTOOLPY_FLASHMODE_QIO && CONFIG_SPI_FLASH_HPM_ON
mspi_timing_ll_set_flash_user_dummy(MSPI_TIMING_LL_MSPI_ID_0, 7);
#endif
int spi1_usr_dummy = 0;
int spi1_extra_dummy = 0;
int spi0_usr_dummy = 0;
int spi0_extra_dummy = 0;
mspi_timing_ll_get_flash_dummy(MSPI_TIMING_LL_MSPI_ID_0, &spi0_usr_dummy, &spi0_extra_dummy);
mspi_timing_ll_get_flash_dummy(MSPI_TIMING_LL_MSPI_ID_1, &spi1_usr_dummy, &spi1_extra_dummy);
ESP_DRAM_LOGV(TAG, "flash, spi0_usr_dummy: %d, spi0_extra_dummy: %d, spi1_usr_dummy: %d, spi1_extra_dummy: %d", spi0_usr_dummy, spi0_extra_dummy, spi1_usr_dummy, spi1_extra_dummy);
}
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi)
{
(void)control_both_mspi; //for compatibility
s_set_psram_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, 0, 0);
s_set_psram_extra_dummy(MSPI_TIMING_LL_MSPI_ID_0, 0);
}
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi)
{
(void)control_both_mspi; //for compatibility
s_set_psram_din_mode_num(MSPI_TIMING_LL_MSPI_ID_0, s_psram_best_timing_tuning_config.spi_din_mode, s_psram_best_timing_tuning_config.spi_din_num);
s_set_psram_extra_dummy(MSPI_TIMING_LL_MSPI_ID_0, s_psram_best_timing_tuning_config.extra_dummy_len);
int spi0_usr_rdummy = 0;
int spi0_extra_dummy = 0;
mspi_timing_ll_get_psram_dummy(MSPI_TIMING_LL_MSPI_ID_0, &spi0_usr_rdummy, &spi0_extra_dummy);
ESP_DRAM_LOGV(TAG, "psram, spi0_usr_rdummy: %d, spi0_extra_dummy: %d", spi0_usr_rdummy, spi0_extra_dummy);
}
/*-------------------------------------------------------------------------------------------------
* To let upper layer (spi_flash_timing_tuning.c) to know the necessary timing registers
*-------------------------------------------------------------------------------------------------*/
/**
* Get the SPI1 Flash CS timing setting. The setup time and hold time are both realistic cycles.
* @note On ESP32-S3, SPI0/1 share the Flash CS timing registers. Therefore, we should not change these values.
* @note This function inform `spi_flash_timing_tuning.c` (driver layer) of the cycle,
* and other component (esp_flash driver) should get these cycle and configure the registers accordingly.
*/
void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time)
{
*setup_time = mspi_timing_ll_get_cs_setup_val(MSPI_TIMING_LL_MSPI_ID_0);
*hold_time = mspi_timing_ll_get_cs_hold_val(0);
/**
* The logic here is, if setup_en / hold_en is false, then we return the realistic cycle number,
* which is 0. If true, then the realistic cycle number is (reg_value + 1)
*/
if (mspi_timing_ll_is_cs_setup_enabled(MSPI_TIMING_LL_MSPI_ID_0)) {
*setup_time += 1;
} else {
*setup_time = 0;
}
if (mspi_timing_ll_is_cs_hold_enabled(MSPI_TIMING_LL_MSPI_ID_0)) {
*hold_time += 1;
} else {
*hold_time = 0;
}
}
/**
* Get the SPI1 Flash clock setting.
* @note Similarly, this function inform `spi_flash_timing_tuning.c` (driver layer) of the clock setting,
* and other component (esp_flash driver) should get these and configure the registers accordingly.
*/
uint32_t mspi_timing_config_get_flash_clock_reg(void)
{
return mspi_timing_ll_get_clock_reg(MSPI_TIMING_LL_MSPI_ID_1);
}
uint8_t mspi_timing_config_get_flash_extra_dummy(void)
{
//use hw extra dummy
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS CONFIG_SPI_FLASH_DANGEROUS_WRITE
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
+113
View File
@@ -0,0 +1,113 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_rom_sys.h"
#include "hal/spi_flash_hal.h"
#include "esp_private/spi_flash_os.h"
/*******************************************************************************
* Flash deep power-down mode.
* DPD: Deep power-down mode.
* TDP: CS high to deep power-down mode duration.
* TRES1: CS high to standby mode without ID read duration.
*
* Different flash chips might have different deep power-down strategy.
* 1. Most flash chips send B9H to enter DPD and send ABH to exist DPD.
* 2. Some flash chips send ABH followed by 3-dummy bytes to get device ID.
* 3. Some flash chips send B9H to enter PD(power-down) → send 79H to enter UDPD(ultra-deep power-down mode); send FFH to exit UDPD → send ABH to exit PD (ABH).
* 4. Some flash chips do nothing.
******************************************************************************/
__attribute__((unused)) const static char *DPD_TAG = "flash DPD";
#ifdef CONFIG_ESP_SLEEP_SPI_FLASH_ENTER_DPD_MODE_DELAY
#define SPI_FLASH_TDP_SAFE_VAL_US CONFIG_ESP_SLEEP_SPI_FLASH_ENTER_DPD_MODE_DELAY
#else
#define SPI_FLASH_TDP_SAFE_VAL_US (25)
#endif
#ifdef CONFIG_ESP_SLEEP_SPI_FLASH_EXIT_DPD_MODE_DELAY
#define SPI_FLASH_TRES1_SAFE_VAL_US CONFIG_ESP_SLEEP_SPI_FLASH_EXIT_DPD_MODE_DELAY
#else
#define SPI_FLASH_TRES1_SAFE_VAL_US (40)
#endif
/*
* Note: This file should only be compiled when DPD_ON, which is only available when (!APP_BUILD_TYPE_PURE_RAM_APP && !ESP_SLEEP_POWER_DOWN_FLASH).
* However when DPD_ON, there are still some cases this file is not actually used:
* TODO: PM-623
*/
static spi_flash_dpd_funcs_t s_dpd_funcs = {
.enter_dpd = NULL,
.exit_dpd = NULL,
};
esp_err_t spi_flash_dpd_register_funcs(spi_flash_dpd_funcs_t *dpd_funcs)
{
if (dpd_funcs == NULL) {
return ESP_ERR_INVALID_ARG;
}
s_dpd_funcs = *dpd_funcs;
return ESP_OK;
}
uint32_t spi_flash_dpd_get_enter_duration(void)
{
#ifndef CONFIG_ESP_SLEEP_SPI_FLASH_ENTER_DPD_MODE_DELAY
ESP_EARLY_LOGW(DPD_TAG, "No DPD enter delay value defined. Using default safe delay value. Verify with your flash datasheet.");
#endif
return SPI_FLASH_TDP_SAFE_VAL_US;
}
uint32_t spi_flash_dpd_get_exit_duration(void)
{
#ifndef CONFIG_ESP_SLEEP_SPI_FLASH_EXIT_DPD_MODE_DELAY
ESP_EARLY_LOGW(DPD_TAG, "No DPD exit delay value defined. Using default safe delay value. Verify with your flash datasheet.");
#endif
return SPI_FLASH_TRES1_SAFE_VAL_US;
}
static esp_err_t spi_flash_enter_dpd(bool wait_delay)
{
if (s_dpd_funcs.enter_dpd == NULL) {
ESP_EARLY_LOGE(DPD_TAG, "DPD enter function not registered");
return ESP_ERR_INVALID_STATE;
}
esp_err_t ret = s_dpd_funcs.enter_dpd();
if (wait_delay) {
esp_rom_delay_us(spi_flash_dpd_get_enter_duration());
}
return ret;
}
static esp_err_t spi_flash_exit_dpd(bool wait_delay)
{
if (s_dpd_funcs.exit_dpd == NULL) {
ESP_EARLY_LOGE(DPD_TAG, "DPD exit function not registered");
return ESP_ERR_INVALID_STATE;
}
esp_err_t ret = s_dpd_funcs.exit_dpd();
if (wait_delay) {
esp_rom_delay_us(spi_flash_dpd_get_exit_duration());
}
return ret;
}
esp_err_t spi_flash_enable_deep_power_down_mode(bool enable, bool wait_delay)
{
if (enable) {
return spi_flash_enter_dpd(wait_delay);
}
return spi_flash_exit_dpd(wait_delay);
}
+516
View File
@@ -0,0 +1,516 @@
/*
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "esp_rom_spiflash.h"
#include "spi_flash_defs.h"
#include "esp_flash_chips/spi_flash_override.h"
// TODO: These dependencies will be removed after remove bootloader_flash to G0.IDF-4609
#include "bootloader_flash_override.h"
#include "esp_private/bootloader_flash_internal.h"
#define CMD_HPMEN 0xA3 /* Enable High Performance mode on flash */
/*******************************************************************************
* Flash high speed performance mode.
* HPM: High performance mode.
* HPF: High performance flag.
*
* Different flash chips might have different high performance strategy.
* 1. Some flash chips send A3H to enable the HPM.
* 2. Some flash chips write HPF bit in status register.
* 3. Some flash chips adjust dummy cycles.
* 4. Some flash chips do nothing.
******************************************************************************/
/*
* Note: This file should only be compiled when HPM_ON, which is only available when !CONFIG_ESPTOOLPY_OCT_FLASH.
* However when HPM_ON, there are still some cases this file is not actually used:
*
* - !CONFIG_SPI_FLASH_UNDER_HIGH_FREQ:
* It mean that the flash not running under frequency requires HPM. spi_flash_enable_high_performance_mode() still
* called because caller shouldn't take care of the frequency.
*
* - spi_flash_is_octal_mode_enabled() == true:
* This is possible when `CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT` selected
*
* Octal Flash for now all support 120M. No need to enable HPM. The file is compiled, but will not actually run
* into spi_flash_enable_high_performance_mode().
*/
void spi_flash_hpm_get_dummy_generic(spi_flash_hpm_dummy_conf_t *dummy_conf);
#if CONFIG_SPI_FLASH_UNDER_HIGH_FREQ
#if CONFIG_SPI_FLASH_HPM_AUTO
// This only happens on S3, where HPM_AUTO leads to HPM_ON
#warning High Performance Mode (QSPI Flash > 80MHz) is optional feature that depends on flash model. Read Docs First!
#endif
const static char *HPM_TAG = "flash HPM";
// TODO: This function will be changed after remove bootloader_flash to G0.IDF-4609
extern uint32_t bootloader_flash_execute_command_common(
uint8_t command,
uint32_t addr_len, uint32_t address,
uint8_t dummy_len,
uint8_t mosi_len, uint32_t mosi_data,
uint8_t miso_len);
extern uint32_t IRAM_ATTR bootloader_flash_read_sfdp(uint32_t sfdp_addr, unsigned int miso_byte_num);
//-----------------For flash chips which enter HPM via command-----------------------//
/**
* @brief Probe the chip whether use command to enable HPM mode. Take GD as an example:
* Some GD send 0xA3 command to enable HPM mode of the flash.
*/
static esp_err_t spi_flash_hpm_probe_chip_with_cmd(uint32_t flash_id)
{
esp_err_t ret = ESP_OK;
uint32_t gd_sfdp;
switch (flash_id) {
/* The flash listed here should enter the HPM with command 0xA3 */
case 0xC84016:
case 0xC84017:
// Read BYTE4 in SFDP, 0 means C series, 6 means E series
gd_sfdp = bootloader_flash_read_sfdp(0x4, 1);
if (gd_sfdp == 0x0) {
break;
} else {
ret = ESP_ERR_NOT_FOUND;
break;
}
default:
ret = ESP_ERR_NOT_FOUND;
break;
}
return ret;
}
static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_cmd(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre)
{
// voltage and temperature are not been used now, to be completed in the future.
(void)voltage_mv;
(void)temperautre;
spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED;
if (freq_mhz > 80) {
chip_cap = SPI_FLASH_HPM_CMD_NEEDED;
}
ESP_EARLY_LOGD(HPM_TAG, "HPM with command, status is %d", chip_cap);
return chip_cap;
}
/**
* @brief Send HPMEN command (A3H)
*/
static void spi_flash_enable_high_performance_send_cmd(void)
{
uint32_t dummy = 24;
bootloader_flash_execute_command_common(CMD_HPMEN, 0, 0, dummy, 0, 0, 0);
// Delay for T(HPM) referring to datasheet.
esp_rom_delay_us(20);
}
/**
* @brief Check whether flash HPM has been enabled. According to flash datasheets, majorities of
* HPF bit are at bit-5, sr-3. But some are not. Therefore, this function is only used for those
* HPF bit is at bit-5, sr-3.
*/
static esp_err_t spi_flash_high_performance_check_hpf_bit_5(void)
{
if ((bootloader_read_status_8b_rdsr3() & (1 << 4)) == 0) {
return ESP_FAIL;
}
return ESP_OK;
}
//-----------------For flash chips which enter HPM via adjust dummy-----------------------//
#if CONFIG_SPI_FLASH_HPM_DC_ON
/**
* @brief Probe the chip whether adjust dummy to enable HPM mode. Take XMC as an example:
* Adjust dummy bits to enable HPM mode of the flash. If XMC works under 80MHz, the dummy bits
* might be 6, but when works under 120MHz, the dummy bits might be 10.
*/
static esp_err_t spi_flash_hpm_probe_chip_with_dummy(uint32_t flash_id)
{
esp_err_t ret = ESP_OK;
uint32_t gd_sfdp;
switch (flash_id) {
/* The flash listed here should enter the HPM by adjusting dummy cycles */
// XMC chips.
case 0x204017:
case 0x204018:
case 0x464016:
case 0x464017:
case 0x464018:
break;
// GD chips.
case 0xC84017:
case 0xC84018:
// Read BYTE4 in SFDP, 0 means C series, 6 means E series
gd_sfdp = bootloader_flash_read_sfdp(0x4, 1);
if (gd_sfdp == 0x6) {
break;
} else {
ret = ESP_ERR_NOT_FOUND;
break;
}
default:
ret = ESP_ERR_NOT_FOUND;
break;
}
return ret;
}
static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_dummy(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre)
{
// voltage and temperature are not been used now, to be completed in the future.
(void)voltage_mv;
(void)temperautre;
spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED;
if (freq_mhz >= 104) {
chip_cap = SPI_FLASH_HPM_DUMMY_NEEDED;
}
ESP_EARLY_LOGD(HPM_TAG, "HPM with dummy, status is %d", chip_cap);
return chip_cap;
}
/**
* @brief Adjust dummy cycles. This function modifies the Dummy Cycle Bits in SR3.
* Usually, the bits are at bit-0, bit-1, sr-3 and set DC[1:0]=[1,1].
*
* @note Don't forget to adjust dummy configurations for MSPI, you can get the
* correct dummy from interface `spi_flash_hpm_get_dummy`.
*/
static void spi_flash_turn_high_performance_reconfig_dummy(void)
{
uint8_t old_status_3 = bootloader_read_status_8b_rdsr3();
uint8_t new_status = (old_status_3 | 0x03);
bootloader_execute_flash_command(CMD_WRENVSR, 0, 0, 0);
bootloader_write_status_8b_wrsr3(new_status);
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
}
/**
* @brief Check whether HPM has been enabled. This function checks the DC bits
*/
static esp_err_t spi_flash_high_performance_check_dummy_sr(void)
{
if ((bootloader_read_status_8b_rdsr3() & 0x03) == 0) {
return ESP_FAIL;
}
return ESP_OK;
}
static void spi_flash_hpm_get_dummy_xmc(spi_flash_hpm_dummy_conf_t *dummy_conf)
{
dummy_conf->dio_dummy = SPI_FLASH_DIO_HPM_DUMMY_BITLEN;
dummy_conf->dout_dummy = SPI_FLASH_DOUT_DUMMY_BITLEN;
dummy_conf->qio_dummy = SPI_FLASH_QIO_HPM_DUMMY_BITLEN;
dummy_conf->qout_dummy = SPI_FLASH_QOUT_DUMMY_BITLEN;
dummy_conf->fastrd_dummy = SPI_FLASH_FASTRD_DUMMY_BITLEN;
}
/**
* @brief Probe the chip whether adjust dummy (bit3,4) to enable HPM mode. Take XMC as an example:
* Adjust dummy bits to enable HPM mode of the flash. If XMC works under 80MHz, the dummy bits
* might be 6, but when works under 120MHz, the dummy bits might be 10.
*/
static esp_err_t spi_flash_hpm_probe_chip_with_dummy_bit3_4(uint32_t flash_id)
{
esp_err_t ret = ESP_OK;
switch (flash_id) {
/* The flash listed here should enter the HPM by adjusting dummy cycles */
// XMC chips.
case 0x204019:
case 0x204020:
break;
default:
ret = ESP_ERR_NOT_FOUND;
break;
}
return ret;
}
static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_dummy_bit3_4(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre)
{
// voltage and temperature are not been used now, to be completed in the future.
(void)voltage_mv;
(void)temperautre;
spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED;
if (freq_mhz >= 104) {
chip_cap = SPI_FLASH_HPM_DUMMY_NEEDED;
}
ESP_EARLY_LOGD(HPM_TAG, "HPM with dummy bit3,4, status is %d", chip_cap);
return chip_cap;
}
/**
* @brief Adjust dummy cycles. This function modifies the Dummy Cycle Bits in SR3.
* Usually, the bits are at bit-0, bit-1, sr-3 and set DC[1:0]=[1,1].
*
* @note Don't forget to adjust dummy configurations for MSPI, you can get the
* correct dummy from interface `spi_flash_hpm_get_dummy`.
*/
static void spi_flash_turn_high_performance_dummy_bit3_4(void)
{
uint8_t old_status_3 = bootloader_read_status_8b_rdsr3();
uint8_t new_status = (old_status_3 | 0x18);
bootloader_execute_flash_command(CMD_WRENVSR, 0, 0, 0);
bootloader_write_status_8b_wrsr3(new_status);
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
}
/**
* @brief Check whether HPM has been enabled. This function checks the DC bits
*/
static esp_err_t spi_flash_high_performance_check_dummy_bit3_4(void)
{
if ((bootloader_read_status_8b_rdsr3() & 0x18) == 0) {
return ESP_FAIL;
}
return ESP_OK;
}
#elif !CONFIG_SPI_FLASH_HPM_DC_DISABLE
//This is because bootloader doesn't support this
#warning HPM-DC, which helps to run some flash > 80MHz by adjusting dummy cycles, is no longer enabled by default.
#warning To enable this feature, your bootloader needs to have the support for it (by explicitly selecting BOOTLOADER_FLASH_DC_AWARE).
#warning If your bootloader does not support it, select SPI_FLASH_HPM_DC_DISABLE to suppress the warning. READ DOCS FIRST!
#endif //CONFIG_SPI_FLASH_HPM_DC_ON
//-----------------For flash chips which enter HPM via write status register-----------------------//
/**
* @brief Probe the chip whether to write status register to enable HPM mode. Take ZB as an example:
* Write status register bits to enable HPM mode of the flash. If ZB works under 80MHz, the register value
* would be 0, but when works under 120MHz, the register value would be 1.
*/
static esp_err_t spi_flash_hpm_probe_chip_with_write_hpf_bit_5(uint32_t flash_id)
{
esp_err_t ret = ESP_OK;
switch (flash_id) {
/* The flash listed here should enter the HPM by adjusting dummy cycles */
// ZB chips.
case 0x5E4016:
break;
default:
ret = ESP_ERR_NOT_FOUND;
break;
}
return ret;
}
static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_write_hpf_bit_5(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre)
{
// voltage and temperature are not been used now, to be completed in the future.
(void)voltage_mv;
(void)temperautre;
spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED;
if (freq_mhz >= 104) {
chip_cap = SPI_FLASH_HPM_WRITE_SR_NEEDED;
}
ESP_EARLY_LOGD(HPM_TAG, "HPM with dummy, status is %d", chip_cap);
return chip_cap;
}
/**
* @brief Write bit 5 in status 3
*/
static void spi_flash_turn_high_performance_write_hpf_bit_5(void)
{
uint8_t old_status_3 = bootloader_read_status_8b_rdsr3();
uint8_t new_status = (old_status_3 | 0x10);
bootloader_execute_flash_command(CMD_WRENVSR, 0, 0, 0);
bootloader_write_status_8b_wrsr3(new_status);
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
}
//-----------------For flash chips which enter HPM with doing nothing-----------------------//
/**
* @brief Probe the chip whether to write status register to enable HPM mode. Take a GD chip as an example:
* This chip (GD25LQ255E) supports maximum frequency to 133MHz by default. So, we don't need to do any extra
* thing.
*/
static esp_err_t spi_flash_hpm_probe_chip_with_doing_nothing(uint32_t flash_id)
{
esp_err_t ret = ESP_OK;
switch (flash_id) {
/* The flash listed here should enter the HPM by doing nothing */
// GD25LQ255E.
case 0xC86019:
break;
case 0xEF4017:
break;
default:
ret = ESP_ERR_NOT_FOUND;
break;
}
return ret;
}
static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_doing_nothing(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre)
{
// voltage and temperature are not been used now, to be completed in the future.
(void)voltage_mv;
(void)temperautre;
spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED;
ESP_EARLY_LOGD(HPM_TAG, "HPM by default, chip caps is %d", chip_cap);
return chip_cap;
}
const spi_flash_hpm_info_t __attribute__((weak)) spi_flash_hpm_enable_list[] = {
/* vendor, chip_id, freq_threshold, temperature threshold, operation for setting high performance, reading HPF status, get dummy */
{ "command", spi_flash_hpm_probe_chip_with_cmd, spi_flash_hpm_chip_hpm_requirement_check_with_cmd, spi_flash_enable_high_performance_send_cmd, spi_flash_high_performance_check_hpf_bit_5, spi_flash_hpm_get_dummy_generic },
#if CONFIG_SPI_FLASH_HPM_DC_ON
{ "dummy sr3-bit01", spi_flash_hpm_probe_chip_with_dummy, spi_flash_hpm_chip_hpm_requirement_check_with_dummy, spi_flash_turn_high_performance_reconfig_dummy, spi_flash_high_performance_check_dummy_sr, spi_flash_hpm_get_dummy_xmc},
{ "dummy sr3-bit3_4", spi_flash_hpm_probe_chip_with_dummy_bit3_4, spi_flash_hpm_chip_hpm_requirement_check_with_dummy_bit3_4, spi_flash_turn_high_performance_dummy_bit3_4, spi_flash_high_performance_check_dummy_bit3_4, spi_flash_hpm_get_dummy_xmc},
#endif //CONFIG_SPI_FLASH_HPM_DC_ON
{ "write sr3-bit5", spi_flash_hpm_probe_chip_with_write_hpf_bit_5, spi_flash_hpm_chip_hpm_requirement_check_with_write_hpf_bit_5, spi_flash_turn_high_performance_write_hpf_bit_5, spi_flash_high_performance_check_hpf_bit_5, spi_flash_hpm_get_dummy_generic},
{ "noting-to-do", spi_flash_hpm_probe_chip_with_doing_nothing, spi_flash_hpm_chip_hpm_requirement_check_with_doing_nothing, NULL, NULL, spi_flash_hpm_get_dummy_generic},
// default: do nothing, but keep the dummy get function. The first item with NULL as its probe will be the fallback.
{ "NULL", NULL, NULL, NULL, NULL, spi_flash_hpm_get_dummy_generic},
};
static const spi_flash_hpm_info_t *chip_hpm = NULL;
#if CONFIG_SPI_FLASH_HPM_DC_ON
static bool s_hpm_dummy_changed = false;
static spi_flash_hpm_dummy_conf_t s_dummy_conf;
const spi_flash_hpm_dummy_conf_t *spi_flash_hpm_get_dummy(void)
{
chip_hpm->flash_get_dummy(&s_dummy_conf);
return &s_dummy_conf;
}
bool spi_flash_hpm_dummy_adjust(void)
{
return s_hpm_dummy_changed;
}
#endif //CONFIG_SPI_FLASH_HPM_DC_ON
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define FLASH_FREQUENCY 120
#endif
esp_err_t spi_flash_enable_high_performance_mode(void)
{
uint32_t flash_chip_id = g_rom_flashchip.device_id;
uint32_t flash_freq = FLASH_FREQUENCY;
spi_flash_requirement_t hpm_requirement_check;
// voltage and temperature has not been implemented, just leave an interface here. Complete in the future.
int voltage = 0;
int temperature = 0;
#if CONFIG_SPI_FLASH_HPM_AUTO
ESP_EARLY_LOGW(HPM_TAG, "HPM mode is optional feature that depends on flash model. Read Docs First!");
#endif
#if CONFIG_SPI_FLASH_HPM_DC_DISABLE
// case 1: force disabled
ESP_EARLY_LOGI(HPM_TAG, "w/o HPM-DC support");
#elif CONFIG_SPI_FLASH_HPM_DC_ON
// case 2: auto, and actually enabled
ESP_EARLY_LOGI(HPM_TAG, "with HPM-DC support");
#else
// case 3: auto, but disabled (not supported by bootloader)
ESP_EARLY_LOGW(HPM_TAG, "HPM mode with DC adjustment is disabled. Some flash models may not be supported. Read Docs First!");
#endif
const spi_flash_hpm_info_t *chip = spi_flash_hpm_enable_list;
esp_err_t ret = ESP_OK;
while (chip->probe) {
ret = chip->probe(flash_chip_id);
if (ret == ESP_OK) {
break;
}
chip++;
}
chip_hpm = chip;
/* When > 80 MHz, flash chips usually need special HPM support to run normally. The support is chip-specific. When
* the chip is not in the known flash list, nothing will be done and there will be an warning.
* When <= 80 MHz, it's assumed that all flash chips can run without chip-specific HPM support. This function will not be called and there will be no warning.
*/
if (ret != ESP_OK) {
ESP_EARLY_LOGW(HPM_TAG, "High performance mode of this flash model hasn't been supported.");
return ret;
}
hpm_requirement_check = chip_hpm->chip_hpm_requirement_check(flash_chip_id, flash_freq, voltage, temperature);
if ((hpm_requirement_check == SPI_FLASH_HPM_CMD_NEEDED) || (hpm_requirement_check == SPI_FLASH_HPM_DUMMY_NEEDED) || (hpm_requirement_check == SPI_FLASH_HPM_WRITE_SR_NEEDED)) {
ESP_EARLY_LOGI(HPM_TAG, "Enabling flash high speed mode by %s", chip_hpm->method);
chip_hpm->flash_hpm_enable();
ESP_EARLY_LOGD(HPM_TAG, "Checking whether HPM has been executed");
if (chip_hpm->flash_hpf_check() != ESP_OK) {
ESP_EARLY_LOGE(HPM_TAG, "Flash high performance mode hasn't been executed successfully");
return ESP_FAIL;
}
#if CONFIG_SPI_FLASH_HPM_DC_ON
s_hpm_dummy_changed = (hpm_requirement_check == SPI_FLASH_HPM_DUMMY_NEEDED) ? true : false;
#else
assert(hpm_requirement_check != SPI_FLASH_HPM_DUMMY_NEEDED);
#endif
} else if (hpm_requirement_check == SPI_FLASH_HPM_BEYOND_LIMIT) {
ESP_EARLY_LOGE(HPM_TAG, "Flash does not have the ability to raise to that frequency");
return ESP_FAIL;
}
return ESP_OK;
}
#else
//!CONFIG_SPI_FLASH_UNDER_HIGH_FREQ
static spi_flash_hpm_dummy_conf_t s_dummy_conf;
esp_err_t spi_flash_enable_high_performance_mode(void)
{
return ESP_OK;
}
const spi_flash_hpm_dummy_conf_t *spi_flash_hpm_get_dummy(void)
{
spi_flash_hpm_get_dummy_generic(&s_dummy_conf);
return &s_dummy_conf;
}
bool spi_flash_hpm_dummy_adjust(void)
{
return false;
}
#endif //CONFIG_SPI_FLASH_UNDER_HIGH_FREQ
//-----------------------generic functions-------------------------------------//
/**
* @brief Default dummy for almost all flash chips. If your flash doesn't need to reconfigure dummy,
* just call this function.
*/
void __attribute__((weak)) spi_flash_hpm_get_dummy_generic(spi_flash_hpm_dummy_conf_t *dummy_conf)
{
dummy_conf->dio_dummy = SPI_FLASH_DIO_DUMMY_BITLEN;
dummy_conf->dout_dummy = SPI_FLASH_DOUT_DUMMY_BITLEN;
dummy_conf->qio_dummy = SPI_FLASH_QIO_DUMMY_BITLEN;
dummy_conf->qout_dummy = SPI_FLASH_QOUT_DUMMY_BITLEN;
dummy_conf->fastrd_dummy = SPI_FLASH_FASTRD_DUMMY_BITLEN;
}
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*
* Stub for spi_flash_is_octal_mode_enabled() when building for non-OS contexts
* (bootloader, TEE, PURE_RAM_APP). Full flash_ops.c is not built there, but
* esp_hw_support may still reference this symbol; returning false is safe.
*/
#include <stdbool.h>
bool spi_flash_is_octal_mode_enabled(void)
{
return false;
}
+177
View File
@@ -0,0 +1,177 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "esp_rom_spiflash.h"
#include "rom/spi_flash.h"
#include "esp_flash_chips/spi_flash_override.h"
#include "esp_private/spi_flash_os.h"
// TODO: These dependencies will be removed after remove bootloader_flash to G0.IDF-4609
#include "bootloader_flash_override.h"
#include "esp_private/bootloader_flash_internal.h"
#define CMD_WRAP 0x77
#define CMD_BURST_RD 0xC0
/***********************************************************************************
* Flash wrap feature (also called burst read on some flash chips)
*
* Different flash chips enter wrap (burst read) mode in different strategies.
* 1. Command 0xC0 + 8 Bytes.
* 2. Command 0x77 + 24 dummy + 8 Bytes.
**********************************************************************************/
#if SOC_SPI_MEM_SUPPORT_WRAP
const static char *FLASH_WRAP_TAG = "flash wrap";
// TODO: This function will be changed after remove bootloader_flash to G0.IDF-4609
extern uint32_t bootloader_flash_execute_command_common(
uint8_t command,
uint32_t addr_len, uint32_t address,
uint8_t dummy_len,
uint8_t mosi_len, uint32_t mosi_data,
uint8_t miso_len);
esp_err_t spi_flash_wrap_probe_c0(uint32_t flash_id)
{
esp_err_t ret = ESP_OK;
switch (flash_id) {
/* The flash listed here should enter the wrap with command 0xC0 */
case 0xC22018:
break;
default:
ret = ESP_ERR_NOT_FOUND;
break;
}
return ret;
}
/**
* @brief Burst read with command 0xC0 + 8 Bytes
*
* |------------|-----------------------------|
* | data | wrap depth |
* | 00h | 8 |
* | 01h | 16 |
* | 02h | 32 |
* | 03h | 64 |
* |------------|-----------------------------|
*/
esp_err_t spi_flash_wrap_enable_c0(spi_flash_wrap_size_t wrap_size)
{
uint8_t wrap_code = (uint8_t)(__builtin_ctz(wrap_size) - 3);
bootloader_flash_execute_command_common(CMD_BURST_RD, 0, 0, 0, 8, wrap_code, 0);
return ESP_OK;
}
/**
* @brief Burst read with command 0x77 + 24 Dummy + 8 Bytes
*
* |-------------------|-----------------------------|
* | data(W6,W5) | wrap depth |
* | 00h | 8 |
* | 01h | 16 |
* | 02h | 32 |
* | 03h | 64 |
* |-------------------|-----------------------------|
*/
esp_err_t spi_flash_wrap_enable_77(spi_flash_wrap_size_t wrap_size)
{
uint8_t wrap_code = (uint8_t)(((__builtin_ctz(wrap_size) - 3) * 2) << 4);
// According to the special format, we need enable QIO_FWRITE for command 77h and clear it after this command is done.
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_MEM_FWRITE_QIO);
bootloader_flash_execute_command_common(CMD_WRAP, 0, 0, 6, 8, wrap_code, 0);
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_MEM_FWRITE_QIO);
return ESP_OK;
}
/**
* @brief Burst read is cleared by setting 0x1xh,
* so we set 0x10 to disable this feature.
*/
esp_err_t spi_flash_wrap_clear_c0(void)
{
bootloader_flash_execute_command_common(CMD_BURST_RD, 0, 0, 0, 8, 0x10, 0);
return ESP_OK;
}
/**
* @brief Burst read is cleared by setting W4 bit 1,
* so we set 0x10 to disable this feature.
*/
esp_err_t spi_flash_wrap_clear_77(void)
{
// According to the special format, we need enable QIO_FWRITE for command 77h and clear it after this command is done.
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_MEM_FWRITE_QIO);
bootloader_flash_execute_command_common(CMD_WRAP, 0, 0, 6, 8, 0x10, 0);
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_MEM_FWRITE_QIO);
return ESP_OK;
}
const spi_flash_wrap_info_t __attribute__((weak)) spi_flash_wrap_list[] = {
/* method probe chip wrap set chip wrap clear */
{"C0H+8B", spi_flash_wrap_probe_c0, spi_flash_wrap_enable_c0, spi_flash_wrap_clear_c0},
{"default", NULL, spi_flash_wrap_enable_77, spi_flash_wrap_clear_77},
};
static const spi_flash_wrap_info_t *chip_wrap = NULL;
esp_err_t spi_flash_wrap_probe(void)
{
uint32_t flash_chip_id = g_rom_flashchip.device_id;
const spi_flash_wrap_info_t *chip = spi_flash_wrap_list;
esp_err_t ret = ESP_OK;
while (chip->probe) {
ret = chip->probe(flash_chip_id);
if (ret == ESP_OK) {
break;
}
chip++;
}
chip_wrap = chip;
return ret;
}
esp_err_t spi_flash_wrap_enable(spi_flash_wrap_size_t wrap_size)
{
return chip_wrap->chip_wrap_set(wrap_size);
}
esp_err_t spi_flash_wrap_disable(void)
{
return chip_wrap->chip_wrap_clr();
}
bool spi_flash_support_wrap_size(uint32_t wrap_size)
{
// Only QIO mode supports wrap.
if (!REG_GET_BIT(PERIPHS_SPI_FLASH_CTRL, SPI_MEM_FREAD_QIO)) {
ESP_EARLY_LOGE(FLASH_WRAP_TAG, "flash wrap is only supported in QIO mode");
abort();
}
// Only following size can be wrapped.
switch (wrap_size) {
case 0:
case 8:
case 16:
case 32:
case 64:
return true;
default:
return false;
}
}
#endif // SOC_SPI_MEM_SUPPORT_WRAP
@@ -0,0 +1,20 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/esp_mspi/test_apps/mspi:
depends_components:
- esp_mspi
- esp_hal_mspi
- spi_flash
- esp_psram
disable:
- if: IDF_TARGET not in ["esp32s3", "esp32p4", "esp32c61", "esp32c5", "esp32s31"]
- if: CONFIG_NAME in ["generic_timing_tuning_log_safe", "generic_timing_tuning_xip"] and IDF_TARGET in ["esp32s31"]
- if: CONFIG_NAME == "120sdr_120sdr" and IDF_TARGET in ["esp32s3", "esp32p4", "esp32s31"]
components/esp_mspi/test_apps/no_flash_delay:
depends_components:
- esp_mspi
- esp_hal_mspi
disable:
- if: IDF_TARGET not in ["esp32c3"]
reason: Testing on a single target is sufficient
@@ -0,0 +1,10 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on esp_psram
# as we set CONFIG_SPIRAM_... options.
set(COMPONENTS main esp_psram)
project(mspi_test_app)
@@ -0,0 +1,7 @@
| Supported Targets | ESP32-C5 | ESP32-C61 | ESP32-P4 | ESP32-S3 | ESP32-S31 |
| ----------------- | -------- | --------- | -------- | -------- | --------- |
This project tests if Flash and PSRAM can work under different configurations.
To add new configuration, create one more sdkconfig.ci.NAME file in this directory.
If you need to test for anything other than flash and psram, create another test project.
@@ -0,0 +1,11 @@
set(srcs
"test_app_main.c"
"test_flash_psram.c"
"test_mspi.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 esp_timer spi_flash esp_partition esp_hal_mspi esp_mspi
WHOLE_ARCHIVE)
@@ -0,0 +1,3 @@
dependencies:
test_utils:
path: ${IDF_PATH}/tools/test_apps/components/test_utils
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_heap_caps.h"
#include "esp_newlib.h"
// load partition table in tests will use memory
#define TEST_MEMORY_LEAK_THRESHOLD (600)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
printf("\n");
printf("===================TEST MSPI=================\n");
unity_run_menu();
}
@@ -0,0 +1,194 @@
/*
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_check.h"
#include "esp_attr.h"
#include "esp_flash.h"
#include "esp_partition.h"
#if CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/spi_flash.h"
#include "esp32s3/rom/opi_flash.h"
#endif
__attribute__((unused)) const static char *TAG = "MSPI";
//-----------------------------------------SPI0 PSRAM TEST-----------------------------------------------//
#if CONFIG_SPIRAM
#if CONFIG_SPIRAM_MODE_OCT
#define SPI0_PSRAM_TEST_LEN (512 * 1024)
#define LENGTH_PER_TIME 1024
#else
#define SPI0_PSRAM_TEST_LEN (128 * 1024)
#define LENGTH_PER_TIME 1024
#endif
TEST_CASE("MSPI: Test_SPI0_PSRAM", "[mspi]")
{
printf("----------SPI0 PSRAM Test----------\n");
uint8_t *psram_rd_buf = (uint8_t *)heap_caps_malloc(LENGTH_PER_TIME, MALLOC_CAP_32BIT | MALLOC_CAP_SPIRAM);
if (!psram_rd_buf) {
printf("no memory\n");
abort();
}
uint8_t *psram_wr_buf = (uint8_t *)heap_caps_malloc(SPI0_PSRAM_TEST_LEN, MALLOC_CAP_32BIT | MALLOC_CAP_SPIRAM);
if (!psram_wr_buf) {
printf("no memory\n");
abort();
}
srand(399);
for (int i = 0; i < SPI0_PSRAM_TEST_LEN / LENGTH_PER_TIME; i++) {
for (int j = 0; j < sizeof(psram_rd_buf); j++) {
psram_rd_buf[j] = rand();
}
memcpy(psram_wr_buf + i * LENGTH_PER_TIME, psram_rd_buf, LENGTH_PER_TIME);
if (memcmp(psram_wr_buf + i * LENGTH_PER_TIME, psram_rd_buf, LENGTH_PER_TIME) != 0) {
free(psram_wr_buf);
free(psram_rd_buf);
TEST_FAIL_MESSAGE("SPI0 PSRAM Test Fail");
}
}
free(psram_wr_buf);
free(psram_rd_buf);
printf(DRAM_STR("----------SPI0 PSRAM Test Success----------\n\n"));
}
#endif
//-----------------------------------------SPI1 FLASH TEST-----------------------------------------------//
#define SPI1_FLASH_TEST_LEN 512
#define SECTOR_LEN 4096
#define SPI1_FLASH_TEST_NUM (SECTOR_LEN / SPI1_FLASH_TEST_LEN)
#define SPI1_FLASH_TEST_ADDR 0x2a0000
static uint8_t rd_buf[SPI1_FLASH_TEST_LEN];
static uint8_t wr_buf[SPI1_FLASH_TEST_LEN];
static const esp_partition_t *get_test_flash_partition(void)
{
/* This finds "flash_test" partition defined in partition_table_unit_test_app.csv */
const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_ANY, "flash_test");
assert(result != NULL); /* means partition table set wrong */
return result;
}
TEST_CASE("MSPI: Test_SPI1_Flash", "[mspi]")
{
printf(DRAM_STR("----------SPI1 Flash Test----------\n"));
//We need to use SPI1
const esp_partition_t* part = get_test_flash_partition();
esp_flash_erase_region(part->flash_chip, part->address, part->size);
for (int i = 0; i < SPI1_FLASH_TEST_NUM; i++) {
for (int j = i + 10; j < SPI1_FLASH_TEST_LEN; j++) {
wr_buf[j] = j;
}
uint32_t test_flash_addr = SPI1_FLASH_TEST_ADDR;
esp_flash_write(part->flash_chip, wr_buf, part->address, sizeof(wr_buf));
esp_flash_read(part->flash_chip, rd_buf, part->address, sizeof(rd_buf));
if (memcmp(wr_buf, rd_buf, SPI1_FLASH_TEST_LEN) != 0) {
printf(DRAM_STR("error happened between 0x%x and 0x%x!!!!\n"), test_flash_addr, test_flash_addr + SPI1_FLASH_TEST_LEN);
for (int i = 0; i < SPI1_FLASH_TEST_LEN; i++) {
if (wr_buf[i] != rd_buf[i]) {
printf(DRAM_STR("err: wr[%d]: 0x%02x -- rd[%d]: 0x%02x\n"), i, wr_buf[i], i, rd_buf[i]);
}
}
TEST_FAIL_MESSAGE("SPI1 Flash Test Fail");
}
memset(rd_buf, 0x0, SPI1_FLASH_TEST_LEN);
}
printf(DRAM_STR("----------SPI1 Flash Test Success----------\n\n"));
}
//-----------------------------------------SPI0 FLASH TEST-----------------------------------------------//
#define SPI0_FLASH_TEST_LEN 32
#define SPI0_FLASH_TEST_BUF {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}
static const uint8_t flash_rd_buf[SPI0_FLASH_TEST_LEN] = SPI0_FLASH_TEST_BUF;
extern int _flash_rodata_start;
extern int _rodata_reserved_end;
TEST_CASE("MSPI: Test_SPI0_Flash", "[mspi]")
{
printf("----------SPI0 Flash Test----------\n");
//Check if the flash_rd_buf is in .rodata
assert(((intptr_t)flash_rd_buf >= (intptr_t)&_flash_rodata_start) && ((intptr_t)flash_rd_buf < (intptr_t)&_rodata_reserved_end));
uint8_t cmp_buf[SPI0_FLASH_TEST_LEN] = SPI0_FLASH_TEST_BUF;
for (int i = 0; i < SPI0_FLASH_TEST_LEN; i++) {
if (flash_rd_buf[i] != cmp_buf[i]) {
TEST_FAIL_MESSAGE("SPI0 Flash Test Fail");
}
}
printf(DRAM_STR("----------SPI0 Flash Test Success----------\n\n"));
}
/*---------------------------------------------------------------
XIP + PSRAM Stack + Flash API
---------------------------------------------------------------*/
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA
typedef struct {
SemaphoreHandle_t sem;
const esp_partition_t *part;
} test_flash_api_ctx_t;
static void test_flash_api_on_psram_when_xip(void *arg)
{
test_flash_api_ctx_t *ctx = (test_flash_api_ctx_t *)arg;
SemaphoreHandle_t test_semphr = ctx->sem;
TEST_ESP_OK(esp_flash_erase_region(ctx->part->flash_chip, ctx->part->address, ctx->part->size));
uint32_t test_val = 0x55;
uint32_t read_val = 0;
TEST_ESP_OK(esp_flash_write(ctx->part->flash_chip, &test_val, ctx->part->address, 4));
TEST_ESP_OK(esp_flash_read(ctx->part->flash_chip, &read_val, ctx->part->address, 4));
TEST_ASSERT(test_val == read_val);
xSemaphoreGive(test_semphr);
vTaskDelete(NULL);
}
TEST_CASE("test Flash API work with PSRAM stack when XIP_PSRAM", "[psram]")
{
SemaphoreHandle_t test_semphr = xSemaphoreCreateBinary();
TEST_ASSERT(test_semphr);
const esp_partition_t *part = get_test_flash_partition();
ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
test_flash_api_ctx_t ctx = {
.sem = test_semphr,
.part = part,
};
int size_stack = 1024 * 4;
StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
printf("init_task: current addr_stack = %p, stack_for_task = %p\n", esp_cpu_get_sp(), stack_for_task);
static StaticTask_t task_buf;
xTaskCreateStaticPinnedToCore(test_flash_api_on_psram_when_xip, "test_flash_api_on_psram_when_xip", size_stack, &ctx, 5, stack_for_task, &task_buf, 0);
xSemaphoreTake(test_semphr, portMAX_DELAY);
vSemaphoreDelete(test_semphr);
free(stack_for_task);
}
#endif //CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "esp_timer.h"
#include "esp_private/mspi_timing_tuning.h"
static void sorted_array_insert(uint32_t *array, uint32_t *size, uint32_t item)
{
uint32_t pos;
for (pos = *size; pos > 0; pos--) {
if (array[pos - 1] <= item) {
break;
}
array[pos] = array[pos - 1];
}
array[pos] = item;
(*size)++;
}
#if CONFIG_IDF_TARGET_ESP32S3
#define TEST_TIME_CNT 10
#define TEST_TIME_LIMIT_US 10
#elif CONFIG_IDF_TARGET_ESP32P4
#define TEST_TIME_CNT 30
#define TEST_TIME_LIMIT_US 30
#else
#define TEST_TIME_CNT 20
#define TEST_TIME_LIMIT_US 20
#endif
TEST_CASE("MSPI: Test mspi timing tuning time cost", "[mspi]")
{
uint64_t start, end;
uint32_t cost, index_1 = 0, index_2 = 0;
uint32_t slow_down_time[TEST_TIME_CNT], speed_up_time[TEST_TIME_CNT];
printf("\nFunc call `mspi_timing_change_speed_mode_cache_safe` should spend time less than %d us\n", TEST_TIME_LIMIT_US);
for (uint8_t i = 0; i < TEST_TIME_CNT; i++) {
start = esp_timer_get_time();
mspi_timing_change_speed_mode_cache_safe(true);
end = esp_timer_get_time();
cost = end - start;
sorted_array_insert(slow_down_time, &index_1, cost);
printf("mspi psram slow down cost %ld\t", cost);
start = esp_timer_get_time();
mspi_timing_change_speed_mode_cache_safe(false);
end = esp_timer_get_time();
cost = end - start;
sorted_array_insert(speed_up_time, &index_2, cost);
printf("mspi psram speed up cost %ld\n", cost);
}
TEST_ASSERT_LESS_THAN_UINT32(TEST_TIME_LIMIT_US, slow_down_time[TEST_TIME_CNT / 2]);
TEST_ASSERT_LESS_THAN_UINT32(TEST_TIME_LIMIT_US, speed_up_time[TEST_TIME_CNT / 2]);
}
@@ -0,0 +1,6 @@
# 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,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
flash_test, data, fat, , 512K,
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 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 flash_test, data, fat, , 512K,
@@ -0,0 +1,118 @@
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import os
import pathlib
import pytest
from pytest_embedded_idf import IdfDut
from pytest_embedded_idf.utils import idf_parametrize
MSPI_F8R8_configs = [
p.name.replace('sdkconfig.ci.', '')
for p in pathlib.Path(os.path.dirname(__file__)).glob('sdkconfig.ci.esp32s3_f8r8*')
]
@pytest.mark.MSPI_F8R8
@pytest.mark.parametrize('config', MSPI_F8R8_configs, indirect=True)
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
def test_flash8_psram8(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
# For F4R8 board (Quad Flash and Octal PSRAM)
MSPI_F4R8_configs = [
p.name.replace('sdkconfig.ci.', '')
for p in pathlib.Path(os.path.dirname(__file__)).glob('sdkconfig.ci.esp32s3_f4r8*')
]
@pytest.mark.MSPI_F4R8
@pytest.mark.parametrize('config', MSPI_F4R8_configs, indirect=True)
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
def test_flash4_psram8(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
# For F4R4 board (Quad Flash and Quad PSRAM)
MSPI_F4R4_configs = [
p.name.replace('sdkconfig.ci.', '')
for p in pathlib.Path(os.path.dirname(__file__)).glob('sdkconfig.ci.esp32s3_f4r4*')
]
@pytest.mark.MSPI_F4R4
@pytest.mark.parametrize('config', MSPI_F4R4_configs, indirect=True)
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
def test_flash4_psram4(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.flash_120m
@pytest.mark.parametrize(
'config',
[
'esp32p4_120sdr_200ddr',
],
indirect=True,
)
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
def test_flash_psram_esp32p4(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'120sdr_120sdr',
],
indirect=True,
)
@idf_parametrize('target', ['esp32c5', 'esp32c61'], indirect=['target'])
def test_flash_psram_120sdr_120sdr(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'esp32s31_120sdr_200ddr',
],
indirect=True,
)
@idf_parametrize('target', ['esp32s31'], indirect=['target'])
def test_flash_psram_esp32s31(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.parametrize(
'config',
[
'generic_timing_tuning_log_safe',
'generic_timing_tuning_xip',
],
indirect=True,
)
@idf_parametrize(
'target,markers',
[
# S3 has no flash support auto suspend, this test is not applicable
(
'esp32p4',
(pytest.mark.generic,),
),
(
'esp32c5',
(pytest.mark.generic,),
),
(
'esp32c61',
(pytest.mark.generic,),
),
],
indirect=['target'],
)
def test_flash_psram_generic(dut: IdfDut) -> None:
dut.run_all_single_board_cases()
@@ -0,0 +1,6 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_120M=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
@@ -0,0 +1,7 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_200M=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
@@ -0,0 +1,10 @@
CONFIG_IDF_TARGET="esp32s31"
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_200M=y
#use 80 until 120 is supported TODO: IDF-14653
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
#CONFIG_SPI_FLASH_HPM_ENA=y
#CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
@@ -0,0 +1,11 @@
# Legacy, F4R4, Flash 120M SDR, PSRAM disable
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
@@ -0,0 +1,13 @@
# Legacy, F4R4, Flash 120M SDR, PSRAM 120M SDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_120M=y
@@ -0,0 +1,13 @@
# Legacy, F4R4, Flash 120M SDR, PSRAM 40M SDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_40M=y
@@ -0,0 +1,15 @@
# Legacy, F4R4, Flash 120M SDR, PSRAM disable
# config for build only
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_SPI_FLASH_HPM_ON=y
CONFIG_SPI_FLASH_HPM_DC_DISABLE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
@@ -0,0 +1,15 @@
# Legacy, F4R4, Flash 120M SDR, PSRAM disable, compiler -Os and silent
CONFIG_IDF_TARGET="esp32s3"
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
@@ -0,0 +1,11 @@
# Legacy, F4R4, Flash 40M SDR, PSRAM 120M SDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_120M=y
@@ -0,0 +1,11 @@
# Legacy, F4R4, Flash 80M SDR, PSRAM 80M SDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_80M=y
@@ -0,0 +1,11 @@
# Legacy, F4R8, Flash 120M SDR, PSRAM disable
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_SPI_FLASH_HPM_ENA=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_BOOTLOADER_FLASH_DC_AWARE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
@@ -0,0 +1,12 @@
# Legacy, F4R8, Flash 80M SDR, PSRAM 40M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_40M=y
@@ -0,0 +1,12 @@
# Legacy, F4R8, Flash 80M SDR, PSRAM 80M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
@@ -0,0 +1,12 @@
# Legacy, F8R8, Flash 120M SDR, PSRAM disable
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=n
@@ -0,0 +1,14 @@
# Legacy, F8R8, Flash 40M DDR, PSRAM 40M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_40M=y
@@ -0,0 +1,14 @@
# Legacy, F8R8, Flash 40M DDR, PSRAM 80M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
@@ -0,0 +1,14 @@
# Legacy, F8R8, Flash 80M DDR, PSRAM 40M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_40M=y
@@ -0,0 +1,14 @@
# Legacy, F8R8, Flash 80M DDR, PSRAM 80M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
@@ -0,0 +1,15 @@
# Legacy, F8R8, Flash 80M DDR, PSRAM 80M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_ECC_ENABLE=y
@@ -0,0 +1,14 @@
# Legacy, F8R8, Flash 80M SDR, PSRAM 80M DDR
CONFIG_IDF_TARGET="esp32s3"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
@@ -0,0 +1,5 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
CONFIG_LOG_IN_IRAM=n
@@ -0,0 +1,4 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
@@ -0,0 +1,6 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(test_build)
@@ -0,0 +1,6 @@
| Supported Targets | ESP32-C3 |
| ----------------- | -------- |
This project tests building with the no_flash_delay configuration.
This project uses MINIMAL_BUILD=y to reduce build time and dependencies.
@@ -0,0 +1,2 @@
idf_component_register(SRCS "test_main.c"
INCLUDE_DIRS ".")
@@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
void app_main(void)
{
}
@@ -0,0 +1 @@
CONFIG_SPI_FLASH_YIELD_DURING_ERASE=n
@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(test_build)
@@ -0,0 +1,6 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |
This project tests building with the spi_flash_opts configuration.
This project uses MINIMAL_BUILD=y to reduce build time and dependencies.
@@ -0,0 +1,2 @@
idf_component_register(SRCS "test_main.c"
INCLUDE_DIRS ".")
@@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
void app_main(void)
{
}
@@ -0,0 +1 @@
CONFIG_SPI_FLASH_SIZE_OVERRIDE=y