Преглед изворни кода

cpu retention: software cpu retention support for esp32c6

cpu retention: add riscv core sleep critical and non-critical register layout structure definition

cpu retention: add assembly subroutine for cpu critical register backup and restore

cpu retention: add cpu core critical register context backup and restore support

cpu retention: add cpu core non-critical register context backup and restore support

cpu retention: add interrupt priority register context backup and restore support

cpu retention: add cache config register context backup and restore support

cpu retention: add plic interrupt register context backup and restore support

cpu retention: add clint interrupt register context backup and restore support

cpu retention: wait icache state idle before pmu enter sleep
Li Shuai пре 3 година
родитељ
комит
9b99fc9033

+ 8 - 0
components/esp_hw_support/CMakeLists.txt

@@ -73,6 +73,14 @@ if(NOT BOOTLOADER_BUILD)
         list(APPEND srcs "esp_ds.c")
     endif()
 
+    if(CONFIG_SOC_PM_CPU_RETENTION_BY_SW)
+        list(APPEND srcs "sleep_cpu_asm.S")
+        set_property(TARGET ${COMPONENT_LIB}
+                    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")
+    endif()
+
     if(CONFIG_SOC_MODEM_CLOCK_IS_INDEPENDENT)
         list(APPEND srcs "modem_clock.c")
     endif()

+ 8 - 0
components/esp_hw_support/include/esp_private/sleep_cpu.h

@@ -50,6 +50,14 @@ void sleep_disable_cpu_retention(void);
 
 #endif // SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_RTCCNTL
 
+
+#if SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_SW
+
+esp_err_t 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);
+
+#endif // SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_SW
+
 #ifdef __cplusplus
 }
 #endif

+ 409 - 20
components/esp_hw_support/sleep_cpu.c

@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 #include <string.h>
+#include <inttypes.h>
 #include <sys/lock.h>
 #include <sys/param.h>
 
@@ -16,22 +17,55 @@
 #include "freertos/task.h"
 #include "esp_heap_caps.h"
 #include "soc/soc_caps.h"
-#include "hal/rtc_hal.h"
 #include "esp_private/sleep_cpu.h"
 #include "sdkconfig.h"
 
+#if !SOC_PMU_SUPPORTED
+#include "hal/rtc_hal.h"
+#endif
+
+#include "soc/rtc_periph.h"
+
 #ifdef CONFIG_IDF_TARGET_ESP32S3
 #include "esp32s3/rom/cache.h"
+#elif CONFIG_IDF_TARGET_ESP32C6
+#include "esp32c6/rom/rtc.h"
+#include "riscv/rvsleep-frames.h"
+#include "soc/intpri_reg.h"
+#include "soc/extmem_reg.h"
+#include "soc/plic_reg.h"
+#include "soc/clint_reg.h"
+#include "esp32c6/rom/cache.h"
 #endif
 
 static __attribute__((unused)) const char *TAG = "sleep";
 
+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 {
 #if SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_RTCCNTL
     rtc_cntl_sleep_retent_t retent;
+#elif SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_SW
+    struct {
+        RvCoreCriticalSleepFrame *critical_frame;
+        RvCoreNonCriticalSleepFrame *non_critical_frame;
+        cpu_domain_dev_sleep_frame_t *intpri_frame;
+        cpu_domain_dev_sleep_frame_t *cache_config_frame;
+        cpu_domain_dev_sleep_frame_t *plic_frame;
+        cpu_domain_dev_sleep_frame_t *clint_frame;
+    } retent;
 #endif
 } sleep_cpu_retention_t;
 
@@ -40,12 +74,12 @@ static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention;
 #if SOC_PM_SUPPORT_TAGMEM_PD && SOC_PM_CPU_RETENTION_BY_RTCCNTL
 
 #if CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP
