feat(spiram): Add temperature support for psram adjustment on esp32c5

This commit is contained in:
C.S.M 2024-07-24 14:11:08 +08:00
parent 5c3964c9ea
commit 931f95068a
6 changed files with 298 additions and 6 deletions

View File

@ -27,6 +27,13 @@
#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
@ -411,6 +418,12 @@ static bool get_working_pll_freq(const uint8_t *reference_data, bool is_flash, u
}
#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)
@ -441,10 +454,11 @@ static uint32_t s_select_best_tuning_config_dtr(const mspi_timing_config_t *conf
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;
#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);
@ -465,6 +479,48 @@ static uint32_t s_select_best_tuning_config_dtr(const mspi_timing_config_t *conf
} else {
ESP_EARLY_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_EARLY_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_EARLY_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_EARLY_LOGD(TAG, "freq scan success, best point is index %" PRIu32, best_point);
}
#endif
return best_point;
@ -630,3 +686,201 @@ uint8_t mspi_timing_config_get_flash_extra_dummy(void)
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_EARLY_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_EARLY_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_EARLY_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_EARLY_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_EARLY_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_EARLY_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_EARLY_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

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -248,6 +248,13 @@ uint8_t mspi_timing_config_get_flash_extra_dummy(void);
#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

View File

@ -287,6 +287,9 @@ static void s_select_best_tuning_config(mspi_timing_config_t *config, uint32_t c
} 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

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,7 +15,7 @@
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR ESP_BOOTLOADER_OFFSET
/**
* @note BACKGOURND:
* @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:
*
@ -24,7 +24,7 @@
* RTC8M ----| |---- PSRAM Module Clock
*
*
* DDR stands for double data rate, MSPI samples at both posedge and negedge. So the real spped will be doubled.
* 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
@ -249,11 +249,16 @@ ESP_STATIC_ASSERT(CHECK_POWER_OF_2(MSPI_TIMING_CORE_CLOCK_MHZ / MSPI_TIMING_PSRA
//------------------------------------------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,
* 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

View File

@ -117,6 +117,25 @@ menu "SPI RAM config"
bool "40Mhz clock speed"
endchoice
config SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
depends on SPIRAM_SPEED_120M && SPIRAM_MODE_OCT && IDF_EXPERIMENTAL_FEATURES
bool "Adjust PSRAM timing tuning point via on-chip temperature sensor in real-time"
default n
help
Temperature would influence the source clock frequency so that the timing tuning point set in start-up
might not be always proper when temperature varies in high range. This configuration would help checking
the temperature in real-time, and adjust timing point automatically when temperature change.
(see External RAM documentation for more details)
config SPIRAM_TIMING_MEASURE_TEMPERATURE_INTERVAL_SECOND
depends on SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
int "PSRAM timing tuning measure temperature interval (unit: second)"
default 5
help
Due to adjusting timing point automatically when temperature change, the task will measure current
temperature value in some specific interval. This configuration option would help you to set the
proper interval. The interval unit is second.
config SPIRAM_SPEED
int
default 120 if SPIRAM_SPEED_120M

View File

@ -105,6 +105,10 @@ SECONDARY: 220: esp_usb_console_init_restart_timer in components/esp_system/port
# This makes more sense to be done after esp_pm_impl_init (called from init_pm).
SECONDARY: 230: usb_serial_jtag_conn_status_init in components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_connection_monitor.c on BIT(0)
# psram adjust timing point need a separate task which should be created at startup.
# Valid only `CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR` is enabled.
SECONDARY: 240: psram_adjust_timing_point_via_temperature in components/esp_hw_support/mspi_timing_by_mspi_delay.c on BIT(0)
# Has to be the last step!
# Now that the application is about to start, disable boot watchdog
SECONDARY: 999: init_disable_rtc_wdt in components/esp_system/startup_funcs.c on BIT(0)