feat(esp_hw_support): support esp32s31 CPU/FPU retention

This commit is contained in:
wuzhenghui
2026-04-14 11:43:29 +08:00
parent 68bd919fda
commit a07e2c8d85
9 changed files with 1447 additions and 1 deletions

View File

@@ -0,0 +1,225 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __RVSLEEP_FRAMES_H__
#define __RVSLEEP_FRAMES_H__
#include "sdkconfig.h"
/* Align a value up to nearest n-byte boundary, where n is a power of 2. */
#define ALIGNUP(n, val) (((val) + (n) - 1) & -(n))
#ifdef STRUCT_BEGIN
#undef STRUCT_BEGIN
#undef STRUCT_FIELD
#undef STRUCT_AFIELD
#undef STRUCT_END
#endif
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#ifdef __clang__
#define STRUCT_BEGIN .set RV_STRUCT_OFFSET, 0
#define STRUCT_FIELD(ctype,size,asname,name) .set asname, RV_STRUCT_OFFSET; .set RV_STRUCT_OFFSET, asname + size
#define STRUCT_AFIELD(ctype,size,asname,name,n) .set asname, RV_STRUCT_OFFSET;\
.set RV_STRUCT_OFFSET, asname + (size)*(n);
#define STRUCT_END(sname) .set sname##Size, RV_STRUCT_OFFSET;
#else // __clang__
#define STRUCT_BEGIN .pushsection .text; .struct 0
#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size
#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n)
#define STRUCT_END(sname) sname##Size:; .popsection
#endif // __clang__
#else
#define STRUCT_BEGIN typedef struct {
#define STRUCT_FIELD(ctype,size,asname,name) ctype name;
#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n];
#define STRUCT_END(sname) } sname;
#endif
/*
* -------------------------------------------------------------------------------
* RISC-V CORE CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP
* -------------------------------------------------------------------------------
*/
STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEPC, mepc) /* Machine Exception Program Counter */
STRUCT_FIELD (long, 4, RV_SLP_CTX_RA, ra) /* Return address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_SP, sp) /* Stack pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_GP, gp) /* Global pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_TP, tp) /* Thread pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T0, t0) /* Temporary/alternate link register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T1, t1) /* t1-2: Temporaries */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T2, t2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S0, s0) /* Saved register/frame pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_S1, s1) /* Saved register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A0, a0) /* a0-1: Function arguments/return address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A1, a1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A2, a2) /* a2-7: Function arguments */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A3, a3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A4, a4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A5, a5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A6, a6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A7, a7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S2, s2) /* s2-11: Saved registers */
STRUCT_FIELD (long, 4, RV_SLP_CTX_S3, s3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S4, s4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S5, s5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S6, s6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S7, s7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S8, s8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S9, s9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S10, s10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S11, s11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T3, t3) /* t3-6: Temporaries */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T4, t4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T5, t5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T6, t6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSTATUS, mstatus) /* Machine Status */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVEC, mtvec) /* Machine Trap-Vector Base Address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCAUSE, mcause) /* Machine Trap Cause */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVAL, mtval) /* Machine Trap Value */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIE, mie) /* Machine intr enable */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIP, mip) /* Machine intr pending */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MINTTHRESH, mintthresh) /* Machine intr threshold */
/* FPU context */
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT0, fpu_ft0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT1, fpu_ft1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT2, fpu_ft2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT3, fpu_ft3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT4, fpu_ft4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT5, fpu_ft5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT6, fpu_ft6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT7, fpu_ft7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS0, fpu_fs0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS1, fpu_fs1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA0, fpu_fa0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA1, fpu_fa1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA2, fpu_fa2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA3, fpu_fa3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA4, fpu_fa4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA5, fpu_fa5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA6, fpu_fa6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA7, fpu_fa7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS2, fpu_fs2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS3, fpu_fs3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS4, fpu_fs4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS5, fpu_fs5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS6, fpu_fs6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS7, fpu_fs7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS8, fpu_fs8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS9, fpu_fs9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS10, fpu_fs10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS11, fpu_fs11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT8, fpu_ft8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT9, fpu_ft9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT10, fpu_ft10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT11, fpu_ft11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FCSR, fpu_fcsr)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMUFUNC, pmufunc) /* A field is used to identify whether it is going
* to sleep or has just been awakened. We use the
* lowest 2 bits as indication information, 3 means
* being awakened, 1 means going to sleep */
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
STRUCT_FIELD (long, 4, RV_SLP_CSF_CTX_CRC, frame_crc) /* Used to check RvCoreCriticalSleepFrame integrity */
#endif
STRUCT_END(RvCoreCriticalSleepFrame)
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#define RV_SLEEP_CTX_SZ1 RvCoreCriticalSleepFrameSize
#else
#define RV_SLEEP_CTX_SZ1 sizeof(RvCoreCriticalSleepFrame)
#endif
/*
* Sleep stack frame size, after align up to 16 bytes boundary
*/
#define RV_SLEEP_CTX_FRMSZ (ALIGNUP(0x10, RV_SLEEP_CTX_SZ1))
/*
* -------------------------------------------------------------------------------
* RISC-V CORE NON-CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP
* -------------------------------------------------------------------------------
*/
STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCH, mscratch)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MISA, misa)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVT, mtvt) /* Machine mode vector interrupt base address register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSW, mscratchcsw) /* Machine mode multi-mode data backup register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSWL, mscratchcswl) /* Machine mode interrupt data backup register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MXSTATUS, mxstatus) /* Extended status register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MHCR, mhcr) /* Hardware control register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MHINT, mhint) /* Implicit operation register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MRADDR, mraddr) /* Processor reset start address indication register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEXSTATUS, mexstatus) /* Extended exception status register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_JVT, jvt) /* Table Jump base vector and control register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEDELEG, medeleg) /* Table Jump base vector and control register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_TSELECT, tselect)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA1, tdata1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA2, tdata2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TCONTROL, tcontrol)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR0, pmpaddr0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR1, pmpaddr1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR2, pmpaddr2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR3, pmpaddr3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR4, pmpaddr4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR5, pmpaddr5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR6, pmpaddr6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR7, pmpaddr7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR8, pmpaddr8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR9, pmpaddr9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR10, pmpaddr10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR11, pmpaddr11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR12, pmpaddr12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR13, pmpaddr13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR14, pmpaddr14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR15, pmpaddr15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG0, pmpcfg0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG1, pmpcfg1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG2, pmpcfg2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG3, pmpcfg3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR0, pmaaddr0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR1, pmaaddr1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR2, pmaaddr2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR3, pmaaddr3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR4, pmaaddr4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR5, pmaaddr5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR6, pmaaddr6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR7, pmaaddr7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR8, pmaaddr8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR9, pmaaddr9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR10, pmaaddr10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR11, pmaaddr11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR12, pmaaddr12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR13, pmaaddr13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR14, pmaaddr14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR15, pmaaddr15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG0, pmacfg0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG1, pmacfg1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG2, pmacfg2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG3, pmacfg3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG4, pmacfg4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG5, pmacfg5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG6, pmacfg6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG7, pmacfg7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG8, pmacfg8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG9, pmacfg9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG10, pmacfg10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG11, pmacfg11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG12, pmacfg12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG13, pmacfg13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG14, pmacfg14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG15, pmacfg15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLE, mcycle)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLEH, mcycleh)
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
STRUCT_FIELD (long, 4, RV_SLP_NCSF_CTX_CRC, frame_crc) /* Used to check RvCoreNonCriticalSleepFrame integrity */
#endif
STRUCT_END(RvCoreNonCriticalSleepFrame)
#endif /* #ifndef __RVSLEEP_FRAMES_H__ */