-static int cache_tagmem_retention_setup(uint32_t code_seg_vaddr, uint32_t code_seg_size, uint32_t data_seg_vaddr, uint32_t data_seg_size)
+static uint32_t cache_tagmem_retention_setup(uint32_t code_seg_vaddr, uint32_t code_seg_size, uint32_t data_seg_vaddr, uint32_t data_seg_size)
 {
-    int sets;   /* i/d-cache total set counts */
-    int index;  /* virtual address mapping i/d-cache row offset */
-    int waysgrp;
-    int icache_tagmem_blk_gs, dcache_tagmem_blk_gs;
+    uint32_t sets;   /* i/d-cache total set counts */
+    uint32_t index;  /* virtual address mapping i/d-cache row offset */
+    uint32_t waysgrp;
+    uint32_t icache_tagmem_blk_gs, dcache_tagmem_blk_gs;
     struct cache_mode imode = { .icache = 1 };
     struct cache_mode dmode = { .icache = 0 };
 
@@ -117,14 +151,13 @@ static esp_err_t esp_sleep_tagmem_pd_low_init(void)
             uint32_t data_start = SOC_DROM_LOW;
             uint32_t data_size = SOC_EXTRAM_DATA_SIZE;
 #endif
-            ESP_LOGI(TAG, "Code start at %08x, total %.2f KiB, data start at %08x, total %.2f KiB",
-                    code_start, (float)code_size/1024, data_start, (float)data_size/1024);
-            int tagmem_sz = cache_tagmem_retention_setup(code_start, code_size, data_start, data_size);
-            void *buf = heap_caps_aligned_alloc(SOC_RTC_CNTL_TAGMEM_PD_DMA_ADDR_ALIGN,
+            ESP_LOGI(TAG, "Code start at 0x%08"PRIx32", total %"PRIu32", data start at 0x%08"PRIx32", total %"PRIu32" Bytes",
+                    code_start, code_size, data_start, data_size);
+            uint32_t tagmem_sz = cache_tagmem_retention_setup(code_start, code_size, data_start, data_size);
+            void *buf = heap_caps_aligned_calloc(SOC_RTC_CNTL_TAGMEM_PD_DMA_ADDR_ALIGN, 1,
                                                 tagmem_sz + RTC_HAL_DMA_LINK_NODE_SIZE,
                                                 MALLOC_CAP_RETENTION);
             if (buf) {
-                memset(buf, 0, tagmem_sz + RTC_HAL_DMA_LINK_NODE_SIZE);
                 s_cpu_retention.retent.tagmem.link_addr = rtc_cntl_hal_dma_link_init(buf,
                                       buf + RTC_HAL_DMA_LINK_NODE_SIZE, tagmem_sz, NULL);
             } else {
@@ -161,11 +194,10 @@ static esp_err_t esp_sleep_tagmem_pd_low_deinit(void)
 esp_err_t esp_sleep_cpu_pd_low_init(void)
 {
     if (s_cpu_retention.retent.cpu_pd_mem == NULL) {
-        void *buf = heap_caps_aligned_alloc(SOC_RTC_CNTL_CPU_PD_DMA_ADDR_ALIGN,
+        void *buf = heap_caps_aligned_calloc(SOC_RTC_CNTL_CPU_PD_DMA_ADDR_ALIGN, 1,
                                             SOC_RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE + RTC_HAL_DMA_LINK_NODE_SIZE,
                                             MALLOC_CAP_RETENTION);
         if (buf) {
-            memset(buf, 0, SOC_RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE + RTC_HAL_DMA_LINK_NODE_SIZE);
             s_cpu_retention.retent.cpu_pd_mem = rtc_cntl_hal_dma_link_init(buf,
                                   buf + RTC_HAL_DMA_LINK_NODE_SIZE, SOC_RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE, NULL);
         } else {
@@ -204,25 +236,371 @@ esp_err_t esp_sleep_cpu_pd_low_deinit(void)
 
 void sleep_enable_cpu_retention(void)
 {
-#if SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_REGDMA
     rtc_cntl_hal_enable_cpu_retention(&s_cpu_retention.retent);
-#endif
-#if SOC_PM_SUPPORT_TAGMEM_PD && SOC_PM_CPU_RETENTION_BY_REGDMA
+
+#if SOC_PM_SUPPORT_TAGMEM_PD
     rtc_cntl_hal_enable_tagmem_retention(&s_cpu_retention.retent);
 #endif
 }
 
 void IRAM_ATTR sleep_disable_cpu_retention(void)
 {
-#if SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_REGDMA
     rtc_cntl_hal_disable_cpu_retention(&s_cpu_retention.retent);
-#endif
-#if SOC_PM_SUPPORT_TAGMEM_PD && SOC_PM_CPU_RETENTION_BY_REGDMA
+
+#if SOC_PM_SUPPORT_TAGMEM_PD
     rtc_cntl_hal_disable_tagmem_retention(&s_cpu_retention.retent);
 #endif
 }
 
-#endif // SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_REGDMA
+#endif
+
+
+#if SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_SW
+
+#define CUSTOM_CSR_PCER_MACHINE        0x7e0
+#define CUSTOM_CSR_PCMR_MACHINE        0x7e1
+#define CUSTOM_CSR_PCCR_MACHINE        0x7e2
+#define CUSTOM_CSR_CPU_TESTBUS_CTRL    0x7e3
+#define CUSTOM_CSR_PCER_USER           0x800
+#define CUSTOM_CSR_PCMR_USER           0x801
+#define CUSTOM_CSR_PCCR_USER           0x802
+#define CUSTOM_CSR_GPIO_OEN_USER       0x803
+#define CUSTOM_CSR_GPIO_IN_USER        0x804
+#define CUSTOM_CSR_GPIO_OUT_USER       0x805
+#define CUSTOM_CSR_CO_EXCEPTION_CAUSE  0x7f0
+#define CUSTOM_CSR_CO_HWLP             0x7f1
+#define CUSTOM_CSR_CO_AIA              0x7f2
+
+extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame;
+
+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_intpri_sleep_frame_alloc_and_init(void)
+{
+    const static cpu_domain_dev_regs_region_t regions[] = {
+        { .start = INTPRI_CORE0_CPU_INT_ENABLE_REG, .end = INTPRI_RND_ECO_LOW_REG + 4 },
+        { .start = INTPRI_RND_ECO_HIGH_REG, .end = INTPRI_RND_ECO_HIGH_REG + 4 }
+    };
+    return cpu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
+}
+
+static inline void * cpu_domain_cache_config_sleep_frame_alloc_and_init(void)
+{
+    const static cpu_domain_dev_regs_region_t regions[] = {
+        { .start = EXTMEM_DCACHE_CTRL_REG, .end = EXTMEM_DCACHE_CTRL_REG + 4 },
+        { .start = EXTMEM_CACHE_WRAP_AROUND_CTRL_REG, .end = EXTMEM_CACHE_WRAP_AROUND_CTRL_REG + 4 }
+    };
+    return cpu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
+}
+
+static inline void * cpu_domain_plic_sleep_frame_alloc_and_init(void)
+{
+    const static cpu_domain_dev_regs_region_t regions[] = {
+        { .start = PLIC_MXINT_ENABLE_REG, .end = PLIC_MXINT_CLAIM_REG + 4 },
+        { .start = PLIC_MXINT_CONF_REG,   .end = PLIC_MXINT_CONF_REG + 4  },
+        { .start = PLIC_UXINT_ENABLE_REG, .end = PLIC_UXINT_CLAIM_REG + 4 },
+        { .start = PLIC_UXINT_CONF_REG,   .end = PLIC_UXINT_CONF_REG + 4  }
+    };
+    return cpu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
+}
+
+static inline void * cpu_domain_clint_sleep_frame_alloc_and_init(void)
+{
+    const static cpu_domain_dev_regs_region_t regions[] = {
+        { .start = CLINT_MINT_SIP_REG, .end = CLINT_MINT_MTIMECMP_H_REG + 4 },
+        { .start = CLINT_UINT_SIP_REG, .end = CLINT_UINT_UTIMECMP_H_REG + 4 }
+    };
+    return cpu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
+}
+
+static esp_err_t esp_sleep_cpu_retention_init_impl(void)
+{
+    if (s_cpu_retention.retent.critical_frame == NULL) {
+        void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
+        if (frame == NULL) {
+            goto err;
+        }
+        s_cpu_retention.retent.critical_frame = (RvCoreCriticalSleepFrame *)frame;
+        rv_core_critical_regs_frame = (RvCoreCriticalSleepFrame *)frame;
+    }
+    if (s_cpu_retention.retent.non_critical_frame == NULL) {
+        void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
+        if (frame == NULL) {
+            goto err;
+        }
+        s_cpu_retention.retent.non_critical_frame = (RvCoreNonCriticalSleepFrame *)frame;
+    }
+    if (s_cpu_retention.retent.intpri_frame == NULL) {
+        void *frame = cpu_domain_intpri_sleep_frame_alloc_and_init();
+        if (frame == NULL) {
+            goto err;
+        }
+        s_cpu_retention.retent.intpri_frame = (cpu_domain_dev_sleep_frame_t *)frame;
+    }
+    if (s_cpu_retention.retent.cache_config_frame == NULL) {
+        void *frame = cpu_domain_cache_config_sleep_frame_alloc_and_init();
+        if (frame == NULL) {
+            goto err;
+        }
+        s_cpu_retention.retent.cache_config_frame = (cpu_domain_dev_sleep_frame_t *)frame;
+    }
+    if (s_cpu_retention.retent.plic_frame == NULL) {
+        void *frame = cpu_domain_plic_sleep_frame_alloc_and_init();
+        if (frame == NULL) {
+            goto err;
+        }
+        s_cpu_retention.retent.plic_frame = (cpu_domain_dev_sleep_frame_t *)frame;
+    }
+    if (s_cpu_retention.retent.clint_frame == NULL) {
+        void *frame = cpu_domain_clint_sleep_frame_alloc_and_init();
+        if (frame == NULL) {
+            goto err;
+        }
+        s_cpu_retention.retent.clint_frame = (cpu_domain_dev_sleep_frame_t *)frame;
+    }
+    return ESP_OK;
+err:
+    esp_sleep_cpu_retention_deinit();
+    return ESP_ERR_NO_MEM;
+}
+
+static esp_err_t esp_sleep_cpu_retention_deinit_impl(void)
+{
+    if (s_cpu_retention.retent.critical_frame) {
+        heap_caps_free((void *)s_cpu_retention.retent.critical_frame);
+        s_cpu_retention.retent.critical_frame = NULL;
+        rv_core_critical_regs_frame = NULL;
+    }
+    if (s_cpu_retention.retent.non_critical_frame) {
+        heap_caps_free((void *)s_cpu_retention.retent.non_critical_frame);
+        s_cpu_retention.retent.non_critical_frame = NULL;
+    }
+    if (s_cpu_retention.retent.intpri_frame) {
+        heap_caps_free((void *)s_cpu_retention.retent.intpri_frame);
+        s_cpu_retention.retent.intpri_frame = NULL;
+    }
+    if (s_cpu_retention.retent.cache_config_frame) {
+        heap_caps_free((void *)s_cpu_retention.retent.cache_config_frame);
+        s_cpu_retention.retent.cache_config_frame = NULL;
+    }
+    if (s_cpu_retention.retent.plic_frame) {
+        heap_caps_free((void *)s_cpu_retention.retent.plic_frame);
+        s_cpu_retention.retent.plic_frame = NULL;
+    }
+    if (s_cpu_retention.retent.clint_frame) {
+        heap_caps_free((void *)s_cpu_retention.retent.clint_frame);
+        s_cpu_retention.retent.clint_frame = NULL;
+    }
+    return ESP_OK;
+}
+
+static inline IRAM_ATTR uint32_t save_mstatus_and_disable_global_int(void)
+{
+    uint32_t mstatus;
+    __asm__ __volatile__ (
+            "csrr   %0, mstatus\n"
+            "csrci  mstatus, 0x8\n"
+            : "=r"(mstatus)
+        );
+    return mstatus;
+}
+
+static inline IRAM_ATTR void restore_mstatus(uint32_t mstatus)
+{
+    __asm__ __volatile__ ("csrw mstatus, %0\n" :: "r"(mstatus));
+}
+
+static IRAM_ATTR RvCoreNonCriticalSleepFrame * rv_core_noncritical_regs_save(void)
+{
+    assert(s_cpu_retention.retent.non_critical_frame);
+    RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame;
+    frame->mscratch  = RV_READ_CSR(mscratch);
+    frame->mideleg   = RV_READ_CSR(mideleg);
+    frame->misa      = RV_READ_CSR(misa);
+    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->pmpcfg0   = RV_READ_CSR(pmpcfg0);
+    frame->pmpcfg1   = RV_READ_CSR(pmpcfg1);
+    frame->pmpcfg2   = RV_READ_CSR(pmpcfg2);
+    frame->pmpcfg3   = RV_READ_CSR(pmpcfg3);
+    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->utvec     = RV_READ_CSR(utvec);
+    frame->ustatus   = RV_READ_CSR(ustatus);
+    frame->uepc      = RV_READ_CSR(uepc);
+    frame->ucause    = RV_READ_CSR(ucause);
+
+    frame->mpcer     = RV_READ_CSR(CUSTOM_CSR_PCER_MACHINE);
+    frame->mpcmr     = RV_READ_CSR(CUSTOM_CSR_PCMR_MACHINE);
+    frame->mpccr     = RV_READ_CSR(CUSTOM_CSR_PCCR_MACHINE);
+    frame->cpu_testbus_ctrl = RV_READ_CSR(CUSTOM_CSR_CPU_TESTBUS_CTRL);
+    frame->upcer     = RV_READ_CSR(CUSTOM_CSR_PCER_USER);
+    frame->upcmr     = RV_READ_CSR(CUSTOM_CSR_PCMR_USER);
+    frame->upccr     = RV_READ_CSR(CUSTOM_CSR_PCCR_USER);
+    frame->ugpio_oen = RV_READ_CSR(CUSTOM_CSR_GPIO_OEN_USER);
+    frame->ugpio_in  = RV_READ_CSR(CUSTOM_CSR_GPIO_IN_USER);
+    frame->ugpio_out = RV_READ_CSR(CUSTOM_CSR_GPIO_OUT_USER);
+    return frame;
+}
+
+static IRAM_ATTR void rv_core_noncritical_regs_restore(RvCoreNonCriticalSleepFrame *frame)
+{
+    assert(frame);
+    RV_WRITE_CSR(mscratch, frame->mscratch);
+    RV_WRITE_CSR(mideleg,  frame->mideleg);
+    RV_WRITE_CSR(misa,     frame->misa);
+    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(pmpcfg0,  frame->pmpcfg0);
+    RV_WRITE_CSR(pmpcfg1,  frame->pmpcfg1);
+    RV_WRITE_CSR(pmpcfg2,  frame->pmpcfg2);
+    RV_WRITE_CSR(pmpcfg3,  frame->pmpcfg3);
+    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(utvec,    frame->utvec);
+    RV_WRITE_CSR(ustatus,  frame->ustatus);
+    RV_WRITE_CSR(uepc,     frame->uepc);
+    RV_WRITE_CSR(ucause,   frame->ucause);
+
+    RV_WRITE_CSR(CUSTOM_CSR_PCER_MACHINE, frame->mpcer);
+    RV_WRITE_CSR(CUSTOM_CSR_PCMR_MACHINE, frame->mpcmr);
+    RV_WRITE_CSR(CUSTOM_CSR_PCCR_MACHINE, frame->mpccr);
+    RV_WRITE_CSR(CUSTOM_CSR_CPU_TESTBUS_CTRL, frame->cpu_testbus_ctrl);
+    RV_WRITE_CSR(CUSTOM_CSR_PCER_USER,    frame->upcer);
+    RV_WRITE_CSR(CUSTOM_CSR_PCMR_USER,    frame->upcmr);
+    RV_WRITE_CSR(CUSTOM_CSR_PCCR_USER,    frame->upccr);
+    RV_WRITE_CSR(CUSTOM_CSR_GPIO_OEN_USER,frame->ugpio_oen);
+    RV_WRITE_CSR(CUSTOM_CSR_GPIO_IN_USER, frame->ugpio_in);
+    RV_WRITE_CSR(CUSTOM_CSR_GPIO_OUT_USER,frame->ugpio_out);
+}
+
+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++];
+        }
+    }
+}
+
+extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void);
+extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(void);
+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)
+{
+    RvCoreCriticalSleepFrame * frame = rv_core_critical_regs_save();
+    if ((frame->pmufunc & 0x3) == 0x1) {
+        REG_CLR_BIT(SLEEP_MODE_REG, BIT(0));    /* Tell rom to run light sleep wake stub */
+        REG_WRITE(LIGHT_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore);
+        return (*goto_sleep)(wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
+    }
+
+    return ESP_OK;
+}
+
+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)
+{
+    uint32_t mstatus = save_mstatus_and_disable_global_int();
+
+    /* wait cache idle */
+    Cache_Freeze_ICache_Enable(CACHE_FREEZE_ACK_BUSY);
+    Cache_Freeze_ICache_Disable();
+
+    cpu_domain_dev_regs_save(s_cpu_retention.retent.plic_frame);
+    cpu_domain_dev_regs_save(s_cpu_retention.retent.clint_frame);
+    cpu_domain_dev_regs_save(s_cpu_retention.retent.intpri_frame);
+    cpu_domain_dev_regs_save(s_cpu_retention.retent.cache_config_frame);
+    RvCoreNonCriticalSleepFrame *frame = rv_core_noncritical_regs_save();
+
+    esp_err_t err = do_cpu_retention(goto_sleep, wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
+
+    rv_core_noncritical_regs_restore(frame);
+    cpu_domain_dev_regs_restore(s_cpu_retention.retent.cache_config_frame);
+    cpu_domain_dev_regs_restore(s_cpu_retention.retent.intpri_frame);
+    cpu_domain_dev_regs_restore(s_cpu_retention.retent.clint_frame);
+    cpu_domain_dev_regs_restore(s_cpu_retention.retent.plic_frame);
+
+    restore_mstatus(mstatus);
+    return err;
+}
+
+#endif // SOC_PM_SUPPORT_CPU_PD && SOC_PM_CPU_RETENTION_BY_SW
 
 
 #if SOC_PM_SUPPORT_CPU_PD
@@ -232,6 +610,8 @@ esp_err_t esp_sleep_cpu_retention_init(void)
     esp_err_t err = ESP_OK;
 #if SOC_PM_CPU_RETENTION_BY_RTCCNTL
     err = esp_sleep_cpu_pd_low_init();
+#elif SOC_PM_CPU_RETENTION_BY_SW
+    err = esp_sleep_cpu_retention_init_impl();
 #endif
     return err;
 }
@@ -241,6 +621,8 @@ esp_err_t esp_sleep_cpu_retention_deinit(void)
     esp_err_t err = ESP_OK;
 #if SOC_PM_CPU_RETENTION_BY_RTCCNTL
     err = esp_sleep_cpu_pd_low_deinit();
+#elif SOC_PM_CPU_RETENTION_BY_SW
+    err = esp_sleep_cpu_retention_deinit_impl();
 #endif
     return err;
 }
