Merge branch 'feature/psram_halfsleep_mode_support_v6.0' into 'release/v6.0'

feat(psram): allow PSRAM to enter halfsleep mode during light sleep (v6.0)

See merge request espressif/esp-idf!49759
This commit is contained in:
morris
2026-07-03 11:14:20 +08:00
16 changed files with 376 additions and 29 deletions

View File

@@ -1,3 +1,5 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
# Disable PSRAM halfsleep feature, otherwise P4 tcm section overflows with COMPILER_OPTIMIZATION_NONE=y
CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED=n

View File

@@ -1,3 +1,5 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
# Disable PSRAM halfsleep feature, otherwise P4 tcm section overflows with COMPILER_OPTIMIZATION_NONE=y
CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED=n

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -26,6 +26,7 @@
#include "soc/clk_tree_defs.h"
#include "soc/hp_system_struct.h"
#include "rom/opi_flash.h"
#include "esp_rom_sys.h"
#ifdef __cplusplus
extern "C" {
@@ -40,6 +41,14 @@ extern "C" {
#define PSRAM_CTRLR_LL_FIFO_MAX_BYTES 64
/**
* ESP32-P4 MSPI revision < 3.0 lacks SPIMEM CS force-control (cs_keep_active /
* cs0_dis / cs1_dis) needed to assert CE# and wake PSRAM from half-sleep.
* The device driver must issue a dummy MSPI write instead.
* Revision >= 3.0 can use psram_ctrlr_ll_half_sleep_wakeup().
*/
#define PSRAM_CTRLR_LL_MSPI_WAKEUP_WORKAROUND (HAL_CONFIG(CHIP_SUPPORT_MIN_REV) < 300)
/**
* @brief Set PSRAM write cmd
*
@@ -243,6 +252,20 @@ static inline void psram_ctrlr_ll_enable_hex_data_line_mode(uint32_t mspi_id, bo
SPIMEM2.mem_sram_cmd.mem_sdout_hex = en;
}
/**
* @brief Get PSRAM hex data line mode enable status
*
* @param mspi_id mspi_id
*
* @return true if hex data line mode is enabled, false otherwise
*/
__attribute__((always_inline))
static inline bool psram_ctrlr_ll_is_hex_data_line_mode(uint32_t mspi_id)
{
(void)mspi_id;
return (SPIMEM2.mem_sram_cmd.mem_sdin_hex & SPIMEM2.mem_sram_cmd.mem_sdout_hex);
}
/**
* @brief Enable PSRAM AXI master access
*
@@ -872,6 +895,29 @@ static inline void psram_ctrlr_ll_enable_core_err_resp(void)
HP_SYSTEM.core_err_resp_dis.val = 0x0;
}
#if !PSRAM_CTRLR_LL_MSPI_WAKEUP_WORKAROUND
/**
* @brief Wake PSRAM from half-sleep via MSPI CS controls (P4 MSPI rev >= 3.0).
*
* MSPI2: mem_cs_oe_ctrl drives CS. MSPI3: cs_keep_active + cs0/cs1_dis for CE#.
* Restore MSPI2/MSPI3 cs settings when done.
*/
__attribute__((always_inline))
static inline void psram_ctrlr_ll_half_sleep_wakeup(void)
{
bool old_oe_ctrl = SPIMEM2.mem_misc.mem_cs_oe_ctrl;
SPIMEM2.mem_misc.mem_cs_oe_ctrl = 1;
SPIMEM3.misc.cs_keep_active = 1;
SPIMEM3.misc.cs0_dis = 1;
SPIMEM3.misc.cs1_dis = 0;
esp_rom_delay_us(3);
SPIMEM3.misc.cs1_dis = 1;
SPIMEM3.misc.cs0_dis = 0;
SPIMEM3.misc.cs_keep_active = 0;
SPIMEM2.mem_misc.mem_cs_oe_ctrl = old_oe_ctrl;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -39,6 +39,7 @@
#if CONFIG_SPIRAM
#include "hal/ldo_ll.h"
#include "hal/mspi_ll.h"
#include "esp_private/esp_psram_impl.h"
#endif
#if (CONFIG_ESP_REV_MIN_FULL == 300)
@@ -191,7 +192,7 @@ const pmu_sleep_config_t* pmu_sleep_config_default(
// Get light sleep analog default
pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT(sleep_flags);
#if CONFIG_SPIRAM
#if CONFIG_SPIRAM && CONFIG_ESP32P4_SELECTS_REV_LESS_V3
// Adjust analog parameters to keep EXT_LDO PSRAM channel volt outputting during light-sleep.
analog_default.hp_sys.analog.pd_cur = PMU_PD_CUR_SLEEP_ON;
analog_default.lp_sys[PMU_MODE_LP_SLEEP].analog.pd_cur = PMU_PD_CUR_SLEEP_ON;
@@ -403,11 +404,15 @@ SPM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt,
// to be written back so that regdma can get the correct link.
// 3. We cannot use the API provided by ROM to invalidate the cache, since it is a function calling that writes data to the stack during
// the return process, which results in dirty cachelines in L1 Cache again.
pmu_sleep_cache_sync_items(SMMU_GID_DEFAULT, CACHE_SYNC_WRITEBACK, CACHE_MAP_L1_DCACHE, 0, 0);
pmu_sleep_cache_sync_items(SMMU_GID_DEFAULT, CACHE_SYNC_WRITEBACK, CACHE_MAP_L1_DCACHE, 0, 0); // No PSRAM dirty data after this time write back
if (!dslp) {
#if CONFIG_SPIRAM
psram_ctrlr_ll_wait_all_transaction_done();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
esp_psram_impl_enter_halfsleep_mode();
#endif
mspi_ll_hold_all_psram_pins();
#endif
s_mpll_freq_mhz_before_sleep = rtc_clk_mpll_get_freq();
if (s_mpll_freq_mhz_before_sleep) {
@@ -421,10 +426,10 @@ SPM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt,
_psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, false);
_psram_ctrlr_ll_enable_module_clock(PSRAM_CTRLR_LL_MSPI_ID_2, false);
}
mspi_ll_hold_all_psram_pins();
#endif
rtc_clk_mpll_disable();
}
pmu_sleep_cache_sync_items(SMMU_GID_DEFAULT, CACHE_SYNC_WRITEBACK, CACHE_MAP_L1_DCACHE, 0, 0); // No L2 MEM dirty data after this time write back
} else {
#if CONFIG_P4_REV3_MSPI_CRASH_AFTER_POWER_UP_WORKAROUND
if (efuse_hal_chip_revision() == 300) {
@@ -488,19 +493,26 @@ SPM_IRAM_ATTR bool pmu_sleep_finish(bool dslp)
// Wait eFuse memory update done.
while(efuse_ll_get_controller_state() != EFUSE_CONTROLLER_STATE_IDLE);
if (s_mpll_freq_mhz_before_sleep && !dslp) {
rtc_clk_mpll_enable();
rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep, true);
if (!dslp) {
if (s_mpll_freq_mhz_before_sleep) {
rtc_clk_mpll_enable();
rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep, true);
#if CONFIG_SPIRAM
if (!s_pmu_sleep_regdma_backup_enabled) {
// MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here.
// If it's a PD_TOP sleep, psram MSPI core clock will be enabled by REGDMA
_psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
_psram_ctrlr_ll_enable_module_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
if (!s_pmu_sleep_regdma_backup_enabled) {
// MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here.
// If it's a PD_TOP sleep, psram MSPI core clock will be enabled by REGDMA
_psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
_psram_ctrlr_ll_enable_module_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
}
_psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_2, PSRAM_CLK_SRC_MPLL);
_psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_3, PSRAM_CLK_SRC_MPLL);
#endif
}
_psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_2, PSRAM_CLK_SRC_MPLL);
_psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_3, PSRAM_CLK_SRC_MPLL);
#if CONFIG_SPIRAM
mspi_ll_unhold_all_psram_pins();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
esp_psram_impl_exit_halfsleep_mode();
#endif
#endif
}

