Merge branch 'refactor/flash_refactor' into 'master'

spi_flash: split driver and os driver into `spi_flash` and `esp_mspi` components

Closes IDF-15210

See merge request espressif/esp-idf!45409
This commit is contained in:
Armando (Dou Yiwen)
2026-09-10 07:29:01 +00:00
266 changed files with 1558 additions and 1208 deletions
+3 -47
View File
@@ -14,10 +14,6 @@ endif()
set(public_include_dirs "include" "include/soc"
"ldo/include" "debug_probe/include" "etm/include"
"mspi/mspi_timing_tuning/include" "mspi/mspi_timing_tuning/tuning_scheme_impl/include"
"mspi/mspi_intr/include"
"mspi/esp_mspi_align/include"
"mspi/mspi_mem_barrier/include"
"power_supply/include" "modem/include")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include/soc/${target}")
@@ -44,7 +40,7 @@ endif()
set(requires esp_hal_gpio esp_hal_usb esp_hal_pmu esp_hal_regi2c esp_hal_uart esp_hal_debug_assist)
set(priv_requires efuse # only esp_hw_support/adc_share_hw_ctrl.c requires efuse component
spi_flash
esp_mspi
bootloader_support
esp_hal_wdt
esp_hal_rtc_timer
@@ -55,9 +51,6 @@ set(priv_requires efuse # only esp_hw_support/adc_share_hw_ctrl.
)
set(srcs "cpu.c" "port/${IDF_TARGET}/esp_cpu_intr.c" "esp_memory_utils.c" "port/${IDF_TARGET}/cpu_region_protect.c")
if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "mspi/esp_mspi_align/esp_mspi_align.c")
endif()
if(NOT non_os_build)
list(APPEND srcs "esp_clk.c"
"clk_ctrl_os.c"
@@ -76,8 +69,7 @@ if(NOT non_os_build)
"port/${target}/esp_clk_tree.c"
"spi_bus_lock.c"
"heap_align_hw.c"
"clk_utils.c"
"mspi/mspi_mem_barrier/mspi_mem_barrier.c")
"clk_utils.c")
if(CONFIG_SOC_USB_OTG_SUPPORTED)
list(APPEND srcs "usb_phy/usb_phy.c")
endif()
@@ -156,27 +148,6 @@ if(NOT non_os_build)
"port/regdma_link.c")
endif()
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi/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/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_mspi_delay.c"
)
if(EXISTS "${mspi_delay_file}")
list(APPEND srcs "mspi/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/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/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_flash_delay.c")
endif()
list(APPEND srcs "mspi/mspi_intr/mspi_intr.c")
endif()
if(CONFIG_SOC_RTC_FAST_MEM_SUPPORTED AND CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB)
list(APPEND srcs "sleep_wake_stub.c")
endif()
@@ -196,18 +167,6 @@ if(NOT non_os_build)
else()
if(ESP_TEE_BUILD)
list(APPEND srcs "esp_clk.c" "hw_random.c")
if(CONFIG_SECURE_TEE_EXT_FLASH_MEMPROT_SPI1)
list(APPEND srcs "mspi/mspi_timing_tuning/mspi_timing_tuning.c")
if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY)
list(APPEND srcs "mspi/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/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/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_flash_delay.c")
endif()
endif()
endif()
# Requires "_esp_error_check_failed()" function
@@ -224,15 +183,12 @@ idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS port/include include/esp_private
REQUIRES ${requires}
PRIV_REQUIRES "${priv_requires}"
LDFRAGMENTS linker.lf ldo/linker.lf mspi/linker.lf)
LDFRAGMENTS linker.lf ldo/linker.lf)
idf_define_esp_err_codes(HEADERS include/esp_memprot_err.h)
idf_build_get_property(target IDF_TARGET)
add_subdirectory(port/${target})
if(CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING)
add_subdirectory(mspi/mspi_timing_tuning/port/${target})
endif()
add_subdirectory(lowpower)
add_subdirectory(modem)
+1 -1
View File
@@ -86,7 +86,7 @@ This document describes the implementation differences of MSPI interrupt handlin
| File | Description |
|------|-------------|
| `components/esp_hw_support/mspi/mspi_intr/mspi_intr.c` | Shared MSPI interrupt management |
| `components/esp_mspi/mspi_intr/mspi_intr.c` | Shared MSPI interrupt management |
| `components/esp_psram/system_layer/esp_psram_mspi.c` | PSRAM specific interrupt handling |
---
@@ -1,59 +0,0 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_efuse.h"
#include "esp_memory_utils.h"
#include "esp_private/esp_mspi_align.h"
#if CONFIG_SPIRAM
#include "esp_psram.h"
#endif /* CONFIG_SPIRAM */
#include "soc/soc_caps.h"
#define MSPI_FLASH_ENC_ALIGNMENT SOC_MEMSPI_ENCRYPTION_ALIGNMENT
#define MSPI_PSRAM_ECC_ALIGNMENT SOC_MEMSPI_ENCRYPTION_ALIGNMENT
size_t esp_mspi_get_alignment(const void *ptr)
{
size_t alignment = 1;
bool generic_query = ptr == NULL;
bool __attribute__((unused)) is_psram = esp_ptr_external_ram(ptr);
bool is_drom = esp_ptr_in_drom(ptr);
bool is_psram_enc = false;
#if CONFIG_SPIRAM
is_psram_enc = is_psram && !esp_psram_ptr_is_no_enc(ptr);
#endif /* CONFIG_SPIRAM */
if ((generic_query || is_drom || is_psram_enc) && esp_efuse_is_flash_encryption_enabled()) {
alignment = MAX(alignment, MSPI_FLASH_ENC_ALIGNMENT);
}
#if CONFIG_SPIRAM_ECC_ENABLE
if (generic_query || is_psram) {
alignment = MAX(alignment, MSPI_PSRAM_ECC_ALIGNMENT);
}
#endif
return alignment;
}
bool esp_mspi_buffer_alignment_satisfied(const void *ptr, size_t size)
{
// Zero-length is not a valid MSPI transfer, so it never satisfies the check.
if (ptr == NULL || size == 0) {
return false;
}
size_t alignment = esp_mspi_get_alignment(ptr);
if (alignment <= 1) {
return true;
}
uintptr_t addr = (uintptr_t)ptr;
return ((addr & (alignment - 1)) == 0) && ((size & (alignment - 1)) == 0);
}
@@ -1,46 +0,0 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get MSPI alignment requirement for an address
*
* The address is used so future targets can apply different alignment rules to
* different MSPI-backed regions. Pass NULL when only the generic external-memory
* requirement is needed and no concrete address is available yet.
*
* @param ptr Buffer pointer in the region to be accessed, or NULL for generic query
* @return Required alignment in bytes, or 1 when no extra MSPI alignment is needed
*/
size_t esp_mspi_get_alignment(const void *ptr);
/**
* @brief Check whether a buffer satisfies MSPI strict alignment requirements
*
* Returns false when @p ptr is NULL or @p size is 0 (not a valid transfer).
* When strict alignment is not required, returns true for any non-empty buffer.
* When required, both @p ptr and @p size must be aligned to the rule returned by
* @ref esp_mspi_get_alignment for that address.
*
* @param ptr Buffer pointer
* @param size Transfer size in bytes
* @return true if alignment requirements are satisfied
*/
bool esp_mspi_buffer_alignment_satisfied(const void *ptr, size_t size);
#ifdef __cplusplus
}
#endif
-22
View File
@@ -1,22 +0,0 @@
[mapping:esp_mspi_align]
archive: libesp_hw_support.a
entries:
if APP_BUILD_TYPE_PURE_RAM_APP = n:
esp_mspi_align:esp_mspi_get_alignment (noflash)
esp_mspi_align:esp_mspi_buffer_alignment_satisfied (noflash)
[mapping:mspi_timing_tuning_driver]
archive: libesp_hw_support.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)
@@ -1,59 +0,0 @@
/*
* 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
@@ -1,140 +0,0 @@
/*
* 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
@@ -1,37 +0,0 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize the MSPI memory barrier workaround
*
* On targets affected by the MSPI memory barrier hardware issue, this allocates the
* dummy cacheline used by @ref esp_psram_mspi_mb to force a cache writeback/fence. On
* other targets this is a no-op.
*
* @return ESP_OK on success, otherwise an error code
*/
esp_err_t esp_psram_mspi_mb_init(void);
/**
* @brief MSPI memory barrier
*
* Some targets need this workaround after a DMA transfer into PSRAM to make sure the
* data is visible to the CPU. This is a no-op on targets that don't need the workaround.
*/
void esp_psram_mspi_mb(void);
#ifdef __cplusplus
}
#endif
@@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_cache.h"
#include "esp_heap_caps.h"
#include "esp_private/mspi_mem_barrier.h"
#define MSPI_MEM_BARRIER_WORKAROUND ((CONFIG_IDF_TARGET_ESP32C5 && CONFIG_ESP32C5_REV_MIN_FULL < 102) || (CONFIG_IDF_TARGET_ESP32C61 && CONFIG_ESP32C61_REV_MIN_FULL < 101))
__attribute__((unused)) ESP_LOG_ATTR_TAG_DRAM(TAG, "mspi_mem_barrier");
#if MSPI_MEM_BARRIER_WORKAROUND
static void *s_psram_mb_dummy_cacheline; //dummy cacheline for cache memory barrier
#endif
esp_err_t esp_psram_mspi_mb_init(void)
{
#if MSPI_MEM_BARRIER_WORKAROUND
s_psram_mb_dummy_cacheline = heap_caps_calloc(1, CONFIG_CACHE_L1_CACHE_LINE_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_CACHE_ALIGNED);
if (!s_psram_mb_dummy_cacheline) {
ESP_EARLY_LOGE(TAG, "Failed to allocate dummy cacheline for PSRAM memory barrier!");
}
#endif
return ESP_OK;
}
void IRAM_ATTR esp_psram_mspi_mb(void)
{
#if MSPI_MEM_BARRIER_WORKAROUND
if (s_psram_mb_dummy_cacheline) {
uint32_t *p = (uint32_t *)s_psram_mb_dummy_cacheline;
*p = (*p + 1) % UINT32_MAX;
__attribute__((unused)) esp_err_t ret = ESP_FAIL;
ret = esp_cache_msync(s_psram_mb_dummy_cacheline, sizeof(uint32_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); //malloc is aligned, no need to writeback all
assert(ret == ESP_OK);
asm volatile("fence");
}
#endif
}
@@ -1,40 +0,0 @@
/*
* 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
@@ -1,80 +0,0 @@
/*
* 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
@@ -1,30 +0,0 @@
/*
* 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
@@ -1,739 +0,0 @@
/*
* 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
}
@@ -1,11 +0,0 @@
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})
@@ -1,78 +0,0 @@
/*
* 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);
}
@@ -1,155 +0,0 @@
/*
* 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
@@ -1,11 +0,0 @@
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})
@@ -1,78 +0,0 @@
/*
* 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);
}
@@ -1,154 +0,0 @@
/*
* 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
@@ -1,11 +0,0 @@
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}")
@@ -1,60 +0,0 @@
/*
* 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
}
@@ -1,85 +0,0 @@
/*
* 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
@@ -1,11 +0,0 @@
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})
@@ -1,885 +0,0 @@
/*
* 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 "bootloader_flash.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 (bootloader_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 (bootloader_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
@@ -1,71 +0,0 @@
/*
* 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);
}
@@ -1,272 +0,0 @@
/*
* 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
@@ -1,11 +0,0 @@
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}")
@@ -1,60 +0,0 @@
/*
* 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
}
@@ -1,60 +0,0 @@
/*
* 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)
@@ -1,164 +0,0 @@
/*
* 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
@@ -1,153 +0,0 @@
/*
* 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
@@ -1,243 +0,0 @@
/*
* 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
@@ -1,85 +0,0 @@
/*
* 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
@@ -1,242 +0,0 @@
/*
* 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);
}
}
}
@@ -1,282 +0,0 @@
/*
* 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);
}
@@ -1,434 +0,0 @@
/*
* 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 "mspi_timing_tuning_configs.h"
#include "bootloader_flash.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 (bootloader_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;
}
+1 -2
View File
@@ -40,7 +40,6 @@
#include "esp_private/sleep_gpio.h"
#include "esp_private/spi_flash_os.h"
#include "esp_private/startup_internal.h"
#include "bootloader_flash.h"
ESP_LOG_ATTR_TAG(TAG, "sleep_gpio");
@@ -152,7 +151,7 @@ void esp_sleep_config_gpio_isolate(void)
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_HD), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_WP), GPIO_PULLUP_ONLY);
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
bool octal_mspi_required = bootloader_flash_is_octal_mode_enabled();
bool octal_mspi_required = spi_flash_is_octal_mode_enabled();
#if CONFIG_SPIRAM_MODE_OCT
octal_mspi_required |= true;
#endif // CONFIG_SPIRAM_MODE_OCT
@@ -22,18 +22,6 @@ components/esp_hw_support/test_apps/host_test_linux:
depends_components:
- esp_hw_support
components/esp_hw_support/test_apps/mspi:
depends_components:
- esp_hw_support
- spi_flash
- esp_partition
- esp_hal_mspi
- soc
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_hw_support/test_apps/mspi_psram_with_dfs:
depends_components:
- esp_hw_support
@@ -1,10 +0,0 @@
# 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)
@@ -1,7 +0,0 @@
| 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.
@@ -1,11 +0,0 @@
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
WHOLE_ARCHIVE)
@@ -1,31 +0,0 @@
/*
* 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();
}
@@ -1,197 +0,0 @@
/*
* 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
@@ -1,61 +0,0 @@
/*
* 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]);
}
@@ -1,6 +0,0 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
flash_test, data, fat, , 512K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 flash_test, data, fat, , 512K,
@@ -1,118 +0,0 @@
# 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()
@@ -1,6 +0,0 @@
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
@@ -1,7 +0,0 @@
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
@@ -1,10 +0,0 @@
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
@@ -1,11 +0,0 @@
# 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
@@ -1,13 +0,0 @@
# 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
@@ -1,13 +0,0 @@
# 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
@@ -1,15 +0,0 @@
# 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
@@ -1,15 +0,0 @@
# 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
@@ -1,11 +0,0 @@
# 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
@@ -1,11 +0,0 @@
# 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
@@ -1,11 +0,0 @@
# 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
@@ -1,12 +0,0 @@
# 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
@@ -1,12 +0,0 @@
# 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
@@ -1,12 +0,0 @@
# 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
@@ -1,14 +0,0 @@
# 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
@@ -1,14 +0,0 @@
# 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
@@ -1,14 +0,0 @@
# 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
@@ -1,14 +0,0 @@
# 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
@@ -1,15 +0,0 @@
# 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
@@ -1,14 +0,0 @@
# 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
@@ -1,5 +0,0 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
CONFIG_LOG_IN_IRAM=n
@@ -1,4 +0,0 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
@@ -1,6 +0,0 @@
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"