@@ -249,6 +631,13 @@ bool cpu_domain_pd_allowed(void)
 {
 #if SOC_PM_CPU_RETENTION_BY_RTCCNTL
     return (s_cpu_retention.retent.cpu_pd_mem != NULL);
+#elif SOC_PM_CPU_RETENTION_BY_SW
+    return (s_cpu_retention.retent.critical_frame != NULL) && \
+         (s_cpu_retention.retent.non_critical_frame != NULL) && \
+         (s_cpu_retention.retent.intpri_frame != NULL) && \
+         (s_cpu_retention.retent.cache_config_frame != NULL) && \
+         (s_cpu_retention.retent.plic_frame != NULL) && \
+         (s_cpu_retention.retent.clint_frame != NULL);
 #else
     return false;
 #endif

+ 239 - 0
components/esp_hw_support/sleep_cpu_asm.S

@@ -0,0 +1,239 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "soc/soc.h"
+#include "riscv/rvsleep-frames.h"
+#include "soc/soc_caps.h"
+#include "sdkconfig.h"
+
+#if !CONFIG_IDF_TARGET_ESP32C6
+#include "soc/lp_aon_reg.h"
+#include "soc/extmem_reg.h"
+#endif
+
+    .section    .data1,"aw"
+    .global     rv_core_critical_regs_frame
+    .type       rv_core_critical_regs_frame,@object
+    .align      4
+rv_core_critical_regs_frame:
+    .word       0
+
+/*
+--------------------------------------------------------------------------------
+    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      t0, rv_core_critical_regs_frame
+    lw      t0, 0(t0)           /* 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)
+    sw      a0, RV_SLP_CTX_A0(t0)
+
+    /* !! WARNING, do not use the a0 register below, a0 carries important sleep
+     * information and will be returned as the return value !! */
+    mv      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)
+
+    /*
+    !!! 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)
+
+#if !CONFIG_IDF_TARGET_ESP32C6
+    /* writeback dcache is required here!!! */
+    la      t0, EXTMEM_CACHE_SYNC_MAP_REG
+    li      t1, 0x10
+    sw      t1, 0x0(t0)                     /* set EXTMEM_CACHE_SYNC_MAP_REG bit 4 */
+    la      t2, EXTMEM_CACHE_SYNC_ADDR_REG
+    sw      zero, 0x0(t2)                   /* clear EXTMEM_CACHE_SYNC_ADDR_REG */
+    la      t0, EXTMEM_CACHE_SYNC_SIZE_REG
+    sw      zero, 0x0(t0)                   /* clear EXTMEM_CACHE_SYNC_SIZE_REG */
+
+    la      t1, EXTMEM_CACHE_SYNC_CTRL_REG
+    lw      t2, 0x0(t1)
+    ori     t2, t2, 0x4
+    sw      t2, 0x0(t1)
+
+    li      t0, 0x10                        /* SYNC_DONE bit */
+wait_sync_done:
+    lw      t2, 0x0(t1)
+    and     t2, t0, t2
+    beqz    t2, wait_sync_done
+#endif
+
+    lw      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
+
+
+#define CSR_PCER_U              0x800
+#define CSR_PCMR_U              0x801
+#define PCER_CYCLES             (1<<0)  /* count clock cycles */
+#define PCMR_GLOBAL_EN          (1<<0)  /* enable count */
+#define pcer                    CSR_PCER_U
+#define pcmr                    CSR_PCMR_U
+
+/*
+--------------------------------------------------------------------------------
+    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
+    .type       rv_core_critical_regs_restore,@function
+    .align      4
+
+rv_core_critical_regs_restore:
+
+    la      t0, rv_core_critical_regs_frame
+    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_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

+ 22 - 18
components/esp_rom/include/esp32c6/rom/rtc.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -32,7 +32,7 @@ extern "C" {
   *          Please do not use reserved or used rtc memory or registers.              *
   *                                                                                   *
   *************************************************************************************
-  *                          RTC  Memory & Store Register usage
+  *                          LP  Memory & Store Register usage
   *************************************************************************************
   *     rtc memory addr         type    size            usage
   *     0x3f421000(0x50000000)  Slow    SIZE_CP         Co-Processor code/Reset Entry
@@ -42,25 +42,29 @@ extern "C" {
   *
   *************************************************************************************
   *     RTC store registers     usage
-  *     RTC_CNTL_STORE0_REG     Reserved
-  *     RTC_CNTL_STORE1_REG     RTC_SLOW_CLK calibration value
-  *     RTC_CNTL_STORE2_REG     Boot time, low word
-  *     RTC_CNTL_STORE3_REG     Boot time, high word
-  *     RTC_CNTL_STORE4_REG     External XTAL frequency
-  *     RTC_CNTL_STORE5_REG     APB bus frequency
-  *     RTC_CNTL_STORE6_REG     FAST_RTC_MEMORY_ENTRY
-  *     RTC_CNTL_STORE7_REG     FAST_RTC_MEMORY_CRC
+  *     LP_AON_STORE0_REG       Reserved
+  *     LP_AON_STORE1_REG       RTC_SLOW_CLK calibration value
+  *     LP_AON_STORE2_REG       Boot time, low word
+  *     LP_AON_STORE3_REG       Boot time, high word
+  *     LP_AON_STORE4_REG       External XTAL frequency
+  *     LP_AON_STORE5_REG       FAST_RTC_MEMORY_LENGTH
+  *     LP_AON_STORE6_REG       FAST_RTC_MEMORY_ENTRY
+  *     LP_AON_STORE7_REG       FAST_RTC_MEMORY_CRC
+  *     LP_AON_STORE8_REG       Store light sleep wake stub addr
+  *     LP_AON_STORE9_REG       Store the sleep mode at bit[0]  (0:light sleep 1:deep sleep)
   *************************************************************************************
   */
 
