feat(esp_hw_support): support esp32p4 & esp32h4 RV FPU context retention in pd_cpu sleep

This commit is contained in:
wuzhenghui
2026-02-05 15:39:40 +08:00
parent 5d597d3e63
commit b7edaebed9
14 changed files with 393 additions and 13 deletions
@@ -20,6 +20,13 @@ if(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP OR
APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u rv_core_critical_regs_save") APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u rv_core_critical_regs_save")
set_property(TARGET ${COMPONENT_LIB} set_property(TARGET ${COMPONENT_LIB}
APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u rv_core_critical_regs_restore") APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u rv_core_critical_regs_restore")
if(CONFIG_SOC_CPU_HAS_FPU AND CONFIG_SOC_PM_FPU_RETENTION_BY_SW)
list(APPEND srcs "port/${target}/sleep_fpu_asm.S")
set_property(TARGET ${COMPONENT_LIB}
APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u rv_core_fpu_save")
set_property(TARGET ${COMPONENT_LIB}
APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u rv_core_fpu_restore")
endif()
endif() endif()
endif() endif()
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -85,6 +85,40 @@ STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIP, mip) /* Machine intr pending */ STRUCT_FIELD (long, 4, RV_SLP_CTX_MIP, mip) /* Machine intr pending */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MINTTHRESH, mintthresh) /* Machine intr threshold */ 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 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 * to sleep or has just been awakened. We use the
* lowest 2 bits as indication information, 3 means * lowest 2 bits as indication information, 3 means
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -19,6 +19,7 @@
#include "esp_rom_crc.h" #include "esp_rom_crc.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/portmacro.h"
#include "riscv/csr.h" #include "riscv/csr.h"
#include "soc/pcr_reg.h" #include "soc/pcr_reg.h"
#include "soc/intpri_reg.h" #include "soc/intpri_reg.h"
@@ -42,6 +43,8 @@
static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS]; static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS];
#endif #endif
static bool s_fpu_saved[portNUM_PROCESSORS];
static __attribute__((unused)) const char *TAG = "sleep"; static __attribute__((unused)) const char *TAG = "sleep";
static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention; static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention;
@@ -337,6 +340,8 @@ static IRAM_ATTR void validate_retention_frame_crc(uint32_t *frame_ptr, uint32_t
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void); extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void);
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(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); 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, static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
@@ -344,14 +349,19 @@ static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
{ {
__attribute__((unused)) uint8_t core_id = esp_cpu_get_core_id(); __attribute__((unused)) uint8_t core_id = esp_cpu_get_core_id();
bool reject = false; 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 */ /* mstatus is core privated CSR, do it near the core critical regs restore */
uint32_t mstatus = save_mstatus_and_disable_global_int(); uint32_t mstatus = save_mstatus_and_disable_global_int();
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND #if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
uint32_t mintthresh = save_mintthresh_and_disable_global_int(); uint32_t mintthresh = save_mintthresh_and_disable_global_int();
#endif #endif
s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id);
if (s_fpu_saved[core_id]) {
rv_core_fpu_save(frame);
}
rv_core_critical_regs_save(); rv_core_critical_regs_save();
RvCoreCriticalSleepFrame * frame = s_cpu_retention.retent.critical_frame[core_id];
if ((frame->pmufunc & 0x3) == 0x1) { if ((frame->pmufunc & 0x3) == 0x1) {
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0); esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0);
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME #if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
@@ -374,6 +384,9 @@ static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
} }
#endif #endif
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame);
}
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND #if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
restore_mintthresh(mintthresh); restore_mintthresh(mintthresh);
#endif #endif
@@ -504,13 +517,16 @@ static IRAM_ATTR void smp_core_do_retention(void)
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND #if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
uint32_t mintthresh = save_mintthresh_and_disable_global_int(); uint32_t mintthresh = save_mintthresh_and_disable_global_int();
#endif #endif
RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id];
s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id);
if (s_fpu_saved[core_id]) {
rv_core_fpu_save(frame_critical);
}
rv_core_noncritical_regs_save(); rv_core_noncritical_regs_save();
cpu_domain_dev_regs_save(s_cpu_retention.retent.clint_frame[core_id]); cpu_domain_dev_regs_save(s_cpu_retention.retent.clint_frame[core_id]);
cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]); cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_critical_regs_save(); rv_core_critical_regs_save();
RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id];
if ((frame_critical->pmufunc & 0x3) == 0x1) { if ((frame_critical->pmufunc & 0x3) == 0x1) {
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE); atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE);
// wait another core trigger sleep and wakeup // wait another core trigger sleep and wakeup
while (1) { while (1) {
@@ -531,6 +547,9 @@ static IRAM_ATTR void smp_core_do_retention(void)
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]); cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clint_frame[core_id]); cpu_domain_dev_regs_restore(s_cpu_retention.retent.clint_frame[core_id]);
rv_core_noncritical_regs_restore(); rv_core_noncritical_regs_restore();
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame_critical);
}
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND #if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
restore_mintthresh(mintthresh); restore_mintthresh(mintthresh);
#endif #endif
@@ -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
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -88,6 +88,40 @@ STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIE, mie) /* Machine intr enable */ 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_MIP, mip) /* Machine intr pending */
/* 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 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 * to sleep or has just been awakened. We use the
* lowest 2 bits as indication information, 3 means * lowest 2 bits as indication information, 3 means
@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -18,6 +18,7 @@
#include "esp_rom_crc.h" #include "esp_rom_crc.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/portmacro.h"
#include "riscv/csr.h" #include "riscv/csr.h"
#include "soc/clic_reg.h" #include "soc/clic_reg.h"
#include "soc/rtc_periph.h" #include "soc/rtc_periph.h"
@@ -43,6 +44,7 @@
static TCM_DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS]; static TCM_DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS];
#endif #endif
static bool s_fpu_saved[portNUM_PROCESSORS];
ESP_LOG_ATTR_TAG(TAG, "sleep"); ESP_LOG_ATTR_TAG(TAG, "sleep");
@@ -250,6 +252,8 @@ static TCM_IRAM_ATTR void validate_retention_frame_crc(uint32_t *frame_ptr, uint
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void); extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void);
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(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); typedef uint32_t (* sleep_cpu_entry_cb_t)(uint32_t, uint32_t, uint32_t, bool);
static TCM_IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep, static TCM_IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
@@ -257,11 +261,14 @@ static TCM_IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
{ {
uint8_t core_id = esp_cpu_get_core_id(); uint8_t core_id = esp_cpu_get_core_id();
bool reject = false; 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 */ /* mstatus is core privated CSR, do it near the core critical regs restore */
uint32_t mstatus = save_mstatus_and_disable_global_int(); 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(); rv_core_critical_regs_save();
RvCoreCriticalSleepFrame * frame = s_cpu_retention.retent.critical_frame[core_id];
if ((frame->pmufunc & 0x3) == 0x1) { if ((frame->pmufunc & 0x3) == 0x1) {
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0); esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0);
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME #if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
@@ -284,6 +291,9 @@ static TCM_IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
} }
#endif #endif
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame);
}
restore_mstatus(mstatus); restore_mstatus(mstatus);
return reject ? reject : pmu_sleep_finish(dslp); return reject ? reject : pmu_sleep_finish(dslp);
} }
@@ -403,9 +413,13 @@ static TCM_IRAM_ATTR void smp_core_do_retention(void)
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START); atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START);
rv_core_noncritical_regs_save(); rv_core_noncritical_regs_save();
cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]); cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]);
uint32_t mstatus = save_mstatus_and_disable_global_int();
rv_core_critical_regs_save();
RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_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) { if ((frame_critical->pmufunc & 0x3) == 0x1) {
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE); atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE);
// wait another core trigger sleep and wakeup // wait another core trigger sleep and wakeup
@@ -423,6 +437,9 @@ static TCM_IRAM_ATTR void smp_core_do_retention(void)
REG_CLR_BIT(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_CORE1_GLOBAL); REG_CLR_BIT(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_CORE1_GLOBAL);
} }
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START); 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); restore_mstatus(mstatus);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]); cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_restore(); rv_core_noncritical_regs_restore();
@@ -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 .tcm.text,"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
@@ -6,7 +6,7 @@
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
* *
* SPDX-FileContributor: 2023-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileContributor: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
@@ -747,6 +747,21 @@ extern volatile UBaseType_t xPortSwitchFlag[portNUM_PROCESSORS];
#define os_task_switch_is_pended(_cpu_) (false) #define os_task_switch_is_pended(_cpu_) (false)
#endif #endif
// -------------- FPU softerware retention ------------------
#if (SOC_CPU_COPROC_NUM > 0) && SOC_CPU_HAS_FPU && SOC_PM_FPU_RETENTION_BY_SW
/**
* @brief Whether the FPU context is dirty on the given core.
*
* Returns non-zero if any task has used the FPU, such context should be
* saved during sleep retention.
*
* @param core_id Core id
* @return pdTRUE FPU context is dirty
* @return pdFALSE FPU was not used
*/
BaseType_t xPortFPUContextIsDirty(BaseType_t core_id);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@@ -6,7 +6,7 @@
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
* *
* SPDX-FileContributor: 2023-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileContributor: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
@@ -895,3 +895,9 @@ configRUN_TIME_COUNTER_TYPE xPortGetRunTimeCounterValue( void )
/* ---------------------------------------------- Misc Implementations ------------------------------------------------- /* ---------------------------------------------- Misc Implementations -------------------------------------------------
* *
* ------------------------------------------------------------------------------------------------------------------ */ * ------------------------------------------------------------------------------------------------------------------ */
#if (SOC_CPU_COPROC_NUM > 0) && SOC_CPU_HAS_FPU && SOC_PM_FPU_RETENTION_BY_SW
BaseType_t xPortFPUContextIsDirty(BaseType_t core_id)
{
return (BaseType_t)(port_uxCoprocOwner[core_id][FPU_COPROC_IDX] != NULL);
}
#endif /* (SOC_CPU_COPROC_NUM > 0) && SOC_CPU_HAS_FPU && SOC_PM_FPU_RETENTION_BY_SW */
+2
View File
@@ -176,6 +176,8 @@ entries:
port:xPortInIsrContext (noflash_text) port:xPortInIsrContext (noflash_text)
port:vPortAssertIfInISR (noflash_text) port:vPortAssertIfInISR (noflash_text)
port:xPortInterruptedFromISRContext (noflash_text) port:xPortInterruptedFromISRContext (noflash_text)
if (SOC_CPU_COPROC_NUM > 0) && SOC_CPU_HAS_FPU && SOC_PM_FPU_RETENTION_BY_SW:
port:xPortFPUContextIsDirty (noflash_text)
# -------------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------------
# portasm.S # portasm.S
@@ -1039,6 +1039,10 @@ config SOC_PM_CPU_RETENTION_BY_SW
bool bool
default y default y
config SOC_PM_FPU_RETENTION_BY_SW
bool
default y
config SOC_PM_MODEM_RETENTION_BY_REGDMA config SOC_PM_MODEM_RETENTION_BY_REGDMA
bool bool
default y default y
@@ -495,6 +495,7 @@
#define SOC_PM_SUPPORT_PMU_CLK_ICG (1) #define SOC_PM_SUPPORT_PMU_CLK_ICG (1)
#define SOC_PM_CPU_RETENTION_BY_SW (1) #define SOC_PM_CPU_RETENTION_BY_SW (1)
#define SOC_PM_FPU_RETENTION_BY_SW (1)
#define SOC_PM_MODEM_RETENTION_BY_REGDMA (1) #define SOC_PM_MODEM_RETENTION_BY_REGDMA (1)
#define SOC_PM_MODEM_CLK_CONF_RETENTION (1) /*!< In esp32H4, i2c lpcon is placed in modem domain*/ #define SOC_PM_MODEM_CLK_CONF_RETENTION (1) /*!< In esp32H4, i2c lpcon is placed in modem domain*/
#define SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN (1) #define SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN (1)
@@ -1783,6 +1783,10 @@ config SOC_PM_CPU_RETENTION_BY_SW
bool bool
default y default y
config SOC_PM_FPU_RETENTION_BY_SW
bool
default y
config SOC_PM_CACHE_RETENTION_BY_PAU config SOC_PM_CACHE_RETENTION_BY_PAU
bool bool
default y default y
@@ -676,6 +676,7 @@
#define SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY (1) /*!<Supports CRC only the stub code in RTC memory */ #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_CPU_RETENTION_BY_SW (1)
#define SOC_PM_FPU_RETENTION_BY_SW (1)
#define SOC_PM_CACHE_RETENTION_BY_PAU (1) #define SOC_PM_CACHE_RETENTION_BY_PAU (1)
#define SOC_PM_PAU_LINK_NUM (4) #define SOC_PM_PAU_LINK_NUM (4)