View File

@@ -0,0 +1,525 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <inttypes.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_ipc_isr.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_rom_crc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/portmacro.h"
#include "riscv/csr.h"
#include "soc/clic_reg.h"
#include "soc/rtc_periph.h"
#include "soc/soc_caps.h"
#include "soc/hp_sys_clkrst_reg.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/sleep_event.h"
#include "sdkconfig.h"
#include "esp_private/esp_pmu.h"
#include "esp32s31/rom/ets_sys.h"
#include "esp32s31/rom/rtc.h"
#include "rvsleep-frames.h"
#include "sleep_cpu_retention.h"
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
#include "esp_private/system_internal.h"
#include "hal/uart_hal.h"
#endif
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS];
#endif
static bool s_fpu_saved[portNUM_PROCESSORS];
ESP_LOG_ATTR_TAG(TAG, "sleep");
static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention;
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS];
FORCE_INLINE_ATTR uint32_t save_mstatus_and_disable_global_int(void)
{
return RV_READ_MSTATUS_AND_DISABLE_INTR();
}
FORCE_INLINE_ATTR void restore_mstatus(uint32_t mstatus_val)
{
RV_WRITE_CSR(mstatus, mstatus_val);
}
#define CUSTOM_CSR_MTVT (0x307)
#define CUSTOM_CSR_MSCRATCHCSW (0x348)
#define CUSTOM_CSR_MSCRATCHCSWL (0x349)
#define CUSTOM_CSR_MXSTATUS (0x7c0)
#define CUSTOM_CSR_MHCR (0x7c1)
#define CUSTOM_CSR_MHINT (0x7c5)
#define CUSTOM_CSR_MRADDR (0x7e0)
#define CUSTOM_CSR_MEXSTATUS (0x7e1)
#define CUSTOM_CSR_JVT (0x017)
#define CUSTOM_CSR_UINTTHRESH (0x047)
#define CUSTOM_CSR_UINTSTATUS (0xCB1)
static IRAM_ATTR RvCoreNonCriticalSleepFrame * rv_core_noncritical_regs_save(void)
{
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()];
frame->mscratch = RV_READ_CSR(mscratch);
frame->misa = RV_READ_CSR(misa);
frame->mtvt = RV_READ_CSR(CUSTOM_CSR_MTVT);
frame->mscratchcsw = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSW);
frame->mscratchcswl = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSWL);
frame->mxstatus = RV_READ_CSR(CUSTOM_CSR_MXSTATUS);
frame->mhcr = RV_READ_CSR(CUSTOM_CSR_MHCR);
frame->mhint = RV_READ_CSR(CUSTOM_CSR_MHINT);
frame->mraddr = RV_READ_CSR(CUSTOM_CSR_MRADDR);
frame->mexstatus = RV_READ_CSR(CUSTOM_CSR_MEXSTATUS);
frame->jvt = RV_READ_CSR(CUSTOM_CSR_JVT);
frame->medeleg = RV_READ_CSR(medeleg);
frame->tselect = RV_READ_CSR(tselect);
frame->tdata1 = RV_READ_CSR(tdata1);
frame->tdata2 = RV_READ_CSR(tdata2);
frame->tcontrol = RV_READ_CSR(tcontrol);
frame->pmpaddr0 = RV_READ_CSR(pmpaddr0);
frame->pmpaddr1 = RV_READ_CSR(pmpaddr1);
frame->pmpaddr2 = RV_READ_CSR(pmpaddr2);
frame->pmpaddr3 = RV_READ_CSR(pmpaddr3);
frame->pmpaddr4 = RV_READ_CSR(pmpaddr4);
frame->pmpaddr5 = RV_READ_CSR(pmpaddr5);
frame->pmpaddr6 = RV_READ_CSR(pmpaddr6);
frame->pmpaddr7 = RV_READ_CSR(pmpaddr7);
frame->pmpaddr8 = RV_READ_CSR(pmpaddr8);
frame->pmpaddr9 = RV_READ_CSR(pmpaddr9);
frame->pmpaddr10 = RV_READ_CSR(pmpaddr10);
frame->pmpaddr11 = RV_READ_CSR(pmpaddr11);
frame->pmpaddr12 = RV_READ_CSR(pmpaddr12);
frame->pmpaddr13 = RV_READ_CSR(pmpaddr13);
frame->pmpaddr14 = RV_READ_CSR(pmpaddr14);
frame->pmpaddr15 = RV_READ_CSR(pmpaddr15);
frame->pmpcfg0 = RV_READ_CSR(pmpcfg0);
frame->pmpcfg1 = RV_READ_CSR(pmpcfg1);
frame->pmpcfg2 = RV_READ_CSR(pmpcfg2);
frame->pmpcfg3 = RV_READ_CSR(pmpcfg3);
frame->pmaaddr0 = RV_READ_CSR(CSR_PMAADDR(0));
frame->pmaaddr1 = RV_READ_CSR(CSR_PMAADDR(1));
frame->pmaaddr2 = RV_READ_CSR(CSR_PMAADDR(2));
frame->pmaaddr3 = RV_READ_CSR(CSR_PMAADDR(3));
frame->pmaaddr4 = RV_READ_CSR(CSR_PMAADDR(4));
frame->pmaaddr5 = RV_READ_CSR(CSR_PMAADDR(5));
frame->pmaaddr6 = RV_READ_CSR(CSR_PMAADDR(6));
frame->pmaaddr7 = RV_READ_CSR(CSR_PMAADDR(7));
frame->pmaaddr8 = RV_READ_CSR(CSR_PMAADDR(8));
frame->pmaaddr9 = RV_READ_CSR(CSR_PMAADDR(9));
frame->pmaaddr10 = RV_READ_CSR(CSR_PMAADDR(10));
frame->pmaaddr11 = RV_READ_CSR(CSR_PMAADDR(11));
frame->pmaaddr12 = RV_READ_CSR(CSR_PMAADDR(12));
frame->pmaaddr13 = RV_READ_CSR(CSR_PMAADDR(13));
frame->pmaaddr14 = RV_READ_CSR(CSR_PMAADDR(14));
frame->pmaaddr15 = RV_READ_CSR(CSR_PMAADDR(15));
frame->pmacfg0 = RV_READ_CSR(CSR_PMACFG(0));
frame->pmacfg1 = RV_READ_CSR(CSR_PMACFG(1));
frame->pmacfg2 = RV_READ_CSR(CSR_PMACFG(2));
frame->pmacfg3 = RV_READ_CSR(CSR_PMACFG(3));
frame->pmacfg4 = RV_READ_CSR(CSR_PMACFG(4));
frame->pmacfg5 = RV_READ_CSR(CSR_PMACFG(5));
frame->pmacfg6 = RV_READ_CSR(CSR_PMACFG(6));
frame->pmacfg7 = RV_READ_CSR(CSR_PMACFG(7));
frame->pmacfg8 = RV_READ_CSR(CSR_PMACFG(8));
frame->pmacfg9 = RV_READ_CSR(CSR_PMACFG(9));
frame->pmacfg10 = RV_READ_CSR(CSR_PMACFG(10));
frame->pmacfg11 = RV_READ_CSR(CSR_PMACFG(11));
frame->pmacfg12 = RV_READ_CSR(CSR_PMACFG(12));
frame->pmacfg13 = RV_READ_CSR(CSR_PMACFG(13));
frame->pmacfg14 = RV_READ_CSR(CSR_PMACFG(14));
frame->pmacfg15 = RV_READ_CSR(CSR_PMACFG(15));
frame->mcycle = RV_READ_CSR(mcycle);
frame->mcycleh = RV_READ_CSR(mcycleh);
return frame;
}
static IRAM_ATTR void rv_core_noncritical_regs_restore(void)
{
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()];
RV_WRITE_CSR(CSR_PMAADDR(0), frame->pmaaddr0);
RV_WRITE_CSR(CSR_PMAADDR(1), frame->pmaaddr1);
RV_WRITE_CSR(CSR_PMAADDR(2), frame->pmaaddr2);
RV_WRITE_CSR(CSR_PMAADDR(3), frame->pmaaddr3);
RV_WRITE_CSR(CSR_PMAADDR(4), frame->pmaaddr4);
RV_WRITE_CSR(CSR_PMAADDR(5), frame->pmaaddr5);
RV_WRITE_CSR(CSR_PMAADDR(6), frame->pmaaddr6);
RV_WRITE_CSR(CSR_PMAADDR(7), frame->pmaaddr7);
RV_WRITE_CSR(CSR_PMAADDR(8), frame->pmaaddr8);
RV_WRITE_CSR(CSR_PMAADDR(9), frame->pmaaddr9);
RV_WRITE_CSR(CSR_PMAADDR(10),frame->pmaaddr10);
RV_WRITE_CSR(CSR_PMAADDR(11),frame->pmaaddr11);
RV_WRITE_CSR(CSR_PMAADDR(12),frame->pmaaddr12);
RV_WRITE_CSR(CSR_PMAADDR(13),frame->pmaaddr13);
RV_WRITE_CSR(CSR_PMAADDR(14),frame->pmaaddr14);
RV_WRITE_CSR(CSR_PMAADDR(15),frame->pmaaddr15);
RV_WRITE_CSR(CSR_PMACFG(0), frame->pmacfg0);
RV_WRITE_CSR(CSR_PMACFG(1), frame->pmacfg1);
RV_WRITE_CSR(CSR_PMACFG(2), frame->pmacfg2);
RV_WRITE_CSR(CSR_PMACFG(3), frame->pmacfg3);
RV_WRITE_CSR(CSR_PMACFG(4), frame->pmacfg4);
RV_WRITE_CSR(CSR_PMACFG(5), frame->pmacfg5);
RV_WRITE_CSR(CSR_PMACFG(6), frame->pmacfg6);
RV_WRITE_CSR(CSR_PMACFG(7), frame->pmacfg7);
RV_WRITE_CSR(CSR_PMACFG(8), frame->pmacfg8);
RV_WRITE_CSR(CSR_PMACFG(9), frame->pmacfg9);
RV_WRITE_CSR(CSR_PMACFG(10), frame->pmacfg10);
RV_WRITE_CSR(CSR_PMACFG(11), frame->pmacfg11);
RV_WRITE_CSR(CSR_PMACFG(12), frame->pmacfg12);
RV_WRITE_CSR(CSR_PMACFG(13), frame->pmacfg13);
RV_WRITE_CSR(CSR_PMACFG(14), frame->pmacfg14);
RV_WRITE_CSR(CSR_PMACFG(15), frame->pmacfg15);
RV_WRITE_CSR(mscratch, frame->mscratch);
RV_WRITE_CSR(misa, frame->misa);
RV_WRITE_CSR(CUSTOM_CSR_MTVT, frame->mtvt);
RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSW, frame->mscratchcsw);
RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSWL, frame->mscratchcswl);
RV_WRITE_CSR(CUSTOM_CSR_MXSTATUS, frame->mxstatus);
RV_WRITE_CSR(CUSTOM_CSR_MHCR, frame->mhcr);
RV_WRITE_CSR(CUSTOM_CSR_MHINT, frame->mhint);
RV_WRITE_CSR(CUSTOM_CSR_MRADDR, frame->mraddr);
RV_WRITE_CSR(CUSTOM_CSR_MEXSTATUS, frame->mexstatus);
RV_WRITE_CSR(CUSTOM_CSR_JVT, frame->jvt);
RV_WRITE_CSR(medeleg, frame->medeleg);
RV_WRITE_CSR(tselect, frame->tselect);
RV_WRITE_CSR(tdata1, frame->tdata1);
RV_WRITE_CSR(tdata2, frame->tdata2);
RV_WRITE_CSR(tcontrol, frame->tcontrol);
RV_WRITE_CSR(pmpaddr0, frame->pmpaddr0);
RV_WRITE_CSR(pmpaddr1, frame->pmpaddr1);
RV_WRITE_CSR(pmpaddr2, frame->pmpaddr2);
RV_WRITE_CSR(pmpaddr3, frame->pmpaddr3);
RV_WRITE_CSR(pmpaddr4, frame->pmpaddr4);
RV_WRITE_CSR(pmpaddr5, frame->pmpaddr5);
RV_WRITE_CSR(pmpaddr6, frame->pmpaddr6);
RV_WRITE_CSR(pmpaddr7, frame->pmpaddr7);
RV_WRITE_CSR(pmpaddr8, frame->pmpaddr8);
RV_WRITE_CSR(pmpaddr9, frame->pmpaddr9);
RV_WRITE_CSR(pmpaddr10, frame->pmpaddr10);
RV_WRITE_CSR(pmpaddr11, frame->pmpaddr11);
RV_WRITE_CSR(pmpaddr12, frame->pmpaddr12);
RV_WRITE_CSR(pmpaddr13, frame->pmpaddr13);
RV_WRITE_CSR(pmpaddr14, frame->pmpaddr14);
RV_WRITE_CSR(pmpaddr15, frame->pmpaddr15);
RV_WRITE_CSR(pmpcfg0, frame->pmpcfg0);
RV_WRITE_CSR(pmpcfg1, frame->pmpcfg1);
RV_WRITE_CSR(pmpcfg2, frame->pmpcfg2);
RV_WRITE_CSR(pmpcfg3, frame->pmpcfg3);
RV_WRITE_CSR(mcycle, frame->mcycle);
RV_WRITE_CSR(mcycleh, frame->mcycleh);
}
static IRAM_ATTR void cpu_domain_dev_regs_save(cpu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
cpu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;
int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
regs_frame[offset++] = *(uint32_t *)addr;
}
}
}
static IRAM_ATTR void cpu_domain_dev_regs_restore(cpu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
cpu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;
int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
*(uint32_t *)addr = regs_frame[offset++];
}
}
}
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
static IRAM_ATTR void update_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr)
{
*(frame_crc_ptr) = esp_rom_crc32_le(0, (void *)frame_ptr, frame_check_size);
}
static IRAM_ATTR void validate_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr)
{
if(*(frame_crc_ptr) != esp_rom_crc32_le(0, (void *)(frame_ptr), frame_check_size)){
// resume uarts
for (int i = 0; i < SOC_UART_NUM; ++i) {
if (!uart_ll_is_enabled(i)) {
continue;
}
uart_ll_force_xon(i);
}
/* Since it is still in the critical now, use ESP_EARLY_LOG */
ESP_EARLY_LOGE(TAG, "Sleep retention frame is corrupted");
esp_restart_noos();
}
}
#endif
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void);
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(void);
extern void rv_core_fpu_save(RvCoreCriticalSleepFrame *frame);
extern void rv_core_fpu_restore(RvCoreCriticalSleepFrame *frame);
typedef uint32_t (* sleep_cpu_entry_cb_t)(uint32_t, uint32_t, uint32_t, bool);
static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
uint8_t core_id = esp_cpu_get_core_id();
bool reject = false;
RvCoreCriticalSleepFrame *frame = s_cpu_retention.retent.critical_frame[core_id];
/* mstatus is core privated CSR, do it near the core critical regs restore */
uint32_t mstatus = save_mstatus_and_disable_global_int();
s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id);
if (s_fpu_saved[core_id]) {
rv_core_fpu_save(frame);
}
rv_core_critical_regs_save();
if ((frame->pmufunc & 0x3) == 0x1) {
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0);
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
/* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
REG_WRITE(RTC_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore);
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE);
while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_BACKUP_DONE) {
;
}
#endif
reject = (*goto_sleep)(wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
}
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
else {
validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
}
#endif
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame);
}
restore_mstatus(mstatus);
return reject ? reject : pmu_sleep_finish(dslp);
}
esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uint32_t, bool),
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_START, (void *)0);
uint8_t core_id = esp_cpu_get_core_id();
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START);
#endif
cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_save();
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[core_id];
/* Minus sizeof(long) is for bypass `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
esp_err_t err = do_cpu_retention(goto_sleep, wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
if (err == ESP_OK) {
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
validate_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
// Start core1
if (core_id == 0) {
REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN);
REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN);
}
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START);
#endif
if (err == ESP_OK) {
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_restore();
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE);
#endif
return err;
}
esp_err_t esp_sleep_cpu_retention_init(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
return esp_sleep_cpu_retention_init_impl(& s_cpu_retention, s_smp_retention_state);
#else
return esp_sleep_cpu_retention_init_impl(& s_cpu_retention);
#endif
}
esp_err_t esp_sleep_cpu_retention_deinit(void)
{
return esp_sleep_cpu_retention_deinit_impl(& s_cpu_retention);
}
bool cpu_domain_pd_allowed(void)
{
bool allowed = true;
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
allowed &= (s_cpu_retention.retent.critical_frame[core_id] != NULL);
allowed &= (s_cpu_retention.retent.non_critical_frame[core_id] != NULL);
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
allowed &= (s_cpu_retention.retent.clic_frame[core_id] != NULL);
}
return allowed;
}
esp_err_t sleep_cpu_configure(bool light_sleep_enable)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
if (light_sleep_enable) {
ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_init(), TAG, "Failed to enable CPU power down during light sleep.");
} else {
ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_deinit(), TAG, "Failed to release CPU retention memory");
}
#endif
return ESP_OK;
}
#if !CONFIG_FREERTOS_UNICORE
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
static IRAM_ATTR void smp_core_do_retention(void)
{
uint8_t core_id = esp_cpu_get_core_id();
if (core_id == 0) {
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0);
} else {
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0);
}
// Wait another core start to do retention
bool smp_skip_retention = false;
smp_retention_state_t another_core_state;
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
while (1) {
another_core_state = atomic_load(&s_smp_retention_state[!core_id]);
if (another_core_state == SMP_SKIP_RETENTION) {
// If another core skips the retention, the current core should also have to skip it.
smp_skip_retention = true;
break;
} else if (another_core_state == SMP_BACKUP_START) {
break;
}
}
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
if (!smp_skip_retention) {
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START);
rv_core_noncritical_regs_save();
cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]);
RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id];
uint32_t mstatus = save_mstatus_and_disable_global_int();
s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id);
if (s_fpu_saved[core_id]) {
rv_core_fpu_save(frame_critical);
}
rv_core_critical_regs_save();
if ((frame_critical->pmufunc & 0x3) == 0x1) {
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE);
// wait another core trigger sleep and wakeup
while (1) {
// If another core's sleep request is rejected by the hardware, jumps out of blocking.
another_core_state = atomic_load(&s_smp_retention_state[!core_id]);
if (another_core_state == SMP_SKIP_RETENTION) {
break;
}
}
} else {
// Start core1
if (core_id == 0) {
REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN);
REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN);
}
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START);
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame_critical);
}
restore_mstatus(mstatus);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_restore();
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE);
}
}
// wait another core out sleep
while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_IDLE) {
;
}
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
}
IRAM_ATTR void esp_sleep_cpu_skip_retention(void) {
atomic_store(&s_smp_retention_state[esp_cpu_get_core_id()], SMP_SKIP_RETENTION);
}
#endif
void sleep_smp_cpu_sleep_prepare(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) {
;
}
esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL);
#else
esp_ipc_isr_stall_other_cpu();
#endif
}
void sleep_smp_cpu_wakeup_prepare(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
uint8_t core_id = esp_cpu_get_core_id();
if (atomic_load(&s_smp_retention_state[core_id]) == SMP_RESTORE_DONE) {
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_RESTORE_DONE) {
;
}
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
}
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
#else
esp_ipc_isr_release_other_cpu();
#endif
}
#endif //!CONFIG_FREERTOS_UNICORE

View File

@@ -0,0 +1,219 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "rvsleep-frames.h"
#include "freertos/FreeRTOSConfig.h"
#include "sdkconfig.h"
#include "riscv/csr_clic.h"
.section .data1,"aw"
.global rv_core_critical_regs_frame
.type rv_core_critical_regs_frame,@object
.align 4
rv_core_critical_regs_frame:
.rept (portNUM_PROCESSORS)
.word 0
.endr
/*
--------------------------------------------------------------------------------
This assembly subroutine is used to save the critical registers of the CPU
core to the internal RAM before sleep, and modify the PMU control flag to
indicate that the system needs to sleep. When the subroutine returns, it
will return the memory pointer that saves the context information of the CPU
critical registers.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_critical_regs_save
.type rv_core_critical_regs_save,@function
.align 4
rv_core_critical_regs_save:
/* arrived here in critical section. we need:
save riscv core critical registers to RvCoreCriticalSleepFrame
*/
csrw mscratch, t0 /* use mscratch as temp storage */
la a0, rv_core_critical_regs_frame
csrr t1, mhartid
slli t1, t1, 2
add a0, a0, t1
lw t0, 0(a0) /* t0 pointer to RvCoreCriticalSleepFrame object */
sw ra, RV_SLP_CTX_RA(t0)
sw sp, RV_SLP_CTX_SP(t0)
sw gp, RV_SLP_CTX_GP(t0)
sw tp, RV_SLP_CTX_TP(t0)
sw t1, RV_SLP_CTX_T1(t0)
sw t2, RV_SLP_CTX_T2(t0)
sw s0, RV_SLP_CTX_S0(t0)
sw s1, RV_SLP_CTX_S1(t0)
/* a0 is caller saved, so it does not need to be saved, but it should be the
pointer value of RvCoreCriticalSleepFrame for return.
*/
mv a0, t0
sw a0, RV_SLP_CTX_A0(t0)
sw a1, RV_SLP_CTX_A1(t0)
sw a2, RV_SLP_CTX_A2(t0)
sw a3, RV_SLP_CTX_A3(t0)
sw a4, RV_SLP_CTX_A4(t0)
sw a5, RV_SLP_CTX_A5(t0)
sw a6, RV_SLP_CTX_A6(t0)
sw a7, RV_SLP_CTX_A7(t0)
sw s2, RV_SLP_CTX_S2(t0)
sw s3, RV_SLP_CTX_S3(t0)
sw s4, RV_SLP_CTX_S4(t0)
sw s5, RV_SLP_CTX_S5(t0)
sw s6, RV_SLP_CTX_S6(t0)
sw s7, RV_SLP_CTX_S7(t0)
sw s8, RV_SLP_CTX_S8(t0)
sw s9, RV_SLP_CTX_S9(t0)
sw s10, RV_SLP_CTX_S10(t0)
sw s11, RV_SLP_CTX_S11(t0)
sw t3, RV_SLP_CTX_T3(t0)
sw t4, RV_SLP_CTX_T4(t0)
sw t5, RV_SLP_CTX_T5(t0)
sw t6, RV_SLP_CTX_T6(t0)
csrr t1, mstatus
sw t1, RV_SLP_CTX_MSTATUS(t0)
csrr t2, mtvec
sw t2, RV_SLP_CTX_MTVEC(t0)
csrr t3, mcause
sw t3, RV_SLP_CTX_MCAUSE(t0)
csrr t1, mtval
sw t1, RV_SLP_CTX_MTVAL(t0)
csrr t2, mie
sw t2, RV_SLP_CTX_MIE(t0)
csrr t3, mip
sw t3, RV_SLP_CTX_MIP(t0)
csrr t1, mepc
sw t1, RV_SLP_CTX_MEPC(t0)
csrr t2, MINTTHRESH_CSR
sw t2, RV_SLP_CTX_MINTTHRESH(t0)
/*
!!! Let idf knows it's going to sleep !!!
RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or
has just been awakened. We use the lowest 2 bits as indication information,
3 means being awakened, 1 means going to sleep.
*/
li t1, ~0x3
lw t2, RV_SLP_CTX_PMUFUNC(t0)
and t2, t1, t2
ori t2, t2, 0x1
sw t2, RV_SLP_CTX_PMUFUNC(t0)
mv t3, t0
csrr t0, mscratch
sw t0, RV_SLP_CTX_T0(t3)
lw t1, RV_SLP_CTX_T1(t3)
lw t2, RV_SLP_CTX_T2(t3)
lw t3, RV_SLP_CTX_T3(t3)
ret
.size rv_core_critical_regs_save, . - rv_core_critical_regs_save
/*
--------------------------------------------------------------------------------
This assembly subroutine is used to restore the CPU core critical register
context before sleep after system wakes up, modify the PMU control
information, and return the critical register context memory object pointer.
After the subroutine returns, continue to restore other modules of the
system.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_critical_regs_restore
.weak rv_core_critical_regs_restore
.type rv_core_critical_regs_restore,@function
.global _rv_core_critical_regs_restore
.type _rv_core_critical_regs_restore,@function
.align 4
_rv_core_critical_regs_restore: /* export a strong symbol to jump to here, used
* for a static callback */
nop
rv_core_critical_regs_restore:
la t0, rv_core_critical_regs_frame
csrr t1, mhartid
slli t1, t1, 2
add t0, t0, t1
lw t0, 0(t0) /* t0 pointer to RvCoreCriticalSleepFrame object */
beqz t0, .skip_restore /* make sure we do not jump to zero address */
/*
!!! Let idf knows it's sleep awake. !!!
RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or
has just been awakened. We use the lowest 2 bits as indication information,
3 means being awakened, 1 means going to sleep.
*/
lw t1, RV_SLP_CTX_PMUFUNC(t0)
ori t1, t1, 0x3
sw t1, RV_SLP_CTX_PMUFUNC(t0)
lw t2, RV_SLP_CTX_MINTTHRESH(t0)
csrw MINTTHRESH_CSR, t2
lw t2, RV_SLP_CTX_MEPC(t0)
csrw mepc, t2
lw t3, RV_SLP_CTX_MIP(t0)
csrw mip, t3
lw t1, RV_SLP_CTX_MIE(t0)
csrw mie, t1
lw t2, RV_SLP_CTX_MSTATUS(t0)
csrw mstatus, t2
lw t3, RV_SLP_CTX_MTVEC(t0)
csrw mtvec, t3
lw t1, RV_SLP_CTX_MCAUSE(t0)
csrw mcause, t1
lw t2, RV_SLP_CTX_MTVAL(t0)
csrw mtval, t2
lw t6, RV_SLP_CTX_T6(t0)
lw t5, RV_SLP_CTX_T5(t0)
lw t4, RV_SLP_CTX_T4(t0)
lw t3, RV_SLP_CTX_T3(t0)
lw s11, RV_SLP_CTX_S11(t0)
lw s10, RV_SLP_CTX_S10(t0)
lw s9, RV_SLP_CTX_S9(t0)
lw s8, RV_SLP_CTX_S8(t0)
lw s7, RV_SLP_CTX_S7(t0)
lw s6, RV_SLP_CTX_S6(t0)
lw s5, RV_SLP_CTX_S5(t0)
lw s4, RV_SLP_CTX_S4(t0)
lw s3, RV_SLP_CTX_S3(t0)
lw s2, RV_SLP_CTX_S2(t0)
lw a7, RV_SLP_CTX_A7(t0)
lw a6, RV_SLP_CTX_A6(t0)
lw a5, RV_SLP_CTX_A5(t0)
lw a4, RV_SLP_CTX_A4(t0)
lw a3, RV_SLP_CTX_A3(t0)
lw a2, RV_SLP_CTX_A2(t0)
lw a1, RV_SLP_CTX_A1(t0)
lw a0, RV_SLP_CTX_A0(t0)
lw s1, RV_SLP_CTX_S1(t0)
lw s0, RV_SLP_CTX_S0(t0)
lw t2, RV_SLP_CTX_T2(t0)
lw t1, RV_SLP_CTX_T1(t0)
lw tp, RV_SLP_CTX_TP(t0)
lw gp, RV_SLP_CTX_GP(t0)
lw sp, RV_SLP_CTX_SP(t0)
lw ra, RV_SLP_CTX_RA(t0)
lw t0, RV_SLP_CTX_T0(t0)
.skip_restore:
ret
.size rv_core_critical_regs_restore, . - rv_core_critical_regs_restore

