mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
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:
committed by
Armando (Dou Yiwen)
parent
c565262ded
commit
6e3abd12a1
@@ -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()
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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*)®_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 = ®istered_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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
+163
@@ -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
|
||||
+151
@@ -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
|
||||
+239
@@ -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
|
||||
+84
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+282
@@ -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);
|
||||
}
|
||||
+434
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
@@ -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
|
||||
Reference in New Issue
Block a user