Merge branch 'feat/support_rv_fp_retention' into 'master'

feat(esp_hw_support): support rv fp retention

See merge request espressif/esp-idf!45565
This commit is contained in:
Wu Zheng Hui
2026-03-03 17:36:04 +08:00
16 changed files with 502 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")
set_property(TARGET ${COMPONENT_LIB}
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()
@@ -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
*/
@@ -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_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
@@ -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
*/
@@ -19,6 +19,7 @@
#include "esp_rom_crc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/portmacro.h"
#include "riscv/csr.h"
#include "soc/pcr_reg.h"
#include "soc/intpri_reg.h"
@@ -42,6 +43,8 @@
static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS];
#endif
static bool s_fpu_saved[portNUM_PROCESSORS];
static __attribute__((unused)) const char *TAG = "sleep";
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_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,
@@ -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();
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();
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
uint32_t mintthresh = save_mintthresh_and_disable_global_int();
#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();
RvCoreCriticalSleepFrame * frame = s_cpu_retention.retent.critical_frame[core_id];
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
@@ -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));
}
#endif
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame);
}
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
restore_mintthresh(mintthresh);
#endif
@@ -504,13 +517,16 @@ static IRAM_ATTR void smp_core_do_retention(void)
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
uint32_t mintthresh = save_mintthresh_and_disable_global_int();
#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();
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]);
rv_core_critical_regs_save();
RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id];
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) {
@@ -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.clint_frame[core_id]);
rv_core_noncritical_regs_restore();
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame_critical);
}
#if __riscv_zcmp && SOC_CPU_ZCMP_WORKAROUND
restore_mintthresh(mintthresh);
#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
*/
@@ -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_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
* to sleep or has just been awakened. We use the
* 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
*/
@@ -18,6 +18,7 @@
#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"
@@ -43,6 +44,7 @@
static TCM_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");
@@ -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_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 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();
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();
RvCoreCriticalSleepFrame * frame = s_cpu_retention.retent.critical_frame[core_id];
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
@@ -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));
}
#endif
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame);
}
restore_mstatus(mstatus);
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);
rv_core_noncritical_regs_save();
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];
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
@@ -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);
}
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();
@@ -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
@@ -1,6 +1,10 @@
set(sources "test_app_main.c"
"test_pm.c")
if(CONFIG_SOC_CPU_HAS_FPU AND CONFIG_IDF_TARGET_ARCH_RISCV AND CONFIG_SOC_PM_FPU_RETENTION_BY_SW)
list(APPEND sources "test_fpu_retention.c")
endif()
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component must be registered as a WHOLE_ARCHIVE
idf_component_register(SRCS ${sources}
@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_private/esp_clk.h"
#include "esp_pm.h"
#include "esp_task.h"
#include "soc/soc_caps.h"
#define MHZ (1000 * 1000)
#define FP_EPS 1e-5f
#define FP_CTX_ITERATIONS 20
#define TEST_TASKS 4
/* Context Switch Test */
static struct {
int fail_count;
SemaphoreHandle_t done;
float seed;
} s_fp_ctx[TEST_TASKS];
static void fp_context_task(void *arg)
{
vTaskDelay(2);
int idx = (int)(intptr_t)arg;
volatile float seed = s_fp_ctx[idx].seed;
for (int i = 0; i < FP_CTX_ITERATIONS; i++) {
volatile float v0 = seed * 6.789f;
vTaskDelay(10);
volatile float v1 = v0 + 8.987f;
vTaskDelay(10);
volatile float v2 = v1 * v1;
vTaskDelay(10);
volatile float v3 = sqrt(v2);
vTaskDelay(10);
volatile float v4 = v3 - 8.987f;
vTaskDelay(10);
volatile float result = v4 / 6.789f;
if (fabsf(result - seed) > FP_EPS) {
s_fp_ctx[idx].fail_count++;
}
}
xSemaphoreGive(s_fp_ctx[idx].done);
vTaskDelete(NULL);
}
TEST_CASE("Test PD_CPU lightsleep preserves FP registers", "[rv_fp]")
{
#if CONFIG_PM_ENABLE
int cur_freq_mhz = esp_clk_cpu_freq() / MHZ;
int xtal_freq = esp_clk_xtal_freq() / MHZ;
esp_pm_config_t pm_config = {
.max_freq_mhz = cur_freq_mhz,
.min_freq_mhz = xtal_freq,
.light_sleep_enable = true
};
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
#endif
const float seeds = 3.1415926f;
SemaphoreHandle_t sem = xSemaphoreCreateCounting(TEST_TASKS, 0);
TEST_ASSERT_NOT_NULL(sem);
for (int i = 0; i < TEST_TASKS; i++) {
s_fp_ctx[i].fail_count = 0;
s_fp_ctx[i].done = sem;
s_fp_ctx[i].seed = seeds * i;
TEST_ASSERT_EQUAL(pdPASS, xTaskCreatePinnedToCore(
fp_context_task, "fp", 4096, (void*)(intptr_t)i,
ESP_TASK_MAIN_PRIO, NULL, i % CONFIG_FREERTOS_NUMBER_OF_CORES));
}
for (int i = 0; i < TEST_TASKS; i++) {
TEST_ASSERT_TRUE(xSemaphoreTake(sem, pdMS_TO_TICKS(20000)));
}
vSemaphoreDelete(sem);
int total = 0;
for (int i = 0; i < TEST_TASKS; i++) {
total += s_fp_ctx[i].fail_count;
}
TEST_ASSERT_EQUAL_MESSAGE(0, total, "FPU context corruption detected");
printf("FPU context retention test passed\n");
#if CONFIG_PM_ENABLE
// Disable lightsleep and DFS
pm_config.min_freq_mhz = cur_freq_mhz;
pm_config.light_sleep_enable = false;
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
#endif
}
@@ -6,7 +6,7 @@
*
* 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
* 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)
#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
}
#endif
@@ -6,7 +6,7 @@
*
* 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
* this software and associated documentation files (the "Software"), to deal in
@@ -895,3 +895,9 @@ configRUN_TIME_COUNTER_TYPE xPortGetRunTimeCounterValue( void )
/* ---------------------------------------------- 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:vPortAssertIfInISR (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
@@ -1023,6 +1023,10 @@ config SOC_PM_CPU_RETENTION_BY_SW
bool
default y
config SOC_PM_FPU_RETENTION_BY_SW
bool
default y
config SOC_PM_MODEM_RETENTION_BY_REGDMA
bool
default y
@@ -491,6 +491,7 @@
#define SOC_PM_SUPPORT_MODEM_CLOCK_DOMAIN_ICG (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_CLK_CONF_RETENTION (1) /*!< In esp32H4, i2c lpcon is placed in modem domain*/
#define SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN (1)
@@ -1755,6 +1755,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
@@ -670,6 +670,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_PM_PAU_LINK_NUM (4)