View File

@@ -15,6 +15,7 @@
#include "soc/timer_group_reg.h"
#include "esp_rom_sys.h"
#include "esp_private/periph_ctrl.h"
#include "esp_attr.h"
ESP_LOG_ATTR_TAG(TAG, "rtc_time");
@@ -211,7 +212,7 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period)
return (rtc_cycles * period) >> RTC_CLK_CAL_FRACT;
}
uint64_t rtc_time_get(void)
SPM_IRAM_ATTR uint64_t rtc_time_get(void)
{
return rtc_timer_hal_get_cycle_count(0);
}

View File

@@ -167,6 +167,10 @@
#include "esp_private/sleep_retention.h"
#endif
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
#include "esp_private/esp_psram_impl.h"
#endif
// If light sleep time is less than that, don't power down flash
#define FLASH_PD_MIN_SLEEP_TIME_US 2000
@@ -926,6 +930,13 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
#endif
#endif // !SOC_MSPI_HAS_INDEPENT_IOMUX
}
#endif
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && (CONFIG_SPIRAM_XIP_FROM_PSRAM || !CONFIG_PM_SLP_IRAM_OPT)
// Code outside of esp_sleep_start_safe may be linked to FLASH if CONFIG_PM_SLP_IRAM_OPT is false,
// Code that accesses flash memory may cause cached PSRAM data to be replaced back into PSRAM before
// it has fully resumed. And if CONFIG_SPIRAM_XIP_FROM_PSRAM is enabled, code in Flash will be copied
// to PSRAM for execution. We need to wait here until PSRAM exits half-sleep before returning.
esp_psram_impl_resume_from_halfsleep_mode(s_config.rtc_clk_cal_period);
#endif
/* Cache Resume 1: Resume cache for continue running*/
resume_cache();
@@ -1580,6 +1591,14 @@ esp_err_t esp_light_sleep_start(void)
esp_clk_private_unlock();
esp_timer_private_unlock();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && !CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_PM_SLP_IRAM_OPT
// If CONFIG_SPIRAM_XIP_FROM_PSRAM is not enabled and CONFIG_PM_SLP_IRAM_OPT is enable,
// the sleep-wake process prior to this point does not access the PSRAM, so we can postpone waiting
// for the PSRAM to resume until here, in order to reuse the time overhead
// of the wake-up process as much as possible.
esp_psram_impl_resume_from_halfsleep_mode(s_config.rtc_clk_cal_period);
#endif
#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT
/* Cache Resume 0: sleep process done, resume cache for continue running */
if (!(s_config.wakeup_triggers & ignore_check_wakeup_triggers)) {

View File

@@ -287,4 +287,28 @@ menu "Power Management"
Only enabled when SPI_FLASH_FREQ_LIMIT_C5_240MHZ is enabled.
This is an internal configuration, automatically set based on SPI Flash configuration.
config PM_SLP_SPIRAM_HALFSLEEP_ENABLED
bool "Enable halfsleep mode for PSRAM in light sleep"
depends on SPIRAM
default y
help
Enable PSRAM halfsleep mode feature to reduce power consumption during light sleep, if the PSRAM used
support halfsleep mode.
When enabled, the PSRAM chip will be configured to enter halfsleep mode (if applicable) before entering
light sleep. This reduces power consumption by:
1. Setting refresh rate to 0.5x
2. Setting PSRAM to halfsleep mode
The PSRAM will automatically exit halfsleep mode after waking up from light sleep.
config PM_SLP_SPIRAM_HALFSLEEP_EXIT_WAIT_DELAY
int "The time to wait for PSRAM to be able to access after exiting halfsleep mode (in us)"
depends on SPIRAM
default 150
range 50 250
help
Configure the delay time between waking up PSRAM halfsleep mode and the first access to PSRAM.
Refer to the tXHS parameter in datasheet of the used PSRAM to configure this option.
endmenu # "Power Management"

View File

@@ -4,7 +4,7 @@ if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
set(includes "include")
set(includes "include" "device/include")
if(CONFIG_SOC_SPIRAM_XIP_SUPPORTED)
list(APPEND includes xip_impl/include)
@@ -37,7 +37,6 @@ endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${includes}
PRIV_INCLUDE_DIRS device/include
PRIV_REQUIRES ${priv_requires}
LDFRAGMENTS linker.lf)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -64,6 +64,8 @@
#define AP_HEX_PSRAM_REF_DATA 0x5a6b7c8d
#define AP_HEX_PSRAM_ULP_MODE_HALFSLEEP 0xF0
typedef struct {
union {
struct {
@@ -78,7 +80,7 @@ typedef struct {
union {
struct {
uint8_t vendor_id: 5;
uint8_t rsvd0_2: 2;
uint8_t rsvd5_7: 3;
uint8_t ulp: 1;
};
uint8_t val;
@@ -93,9 +95,9 @@ typedef struct {
} mr2;
union {
struct {
uint8_t rsvd3_7: 4;
uint8_t rsvd0_3: 4;
uint8_t srf: 2;
uint8_t rsvd0: 1;
uint8_t rsvd6: 1;
uint8_t rbx_en: 1;
};
uint8_t val;
@@ -108,6 +110,13 @@ typedef struct {
};
uint8_t val;
} mr4;
union {
struct {
uint8_t rsvd0_3: 4;
uint8_t halfsleep: 4;
};
uint8_t val;
} mr6;
union {
struct {
uint8_t bl: 2;
@@ -510,3 +519,87 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes)
#endif
return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE);
}
/******************************* Halfsleep Mode *******************************/
static PSRAM_HALFSLEEP_DATA_ATTR struct {
uint8_t mr4;
bool is_hex_line_mode;
uint64_t halfsleep_wakeup_tick;
} s_halfsleep_ctx = {0};
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void)
{
// Backup line mode configuration and set to 8-line mode (MR registers R/W not support in hex line mode)
s_halfsleep_ctx.is_hex_line_mode = psram_ctrlr_ll_is_hex_data_line_mode(PSRAM_CTRLR_LL_MSPI_ID_3);
psram_ctrlr_ll_enable_hex_data_line_mode(PSRAM_CTRLR_LL_MSPI_ID_3, false);
// Backup MR4
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_REG_READ, AP_HEX_PSRAM_RD_CMD_BITLEN,
0x4, AP_HEX_PSRAM_ADDR_BITLEN,
AP_HEX_PSRAM_RD_REG_DUMMY_BITLEN,
NULL, 0,
&s_halfsleep_ctx.mr4, 16,
false);
// Set refresh rate to 0.5x
hex_psram_mode_reg_t mode_reg;
mode_reg.mr4.val = s_halfsleep_ctx.mr4;
mode_reg.mr4.rf = 3; // (0:4x 1:1x 3:0.5x)
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_RD_CMD_BITLEN,
0x4, AP_HEX_PSRAM_ADDR_BITLEN,
0,
&mode_reg.mr4.val, 16,
NULL, 0,
false);
// Set halfsleep mode
uint8_t halfsleep_cmd = AP_HEX_PSRAM_ULP_MODE_HALFSLEEP;
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_RD_CMD_BITLEN,
0x6, AP_HEX_PSRAM_ADDR_BITLEN,
0,
&halfsleep_cmd, 16,
NULL, 0,
false);
}
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void)
{
// Record the tick exiting halfsleep mode
s_halfsleep_ctx.halfsleep_wakeup_tick = rtc_time_get();
#if PSRAM_CTRLR_LL_MSPI_WAKEUP_WORKAROUND
uint8_t null = 0;
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_WR_CMD_BITLEN,
0xFF, AP_HEX_PSRAM_ADDR_BITLEN,
0,
&null, 0,
NULL, 0,
false);
#else
// Set CE# to active to wakeup PSRAM halfsleep
psram_ctrlr_ll_half_sleep_wakeup();
#endif
}
PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period)
{
uint64_t halfsleep_exit_tick = s_halfsleep_ctx.halfsleep_wakeup_tick + rtc_time_us_to_slowclk(CONFIG_PM_SLP_SPIRAM_HALFSLEEP_EXIT_WAIT_DELAY, slowclk_period);
while (rtc_time_get() < halfsleep_exit_tick) {
// Busy wait for PSRAM to exit halfsleep mode
}
// Restore MR4 configuration (restore refresh rate)
psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3,
AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_WR_CMD_BITLEN,
0x4, AP_HEX_PSRAM_ADDR_BITLEN,
0,
&s_halfsleep_ctx.mr4, 16,
NULL, 0,
false);
// Restore hex line mode if it was enabled before
if (s_halfsleep_ctx.is_hex_line_mode) {
psram_ctrlr_ll_enable_hex_data_line_mode(PSRAM_CTRLR_LL_MSPI_ID_3, true);
}
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -434,6 +434,20 @@ static void config_psram_spi_phases(void)
psram_ctrlr_ll_set_cs_pin(PSRAM_CTRLR_LL_MSPI_ID_0, PSRAM_LL_CS_ID_1);
}
/******************************* Halfsleep Mode *******************************/
// This PSRAM device does not support halfsleep mode
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period)
{
}
/*---------------------------------------------------------------------------------
* Following APIs are not required to be IRAM-Safe
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,6 +8,7 @@
#include "esp_err.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
@@ -20,6 +21,21 @@ extern "C" {
#define PSRAM_SIZE_32MB (32 * 1024 * 1024)
#define PSRAM_SIZE_64MB (64 * 1024 * 1024)
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#define PSRAM_HALFSLEEP_DATA_ATTR SPM_DRAM_ATTR
#define PSRAM_HALFSLEEP_SLEEP_CODE_ATTR SPM_IRAM_ATTR
#else
#define PSRAM_HALFSLEEP_DATA_ATTR DRAM_ATTR
#define PSRAM_HALFSLEEP_SLEEP_CODE_ATTR IRAM_ATTR
#endif
#define PSRAM_HALFSLEEP_RESUME_CODE_ATTR IRAM_ATTR
#else
#define PSRAM_HALFSLEEP_DATA_ATTR
#define PSRAM_HALFSLEEP_SLEEP_CODE_ATTR
#define PSRAM_HALFSLEEP_RESUME_CODE_ATTR
#endif
/**
* @brief To get the physical psram size in bytes.
*
@@ -51,6 +67,29 @@ esp_err_t esp_psram_impl_enable(void);
*/
uint8_t esp_psram_impl_get_cs_io(void);
/**
* @brief Set psram to halfsleep mode to save power consumption
*
* It will also set refresh rate to 0.5x.
*/
void esp_psram_impl_enter_halfsleep_mode(void);
/**
* @brief Make psram exit halfsleep mode (no wait, wait is done in esp_psram_impl_resume_from_halfsleep_mode)
*/
void esp_psram_impl_exit_halfsleep_mode(void);
/**
* @brief Resume psram from halfsleep mode (also restore refresh rate)
*
* @param slowclk_period RTC slow clock period, calibration value obtained using rtc_clk_cal
*
* @note PSRAM requires some time (tHSPU) to fully recover from halfsleep mode.
* This function examines the time between exiting halfsleep mode and current time, and busy wait if the time is not long enough.
* This shortens the overall busy loop time if wait is done inside esp_psram_impl_exit_halfsleep_mode.
*/
void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2013-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1175,4 +1175,19 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes)
{
return esp_psram_impl_get_physical_size(out_size_bytes);
}
/******************************* Halfsleep Mode *******************************/
// This PSRAM device does not support halfsleep mode
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period)
{
}
#endif // CONFIG_SPIRAM

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2013-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -577,3 +577,17 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes)
{
return esp_psram_impl_get_physical_size(out_size_bytes);
}
/******************************* Halfsleep Mode *******************************/
// This PSRAM device does not support halfsleep mode
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period)
{
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -456,3 +456,17 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes)
#endif
return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE);
}
/******************************* Halfsleep Mode *******************************/
// This PSRAM supports halfsleep mode, but not implemented yet
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void)
{
}
PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period)
{
}