View File

@@ -0,0 +1,147 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "soc/clic_reg.h"
#include "esp_sleep.h"
#include "rvsleep-frames.h"
#include "sleep_cpu_retention.h"
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS];
static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num)
{
const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num;
int regs_frame_sz = 0;
for (int num = 0; num < region_num; num++) {
regs_frame_sz += regions[num].end - regions[num].start;
}
void *frame = heap_caps_malloc(sizeof(cpu_domain_dev_sleep_frame_t) + region_sz + regs_frame_sz, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame) {
cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}
static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id)
{
const static cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][3] = {
[0 ... portNUM_PROCESSORS - 1] = {
{ .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 },
{ .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 },
{ .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 },
}
};
return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(cpu_domain_dev_regs_region_t));
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
atomic_init(&s_smp_retention_state[core_id], SMP_IDLE);
}
return ESP_OK;
err:
esp_sleep_cpu_retention_deinit();
return ESP_ERR_NO_MEM;
}
#else
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
return ESP_OK;
err:
esp_sleep_cpu_retention_deinit();
return ESP_ERR_NO_MEM;
}
#endif
esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) {
heap_caps_free((void *)sleep_cpu_retention_ptr->retent.critical_frame[core_id]);
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL;
rv_core_critical_regs_frame[core_id] = NULL;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) {
heap_caps_free((void *)sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]);
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) {
heap_caps_free((void *)sleep_cpu_retention_ptr->retent.clic_frame[core_id]);
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL;
}
}
return ESP_OK;
}

