Skip to content

Commit

Permalink
Merge branch 'feature/experimental_feature_mspi_120_ddr' into 'master'
Browse files Browse the repository at this point in the history
mspi: experimental feature mspi 120 mhz ddr

Closes IDF-7073

See merge request espressif/esp-idf!22952
  • Loading branch information
Icarus113 committed Apr 3, 2023
2 parents bcbe8fe + 384b3bd commit ebaf987
Show file tree
Hide file tree
Showing 15 changed files with 293 additions and 65 deletions.
9 changes: 9 additions & 0 deletions Kconfig
Expand Up @@ -522,3 +522,12 @@ mainmenu "Espressif IoT Development Framework Configuration"
menu "Component config"
source "$COMPONENT_KCONFIGS_SOURCE_FILE"
endmenu

config IDF_EXPERIMENTAL_FEATURES
bool "Make experimental features visible"
default "n"
help
By enabling this option, ESP-IDF experimental feature options will be visible.

Note you should still enable a certain experimental feature option to use it, and you
should read the corresponding risk warning and known issue list carefully.
42 changes: 22 additions & 20 deletions components/esp_hw_support/mspi_timing_config.h
Expand Up @@ -139,43 +139,45 @@ void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t le

/*-------------------------------------------------------------------------------------------------
* SPI1 Timing Tuning APIs
* These APIs are only used in `spi_flash_timing_tuning.c/sweep_for_success_sample_points()` for
* configuring SPI1 timing tuning related registers to find best tuning parameter
*
* These APIs are only used in `mspi_timing_tuning.c` for configuring SPI1 timing
* tuning related registers to find best tuning parameter for Flash and PSRAM
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Tune Flash Din Mode and Num of SPI1
* @param din_mode Din mode
* @param din_num Din num
* @brief Tune Flash timing registers for SPI1 accessing Flash
*
* @param[in] params Timing parameters
*/
void mspi_timing_config_flash_tune_din_num_mode(uint8_t din_mode, uint8_t din_num);
void mspi_timing_config_flash_set_tuning_regs(const mspi_timing_tuning_param_t *params);

/**
* @brief Tune Flash extra dummy of SPI1
* @param extra_dummy extra dummy bits
* @brief Tune PSRAM timing registers for SPI1 accessing PSRAM
*
* @param[in] params Timing parameters
*/
void mspi_timing_config_flash_tune_dummy(uint8_t extra_dummy);
void mspi_timing_config_psram_set_tuning_regs(const mspi_timing_tuning_param_t *params);

/**
* @brief Tune PSRAM Din Mode and Num of SPI1
* @param din_mode Din mode
* @param din_num Din num
*/
void mspi_timing_config_psram_tune_din_num_mode(uint8_t din_mode, uint8_t din_num);

/*-------------------------------------------------------------------------------------------------
* APIs for coordination with ESP Flash driver
*-------------------------------------------------------------------------------------------------*/
/**
* @brief Tune PSRAM extra dummy of SPI1
* @param extra_dummy extra dummy bits
* SPI1 register info get APIs. These APIs inform `mspi_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 (`mspi_timing_tuning.c`).
*/
void mspi_timing_config_psram_tune_dummy(uint8_t extra_dummy);