-#define RTC_SLOW_CLK_CAL_REG    LP_AON_STORE1_REG
-#define RTC_BOOT_TIME_LOW_REG   LP_AON_STORE2_REG
-#define RTC_BOOT_TIME_HIGH_REG  LP_AON_STORE3_REG
-#define RTC_XTAL_FREQ_REG       LP_AON_STORE4_REG
-#define RTC_APB_FREQ_REG        LP_AON_STORE5_REG
-#define RTC_ENTRY_ADDR_REG      LP_AON_STORE6_REG
-#define RTC_RESET_CAUSE_REG     LP_AON_STORE6_REG
-#define RTC_MEMORY_CRC_REG      LP_AON_STORE7_REG
+#define RTC_SLOW_CLK_CAL_REG                LP_AON_STORE1_REG
+#define RTC_BOOT_TIME_LOW_REG               LP_AON_STORE2_REG
+#define RTC_BOOT_TIME_HIGH_REG              LP_AON_STORE3_REG
+#define RTC_XTAL_FREQ_REG                   LP_AON_STORE4_REG
+#define RTC_ENTRY_LENGTH_REG                LP_AON_STORE5_REG
+#define RTC_ENTRY_ADDR_REG                  LP_AON_STORE6_REG
+#define RTC_RESET_CAUSE_REG                 LP_AON_STORE6_REG
+#define RTC_MEMORY_CRC_REG                  LP_AON_STORE7_REG
+#define LIGHT_SLEEP_WAKE_STUB_ADDR_REG      LP_AON_STORE8_REG
+#define SLEEP_MODE_REG                      LP_AON_STORE9_REG
 
 #define RTC_DISABLE_ROM_LOG ((1 << 0) | (1 << 16)) //!< Disable logging from the ROM code.
 