View File

@@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SLEEP_CPU_RETENTION_H__
#define __SLEEP_CPU_RETENTION_H__
#include "rvsleep-frames.h"
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
#include <stdatomic.h>
#include "soc/hp_system_reg.h"
typedef enum {
SMP_IDLE,
SMP_BACKUP_START,
SMP_BACKUP_DONE,
SMP_RESTORE_START,
SMP_RESTORE_DONE,
SMP_SKIP_RETENTION,
} smp_retention_state_t;
#endif
typedef struct {
uint32_t start;
uint32_t end;
} cpu_domain_dev_regs_region_t;
typedef struct {
cpu_domain_dev_regs_region_t *region;
int region_num;
uint32_t *regs_frame;
} cpu_domain_dev_sleep_frame_t;
/**
* Internal structure which holds all requested light sleep cpu retention parameters
*/
typedef struct {
struct {
RvCoreCriticalSleepFrame *critical_frame[portNUM_PROCESSORS];
RvCoreNonCriticalSleepFrame *non_critical_frame[portNUM_PROCESSORS];
cpu_domain_dev_sleep_frame_t *clic_frame[portNUM_PROCESSORS];
} retent;
} sleep_cpu_retention_t;
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state);
#else
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr);
#endif
esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr);
#endif /* #ifndef __SLEEP_CPU_RETENTION_H__ */