/**
* 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);

Expand Down
128 changes: 115 additions & 13 deletions components/esp_hw_support/mspi_timing_tuning.c
Expand Up @@ -15,8 +15,11 @@
#include "soc/io_mux_reg.h"
#include "esp_private/mspi_timing_tuning.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "hal/spi_flash_hal.h"
#include "hal/mspi_timing_tuning_ll.h"
#include "hal/clk_tree_ll.h"
#include "hal/regi2c_ctrl_ll.h"
#include "mspi_timing_config.h"
#if CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/cache.h"
Expand Down Expand Up @@ -107,7 +110,7 @@ static void init_spi1_for_tuning(bool is_flash)
* We use different SPI1 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 sweep_for_success_sample_points(uint8_t *reference_data, const mspi_timing_config_t *config, bool is_flash, uint8_t *out_array)
static void sweep_for_success_sample_points(const uint8_t *reference_data, const mspi_timing_config_t *config, bool is_flash, uint8_t *out_array)
{
uint32_t config_idx = 0;
uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0};
Expand All @@ -116,20 +119,21 @@ static void sweep_for_success_sample_points(uint8_t *reference_data, const mspi_
memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN);
#if MSPI_TIMING_FLASH_NEEDS_TUNING
if (is_flash) {
mspi_timing_config_flash_tune_din_num_mode(config->tuning_config_table[config_idx].spi_din_mode, config->tuning_config_table[config_idx].spi_din_num);
mspi_timing_config_flash_tune_dummy(config->tuning_config_table[config_idx].extra_dummy_len);
mspi_timing_config_flash_set_tuning_regs(&(config->tuning_config_table[config_idx]));
mspi_timing_config_flash_read_data(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(read_data));
}
#endif
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
if (!is_flash) {
mspi_timing_config_psram_tune_din_num_mode(config->tuning_config_table[config_idx].spi_din_mode, config->tuning_config_table[config_idx].spi_din_num);
mspi_timing_config_psram_tune_dummy(config->tuning_config_table[config_idx].extra_dummy_len);
mspi_timing_config_psram_set_tuning_regs(&(config->tuning_config_table[config_idx]));
mspi_timing_config_psram_read_data(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_EARLY_LOGD(TAG, "%d, good", config_idx);
} else {
ESP_EARLY_LOGD(TAG, "%d, bad", config_idx);
}
}
}
Expand Down Expand Up @@ -164,14 +168,77 @@ static void find_max_consecutive_success_points(uint8_t *array, uint32_t size, u
*out_end_index = match_num == size ? size : end;
}

#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;
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();

//BBPLL CALIBRATION START
regi2c_ctrl_ll_bbpll_calibration_start();
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) {
/**
* 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);

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);
clk_ll_bbpll_set_config(previous_config.source_freq_mhz, xtal_freq);

//WAIT CALIBRATION DONE
while(!regi2c_ctrl_ll_bbpll_calibration_is_done());

//BBPLL CALIBRATION STOP
regi2c_ctrl_ll_bbpll_calibration_stop();


*out_max_freq = max_freq;
*out_min_freq = min_freq;

return (max_freq != 0);
}
#endif //Frequency Scanning

#if MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE
static uint32_t select_best_tuning_config_dtr(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end)
static uint32_t select_best_tuning_config_dtr(mspi_timing_config_t *config, 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;
(void) reference_data;
(void) is_flash;
uint32_t best_point = 0;

//Define these magic number in macros in `spi_timing_config.h`. TODO: IDF-3663
//These numbers will probably be same on other chips, if this version of algorithm is utilised
if (consecutive_length <= 2 || consecutive_length >= 6) {
//tuning is FAIL, select default point, and generate a warning
best_point = config->default_config_id;
Expand All @@ -187,6 +254,41 @@ static uint32_t select_best_tuning_config_dtr(mspi_timing_config_t *config, uint
}

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 max_freq = 0;
uint32_t temp_max_freq = 0;
uint32_t temp_min_freq = 0;

for (; current_point <= end; current_point++) {
if (is_flash) {
mspi_timing_config_flash_set_tuning_regs(&(config->tuning_config_table[current_point]));
} else {
mspi_timing_config_psram_set_tuning_regs(&(config->tuning_config_table[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_EARLY_LOGD(TAG, "sample point %d, max pll is %d mhz, min pll is %d\n", current_point, temp_max_freq, temp_min_freq);
}
if (max_freq == 0) {
ESP_EARLY_LOGW(TAG, "freq scan tuning fail, best point is fallen back to index %d", end + 1 - consecutive_length);
best_point = end + 1 - consecutive_length;
} else {
ESP_EARLY_LOGD(TAG, "freq scan success, max pll is %dmhz, best point is index %d", max_freq, best_point);
}

return best_point;

#else
//won't reach here
abort();
Expand Down Expand Up @@ -221,27 +323,27 @@ static uint32_t select_best_tuning_config_str(mspi_timing_config_t *config, uint
}
#endif

static void select_best_tuning_config(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, bool is_flash)
static void select_best_tuning_config(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash)
{
uint32_t best_point = 0;
if (is_flash) {
#if MSPI_TIMING_FLASH_DTR_MODE
best_point = select_best_tuning_config_dtr(config, consecutive_length, end);
best_point = select_best_tuning_config_dtr(config, consecutive_length, end, reference_data, is_flash);
#elif MSPI_TIMING_FLASH_STR_MODE
best_point = select_best_tuning_config_str(config, consecutive_length, end);
#endif
s_flash_best_timing_tuning_config = config->tuning_config_table[best_point];
} else {
#if MSPI_TIMING_PSRAM_DTR_MODE
best_point = select_best_tuning_config_dtr(config, consecutive_length, end);
best_point = select_best_tuning_config_dtr(config, consecutive_length, end, reference_data, is_flash);
#elif MSPI_TIMING_PSRAM_STR_MODE
best_point = select_best_tuning_config_str(config, consecutive_length, end);
#endif
s_psram_best_timing_tuning_config = config->tuning_config_table[best_point];
}
}

static void do_tuning(uint8_t *reference_data, mspi_timing_config_t *timing_config, bool is_flash)
static void do_tuning(const uint8_t *reference_data, mspi_timing_config_t *timing_config, bool is_flash)
{
/**
* We use SPI1 to tune the timing:
Expand All @@ -256,7 +358,7 @@ static void do_tuning(uint8_t *reference_data, mspi_timing_config_t *timing_conf
init_spi1_for_tuning(is_flash);
sweep_for_success_sample_points(reference_data, timing_config, is_flash, sample_result);
find_max_consecutive_success_points(sample_result, MSPI_TIMING_CONFIG_NUM_DEFAULT, &consecutive_length, &last_success_point);
select_best_tuning_config(timing_config, consecutive_length, last_success_point, is_flash);
select_best_tuning_config(timing_config, consecutive_length, last_success_point, reference_data, is_flash);
}
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING

Expand Down
23 changes: 8 additions & 15 deletions components/esp_hw_support/port/esp32s3/mspi_timing_config.c
Expand Up @@ -290,40 +290,33 @@ void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t le
* These APIs are only used in `spi_flash_timing_tuning.c/sweep_for_success_sample_points()` for
* configuring SPI1 timing tuning related registers to find best tuning parameter
*-------------------------------------------------------------------------------------------------*/
void mspi_timing_config_flash_tune_din_num_mode(uint8_t din_mode, uint8_t din_num)
void mspi_timing_config_flash_set_tuning_regs(const mspi_timing_tuning_param_t *params)
{
/**
* 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
*/
mspi_timing_config_flash_set_din_mode_num(0, din_mode, din_num);
mspi_timing_config_flash_set_din_mode_num(0, params->spi_din_mode, params->spi_din_num);
mspi_timing_config_flash_set_extra_dummy(1, params->extra_dummy_len);
}