View File

@@ -13,26 +13,30 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_cpu.h"
#include "esp_heap_caps.h"
#include "esp_ipc_isr.h"
#include "esp_private/esp_psram_io.h"
#include "esp_psram.h"
#include "esp_private/esp_psram_extram.h"
#include "esp_private/esp_psram_impl.h"
#include "esp_flash.h"
#include "esp_partition.h"
#include "soc/rtc.h"
__attribute__((unused)) const static char *TAG = "PSRAM";
static void s_test_psram_heap_allocable(void)
{
size_t largest_size = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
ESP_LOGI(TAG, "largest size is %zu", largest_size);
ESP_EARLY_LOGI(TAG, "largest size is 0x%08x", largest_size);
uint32_t *ext_buffer = (uint32_t *)heap_caps_calloc(largest_size, 1, MALLOC_CAP_SPIRAM);
TEST_ASSERT(ext_buffer);
intptr_t start = (intptr_t)ext_buffer;
intptr_t end = (intptr_t)ext_buffer + largest_size;
ESP_LOGI(TAG, "test ext buffer start addr is 0x%"PRIxPTR", end addr is 0x%"PRIxPTR, start, end);
ESP_EARLY_LOGI(TAG, "test ext buffer start addr is %p, end addr is %p", start, end);
TEST_ASSERT(esp_psram_check_ptr_addr((void *)start) && esp_psram_check_ptr_addr((void *)end));
for (int i = 0; i < largest_size / sizeof(uint32_t); i++) {
@@ -58,6 +62,37 @@ TEST_CASE("stress test psram heap allocable", "[psram][manual][ignore]")
}
}
#if !CONFIG_SPIRAM_XIP_FROM_PSRAM
IRAM_ATTR static void s_psram_halfsleep_test(uint32_t rtc_slow_clk_period)
{
// Stall another core and disable branch predictor to prevent it from accessing PSRAM
esp_ipc_isr_stall_other_cpu();
#if SOC_BRANCH_PREDICTOR_SUPPORTED
esp_cpu_branch_prediction_disable();
#endif
esp_psram_impl_enter_halfsleep_mode();
esp_rom_delay_us(1000);
esp_psram_impl_exit_halfsleep_mode();
esp_psram_impl_resume_from_halfsleep_mode(rtc_slow_clk_period);
#if SOC_BRANCH_PREDICTOR_SUPPORTED
esp_cpu_branch_prediction_enable();
#endif
esp_ipc_isr_release_other_cpu();
s_test_psram_heap_allocable();
}
TEST_CASE("test psram halfsleep mode (if applicable)", "[psram]")
{
uint32_t rtc_slow_clk_period = rtc_clk_cal(CLK_CAL_RTC_SLOW, CONFIG_RTC_CLK_CAL_CYCLES);
s_test_psram_heap_allocable();
for (int i = 0; i < 3; i++) {
s_psram_halfsleep_test(rtc_slow_clk_period);
}
}
#endif
#if CONFIG_SPIRAM_XIP_FROM_PSRAM
/*---------------------------------------------------------------
SPI1 with XIP

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -320,6 +320,24 @@ static void test_psram_accessible_after_lightsleep(void)
TEST_ESP_OK(sleep_cpu_configure(true));
#endif
// Verify PSRAM was not corrupted after light sleep
uint8_t *psram_data = (uint8_t *)heap_caps_malloc(1024 * 1024, MALLOC_CAP_SPIRAM);
uint8_t test_pattern = 0x5a;
for (int i = 0; i < 5; i++) {
memset(psram_data, test_pattern, 1024 * 1024);
esp_sleep_enable_timer_wakeup(100 * 1000);
esp_light_sleep_start();
for (int j = 0; j < (1024 * 1024); j++) {
TEST_ASSERT_EQUAL(test_pattern, psram_data[j]);
}
test_pattern = ~test_pattern;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
#endif
}
free(psram_data);
// Verify that all addresses in PSRAM are accessible after light sleep
esp_sleep_enable_timer_wakeup(100 * 1000);
esp_light_sleep_start();
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);