View File

@@ -0,0 +1,146 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_check.h"
#include "soc/clic_reg.h"
#include "rvsleep-frames.h"
#include "sleep_cpu_retention.h"
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS];
#define R_CONCAT(s1, s2) _R_CONCAT(s1, s2)
#define _R_CONCAT(s1, s2) s1 ## s2
#define CPU_DOMAIN_DEV_SZ0 (CPU_DOMAIN_DEV_END_ADDR0 - CPU_DOMAIN_DEV_START_ADDR0)
#define CPU_DOMAIN_DEV_SZ1 (CPU_DOMAIN_DEV_END_ADDR1 - CPU_DOMAIN_DEV_START_ADDR1)
#define CPU_DOMAIN_DEV_SZ2 (CPU_DOMAIN_DEV_END_ADDR2 - CPU_DOMAIN_DEV_START_ADDR2)
#define CPU_DOMAIN_DEV_SZ3 (CPU_DOMAIN_DEV_END_ADDR3 - CPU_DOMAIN_DEV_START_ADDR3)
#define CPU_DOMAIN_DEV_SZ4 (CPU_DOMAIN_DEV_END_ADDR4 - CPU_DOMAIN_DEV_START_ADDR4)
#define CPU_DOMAIN_DEV_SZ5 (CPU_DOMAIN_DEV_END_ADDR5 - CPU_DOMAIN_DEV_START_ADDR5)
#define CPU_DOMAIN_DEV_SZ6 (CPU_DOMAIN_DEV_END_ADDR6 - CPU_DOMAIN_DEV_START_ADDR6)
#define CPU_DOMAIN_DEV_SZ7 (CPU_DOMAIN_DEV_END_ADDR7 - CPU_DOMAIN_DEV_START_ADDR7)
#define CPU_DOMAIN_DEV_TOTAL_SZ(n) (R_CONCAT(__TOTAL_SZ, n))
#define __TOTAL_SZ0 (sizeof(cpu_domain_dev_sleep_frame_t))
#define __TOTAL_SZ1 ((__TOTAL_SZ0) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ0)
#define __TOTAL_SZ2 ((__TOTAL_SZ1) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ1)
#define __TOTAL_SZ3 ((__TOTAL_SZ2) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ2)
#define __TOTAL_SZ4 ((__TOTAL_SZ3) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ3)
#define __TOTAL_SZ5 ((__TOTAL_SZ4) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ4)
#define __TOTAL_SZ6 ((__TOTAL_SZ5) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ5)
#define __TOTAL_SZ7 ((__TOTAL_SZ6) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ6)
#define __TOTAL_SZ8 ((__TOTAL_SZ7) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ7)
static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num, void * frame)
{
const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num;
int regs_frame_sz = 0;
for (int num = 0; num < region_num; num++) {
regs_frame_sz += regions[num].end - regions[num].start;
}
if (frame) {
cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}
static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id)
{
static DRAM_ATTR cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][2] = {
[0 ... portNUM_PROCESSORS - 1] = {
{ .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 },
{ .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 },
{ .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 },
}
};
static DRAM_ATTR uint8_t sleep_frame[portNUM_PROCESSORS][CPU_DOMAIN_DEV_TOTAL_SZ(2)] __attribute__((aligned(4)));
return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(regions[core_id][0]), sleep_frame[core_id]);
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state)
{
static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4)));
static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4)));
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ;
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame));
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
atomic_init(&s_smp_retention_state[core_id], SMP_IDLE);
}
return ESP_OK;
}
#else
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4)));
static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4)));
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ;
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame));
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
return ESP_OK;
}
#endif
esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) {
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL;
rv_core_critical_regs_frame[core_id] = NULL;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) {
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) {
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL;
}
}
return ESP_OK;
}