+ 153 - 0
components/riscv/include/riscv/rvsleep-frames.h

@@ -0,0 +1,153 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __RVSLEEP_FRAMES_H__
+#define __RVSLEEP_FRAMES_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_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 infomation, 3 means
+                                                             * being awakened, 1 means going to sleep */
+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_MIDELEG,          mideleg)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_MISA,             misa)
+    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_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_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_UTVEC,            utvec)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_USTATUS,          ustatus)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UEPC,             uepc)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UCAUSE,           ucause)
+
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_MPCER,            mpcer)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_MPCMR,            mpcmr)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_MPCCR,            mpccr)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_CPU_TESTBUS_CTRL, cpu_testbus_ctrl)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UPCER,            upcer)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UPCMR,            upcmr)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UPCCR,            upccr)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_OEN,        ugpio_oen)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_IN,         ugpio_in)
+    STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_OUT,        ugpio_out)
+STRUCT_END(RvCoreNonCriticalSleepFrame)
+
+#endif /* #ifndef __RVSLEEP_FRAMES_H__ */

+ 1 - 0
tools/ci/check_public_headers_exceptions.txt

@@ -156,3 +156,4 @@ components/espcoredump/include/port/xtensa/esp_core_dump_summary_port.h
 components/riscv/include/esp_private/panic_reason.h
 components/riscv/include/riscv/interrupt.h
 components/riscv/include/riscv/rvruntime-frames.h
+components/riscv/include/riscv/rvsleep-frames.h