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

This commit is contained in:
Song Ruo Jing
2026-06-18 16:05:49 +08:00
parent 1ea37f2961
commit 6079c74f99
15 changed files with 291 additions and 26 deletions
+1 -2
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)
@@ -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
*/
@@ -14,6 +14,7 @@
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#include "clk_ctrl_os.h"
#include "soc/rtc.h"
// Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section
#define PSRAM_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
@@ -65,6 +66,8 @@
#define AP_HEX_PSRAM_REF_DATA 0x5a6b7c8d
#define AP_HEX_PSRAM_ULP_MODE_HALFSLEEP 0xF0
typedef struct {
union {
struct {
@@ -79,7 +82,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;
@@ -94,9 +97,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;
@@ -109,6 +112,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 +520,83 @@ 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();
// Do a SPI dummy write transmission to invalid address to wake up from halfsleep mode
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);
}
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);
}
}
@@ -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
*
@@ -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
@@ -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
*/
@@ -1174,4 +1174,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
@@ -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)
{
}
@@ -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
*/
@@ -455,3 +455,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)
{
}
@@ -17,8 +17,10 @@
#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";
@@ -58,6 +60,21 @@ TEST_CASE("stress test psram heap allocable", "[psram][manual][ignore]")
}
}
#if !CONFIG_SPIRAM_XIP_FROM_PSRAM
TEST_CASE("test psram halfsleep mode (if applicable)", "[psram]")
{
uint32_t rtc_slow_clk_period = rtc_clk_cal(RTC_CAL_RTC_MUX, CONFIG_RTC_CLK_CAL_CYCLES);
s_test_psram_heap_allocable();
for (int i = 0; i < 5; i++) {
esp_psram_impl_enter_halfsleep_mode();
vTaskDelay(pdMS_TO_TICKS(1000));
esp_psram_impl_exit_halfsleep_mode();
esp_psram_impl_resume_from_halfsleep_mode(rtc_slow_clk_period);
s_test_psram_heap_allocable();
}
}
#endif
#if CONFIG_SPIRAM_XIP_FROM_PSRAM
/*---------------------------------------------------------------
SPI1 with XIP