View File

@@ -0,0 +1,118 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "rvsleep-frames.h"
#include "freertos/FreeRTOSConfig.h"
#include "sdkconfig.h"
.section .iram1,"ax"
.global rv_core_fpu_save
.type rv_core_fpu_save,@function
.align 4
/* Bit to set in mstatus to enable the FPU */
#define CSR_MSTATUS_FPU_ENABLE (1 << 13)
/* Bit to clear in mstatus to disable the FPU */
#define CSR_MSTATUS_FPU_DISABLE (3 << 13)
.macro fpu_enable reg
li \reg, CSR_MSTATUS_FPU_ENABLE
csrs mstatus, \reg
.endm
rv_core_fpu_save:
fpu_enable t0
mv t0, a0 /* t0 = frame (arg0) */
fsw ft0, RV_SLP_CTX_FPU_FT0(t0)
fsw ft1, RV_SLP_CTX_FPU_FT1(t0)
fsw ft2, RV_SLP_CTX_FPU_FT2(t0)
fsw ft3, RV_SLP_CTX_FPU_FT3(t0)
fsw ft4, RV_SLP_CTX_FPU_FT4(t0)
fsw ft5, RV_SLP_CTX_FPU_FT5(t0)
fsw ft6, RV_SLP_CTX_FPU_FT6(t0)
fsw ft7, RV_SLP_CTX_FPU_FT7(t0)
fsw fs0, RV_SLP_CTX_FPU_FS0(t0)
fsw fs1, RV_SLP_CTX_FPU_FS1(t0)
fsw fa0, RV_SLP_CTX_FPU_FA0(t0)
fsw fa1, RV_SLP_CTX_FPU_FA1(t0)
fsw fa2, RV_SLP_CTX_FPU_FA2(t0)
fsw fa3, RV_SLP_CTX_FPU_FA3(t0)
fsw fa4, RV_SLP_CTX_FPU_FA4(t0)
fsw fa5, RV_SLP_CTX_FPU_FA5(t0)
fsw fa6, RV_SLP_CTX_FPU_FA6(t0)
fsw fa7, RV_SLP_CTX_FPU_FA7(t0)
fsw fs2, RV_SLP_CTX_FPU_FS2(t0)
fsw fs3, RV_SLP_CTX_FPU_FS3(t0)
fsw fs4, RV_SLP_CTX_FPU_FS4(t0)
fsw fs5, RV_SLP_CTX_FPU_FS5(t0)
fsw fs6, RV_SLP_CTX_FPU_FS6(t0)
fsw fs7, RV_SLP_CTX_FPU_FS7(t0)
fsw fs8, RV_SLP_CTX_FPU_FS8(t0)
fsw fs9, RV_SLP_CTX_FPU_FS9(t0)
fsw fs10, RV_SLP_CTX_FPU_FS10(t0)
fsw fs11, RV_SLP_CTX_FPU_FS11(t0)
fsw ft8, RV_SLP_CTX_FPU_FT8(t0)
fsw ft9, RV_SLP_CTX_FPU_FT9(t0)
fsw ft10, RV_SLP_CTX_FPU_FT10(t0)
fsw ft11, RV_SLP_CTX_FPU_FT11(t0)
csrr t1, fcsr
sw t1, RV_SLP_CTX_FPU_FCSR(t0)
/* Chip will go to sleep and FPU will be powered down, not necessary to disable FPU here. */
ret
.size rv_core_fpu_save, . - rv_core_fpu_save
/*
--------------------------------------------------------------------------------
FPU restore: a0 = RvCoreCriticalSleepFrame *. Restore all FP registers from the frame.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_fpu_restore
.type rv_core_fpu_restore,@function
.align 4
rv_core_fpu_restore:
fpu_enable t0
mv t0, a0 /* t0 = frame (arg0) */
flw ft0, RV_SLP_CTX_FPU_FT0(t0)
flw ft1, RV_SLP_CTX_FPU_FT1(t0)
flw ft2, RV_SLP_CTX_FPU_FT2(t0)
flw ft3, RV_SLP_CTX_FPU_FT3(t0)
flw ft4, RV_SLP_CTX_FPU_FT4(t0)
flw ft5, RV_SLP_CTX_FPU_FT5(t0)
flw ft6, RV_SLP_CTX_FPU_FT6(t0)
flw ft7, RV_SLP_CTX_FPU_FT7(t0)
flw fs0, RV_SLP_CTX_FPU_FS0(t0)
flw fs1, RV_SLP_CTX_FPU_FS1(t0)
flw fa0, RV_SLP_CTX_FPU_FA0(t0)
flw fa1, RV_SLP_CTX_FPU_FA1(t0)
flw fa2, RV_SLP_CTX_FPU_FA2(t0)
flw fa3, RV_SLP_CTX_FPU_FA3(t0)
flw fa4, RV_SLP_CTX_FPU_FA4(t0)
flw fa5, RV_SLP_CTX_FPU_FA5(t0)
flw fa6, RV_SLP_CTX_FPU_FA6(t0)
flw fa7, RV_SLP_CTX_FPU_FA7(t0)
flw fs2, RV_SLP_CTX_FPU_FS2(t0)
flw fs3, RV_SLP_CTX_FPU_FS3(t0)
flw fs4, RV_SLP_CTX_FPU_FS4(t0)
flw fs5, RV_SLP_CTX_FPU_FS5(t0)
flw fs6, RV_SLP_CTX_FPU_FS6(t0)
flw fs7, RV_SLP_CTX_FPU_FS7(t0)
flw fs8, RV_SLP_CTX_FPU_FS8(t0)
flw fs9, RV_SLP_CTX_FPU_FS9(t0)
flw fs10, RV_SLP_CTX_FPU_FS10(t0)
flw fs11, RV_SLP_CTX_FPU_FS11(t0)
flw ft8, RV_SLP_CTX_FPU_FT8(t0)
flw ft9, RV_SLP_CTX_FPU_FT9(t0)
flw ft10, RV_SLP_CTX_FPU_FT10(t0)
flw ft11, RV_SLP_CTX_FPU_FT11(t0)
lw t1, RV_SLP_CTX_FPU_FCSR(t0)
csrw fcsr, t1
/* Caller restores mstatus (and thus FPU state); not necessary to disable FPU here. */
ret
.size rv_core_fpu_restore, . - rv_core_fpu_restore