void mspi_timing_config_flash_tune_dummy(uint8_t extra_dummy)
{
mspi_timing_config_flash_set_extra_dummy(1, extra_dummy);
}

void mspi_timing_config_psram_tune_din_num_mode(uint8_t din_mode, uint8_t din_num)
void mspi_timing_config_psram_set_tuning_regs(const mspi_timing_tuning_param_t *params)
{
/**
* 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
*/
mspi_timing_config_psram_set_din_mode_num(0, din_mode, din_num);
}
mspi_timing_config_psram_set_din_mode_num(0, params->spi_din_mode, params->spi_din_num);

void mspi_timing_config_psram_tune_dummy(uint8_t extra_dummy)
{
#if CONFIG_SPIRAM_MODE_OCT
//On 728, for SPI1, flash and psram share the extra dummy register
mspi_timing_config_flash_set_extra_dummy(1, extra_dummy);
mspi_timing_config_flash_set_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 = extra_dummy;
mspi_timing_ll_set_quad_flash_dummy(1, extra_dummy - 1);
s_psram_extra_dummy = params->extra_dummy_len;
mspi_timing_ll_set_quad_flash_dummy(1, s_psram_extra_dummy - 1);
#endif
}

Expand Down
Expand Up @@ -128,6 +128,11 @@
#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-----------------------------------------------//
/**
Expand Down Expand Up @@ -233,3 +238,20 @@ ESP_STATIC_ASSERT(CHECK_POWER_OF_2(MSPI_TIMING_CORE_CLOCK_MHZ / MSPI_TIMING_PSRA
#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 successfull 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.
*/
#define MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN 440
#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_STEP_MHZ_MODULE_CLK_120M 8
@@ -0,0 +1,6 @@
# F8R8, Flash 120M DDR, PSRAM disable

CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
@@ -0,0 +1,10 @@
# F8R8, Flash 120M DDR, PSRAM 120M DDR

CONFIG_ESPTOOLPY_OCT_FLASH=y
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y

CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_120M=y
5 changes: 3 additions & 2 deletions components/esp_hw_support/test_apps/mspi/sdkconfig.defaults
@@ -1,5 +1,6 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n

CONFIG_SPIRAM_RODATA=y
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"

0 comments on commit ebaf987

Please sign in to comment.