View File

@@ -1083,6 +1083,10 @@ config SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP
bool
default y
config SOC_PM_SUPPORT_CPU_PD
bool
default y
config SOC_PM_SUPPORT_XTAL32K_PD
bool
default y
@@ -1131,6 +1135,10 @@ config SOC_PM_CPU_RETENTION_BY_SW
bool
default y
config SOC_PM_FPU_RETENTION_BY_SW
bool
default y
config SOC_PM_CACHE_RETENTION_BY_PAU
bool
default y

View File

@@ -441,7 +441,7 @@
#define SOC_PM_SUPPORT_WIFI_WAKEUP (1)
#define SOC_PM_SUPPORT_BEACON_WAKEUP (1)
#define SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP (1) /*!<Supports waking up from touch pad trigger */
// #define SOC_PM_SUPPORT_CPU_PD (1) // TODO: [ESP32S31] IDF-14647
#define SOC_PM_SUPPORT_CPU_PD (1)
#define SOC_PM_SUPPORT_XTAL32K_PD (1)
#define SOC_PM_SUPPORT_RC32K_PD (1)
#define SOC_PM_SUPPORT_RC_FAST_PD (1)
@@ -458,6 +458,7 @@
#define SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY (1) /*!<Supports CRC only the stub code in RTC memory */
#define SOC_PM_CPU_RETENTION_BY_SW (1)
#define SOC_PM_FPU_RETENTION_BY_SW (1)
#define SOC_PM_CACHE_RETENTION_BY_PAU (1)
#define SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN (1)