From 0ce211539a166326bbfefbe356244be13bea22c6 Mon Sep 17 00:00:00 2001 From: Guillaume Souchere Date: Wed, 22 Apr 2026 11:42:57 +0200 Subject: [PATCH] fix(esp_system): PSRAM stack crash in esp_restart_noos on RISC-V chips On RISC-V chips with SPIRAM support (esp32c5, esp32c61, esp32h4, esp32p4, esp32s31), esp_restart_noos() was disabling the cache while the current stack pointer could still be in external RAM. Any stack access after cache disable (function call, local variable spill) would then fault with "Cache disabled but cached memory region accessed". Xtensa chips (esp32, esp32s2, esp32s3) already had this guard via the SET_STACK macro. Add the equivalent for RISC-V: - Add rv_utils_set_sp() to riscv/rv_utils.h (plain "mv sp, %0" with a memory clobber; no window register management needed on RISC-V) - In each affected system_internal.c, under CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM, check whether the current SP is in PSRAM and if so switch it to the top of internal BSS before any cache disable or writeback operation - Fix xTaskCreateStaticPinnedToCore() stack size argument in the spiram_stack test (was passing bytes instead of word count) - Mark the null-pointer write in func_do_exception() as volatile to prevent the compiler from optimizing it away - Extend the [spiram_stack] tests to RISC-V by sharing fibonacci() and the task/finish helpers across architectures, guarding only the Xtensa-specific WINDOWBASE/WINDOWSTART prints --- .../port/soc/esp32c5/system_internal.c | 43 +++++--- .../port/soc/esp32c61/system_internal.c | 41 +++++-- .../port/soc/esp32h4/system_internal.c | 97 ++++++++++------- .../port/soc/esp32p4/system_internal.c | 97 ++++++++++------- .../port/soc/esp32s31/system_internal.c | 100 +++++++++++------- .../main/test_reset_reason.c | 86 ++++++++++----- components/riscv/include/riscv/rv_utils.h | 13 ++- 7 files changed, 311 insertions(+), 166 deletions(-) diff --git a/components/esp_system/port/soc/esp32c5/system_internal.c b/components/esp_system/port/soc/esp32c5/system_internal.c index 83228327566..1ab861d269c 100644 --- a/components/esp_system/port/soc/esp32c5/system_internal.c +++ b/components/esp_system/port/soc/esp32c5/system_internal.c @@ -29,6 +29,10 @@ #include "hal/modem_lpcon_ll.h" #endif #include "esp_private/cache_err_int.h" +#include "esp_memory_utils.h" + +#define ALIGN_DOWN(val, align) ((val) & ~((align) - 1)) +extern int _bss_end; #include "esp32c5/rom/cache.h" #include "esp32c5/rom/rtc.h" @@ -101,6 +105,24 @@ void esp_system_reset_modules_on_exit(void) uart_ll_sclk_enable(&UART0); } +static void IRAM_ATTR __attribute__((noinline, noreturn)) esp_restart_noos_inner(void) +{ + // Disable cache + Cache_Disable_Cache(); + + esp_system_reset_modules_on_exit(); + + // Set CPU back to XTAL source, same as hard reset, but keep BBPLL on so that USB Serial JTAG can log at 1st stage bootloader. +#if !CONFIG_IDF_ENV_FPGA + rtc_clk_cpu_set_to_default_config(); +#endif + + // Reset PRO CPU + esp_rom_software_reset_cpu(0); + + ESP_INFINITE_LOOP(); +} + /* "inner" restart function for after RTOS, interrupts & anything else on this * core are already stopped. Stalls other core, resets hardware, * triggers restart. @@ -137,18 +159,15 @@ void esp_restart_noos(void) wdt_hal_write_protect_enable(&wdt1_context); #endif /* SOC_WDT_SUPPORTED */ - // Disable cache - Cache_Disable_Cache(); - - esp_system_reset_modules_on_exit(); - - // Set CPU back to XTAL source, same as hard reset, but keep BBPLL on so that USB Serial JTAG can log at 1st stage bootloader. -#if !CONFIG_IDF_ENV_FPGA - rtc_clk_cpu_set_to_default_config(); +#ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM + if (esp_ptr_external_ram(esp_cpu_get_sp())) { + // If stack is in external RAM (CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM), switch SP to + // internal RAM before disabling the cache to avoid a "Cache disabled but cached memory + // region accessed" crash. + uint32_t new_sp = ALIGN_DOWN((uint32_t)&_bss_end, 16); + rv_utils_set_sp((void *)new_sp); + } #endif - // Reset PRO CPU - esp_rom_software_reset_cpu(0); - - ESP_INFINITE_LOOP(); + esp_restart_noos_inner(); } diff --git a/components/esp_system/port/soc/esp32c61/system_internal.c b/components/esp_system/port/soc/esp32c61/system_internal.c index 02a3ec8b9b4..5e703bc3927 100644 --- a/components/esp_system/port/soc/esp32c61/system_internal.c +++ b/components/esp_system/port/soc/esp32c61/system_internal.c @@ -26,6 +26,10 @@ #endif #include "hal/uart_ll.h" #include "esp_private/cache_err_int.h" +#include "esp_memory_utils.h" + +#define ALIGN_DOWN(val, align) ((val) & ~((align) - 1)) +extern int _bss_end; #if SOC_MODEM_CLOCK_SUPPORTED #include "hal/modem_syscon_ll.h" @@ -102,6 +106,23 @@ void esp_system_reset_modules_on_exit(void) uart_ll_sclk_enable(&UART0); } +static void IRAM_ATTR __attribute__((noinline, noreturn)) esp_restart_noos_inner(void) +{ + // Disable cache + Cache_Disable_Cache(); + + esp_system_reset_modules_on_exit(); + + // Set CPU back to XTAL source, same as hard reset, but keep BBPLL on so that USB Serial JTAG can log at 1st stage bootloader. +#if !CONFIG_IDF_ENV_FPGA + rtc_clk_cpu_set_to_default_config(); +#endif + + // Reset PRO CPU + esp_rom_software_reset_cpu(0); + ESP_INFINITE_LOOP(); +} + /* "inner" restart function for after RTOS, interrupts & anything else on this * core are already stopped. Stalls other core, resets hardware, * triggers restart. @@ -138,17 +159,15 @@ void esp_restart_noos(void) wdt_hal_write_protect_enable(&wdt1_context); #endif /* SOC_WDT_SUPPORTED */ - // Disable cache - Cache_Disable_Cache(); - - esp_system_reset_modules_on_exit(); - - // Set CPU back to XTAL source, same as hard reset, but keep BBPLL on so that USB Serial JTAG can log at 1st stage bootloader. -#if !CONFIG_IDF_ENV_FPGA - rtc_clk_cpu_set_to_default_config(); +#ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM + if (esp_ptr_external_ram(esp_cpu_get_sp())) { + // If stack is in external RAM (CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM), switch SP to + // internal RAM before disabling the cache to avoid a "Cache disabled but cached memory + // region accessed" crash. + uint32_t new_sp = ALIGN_DOWN((uint32_t)&_bss_end, 16); + rv_utils_set_sp((void *)new_sp); + } #endif - // Reset PRO CPU - esp_rom_software_reset_cpu(0); - ESP_INFINITE_LOOP(); + esp_restart_noos_inner(); } diff --git a/components/esp_system/port/soc/esp32h4/system_internal.c b/components/esp_system/port/soc/esp32h4/system_internal.c index bab0dd57698..9b765e5843a 100644 --- a/components/esp_system/port/soc/esp32h4/system_internal.c +++ b/components/esp_system/port/soc/esp32h4/system_internal.c @@ -24,6 +24,10 @@ #include "hal/wdt_hal.h" #endif #include "hal/uart_ll.h" +#include "esp_memory_utils.h" + +#define ALIGN_DOWN(val, align) ((val) & ~((align) - 1)) +extern int _bss_end; #include "esp32h4/rom/cache.h" #include "esp32h4/rom/ets_sys.h" @@ -87,46 +91,9 @@ void esp_system_reset_modules_on_exit(void) uart_ll_sclk_enable(&UART0); } -/* "inner" restart function for after RTOS, interrupts & anything else on this - * core are already stopped. Stalls other core, resets hardware, - * triggers restart. -*/ -void esp_restart_noos(void) +static void IRAM_ATTR __attribute__((noinline, noreturn)) esp_restart_noos_inner(void) { - // Disable interrupts - rv_utils_intr_global_disable(); -#if SOC_RTC_WDT_SUPPORTED - // Enable RTC watchdog for 1 second - wdt_hal_context_t rtc_wdt_ctx; - wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); - uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); - wdt_hal_write_protect_disable(&rtc_wdt_ctx); - wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM); - wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC); - //Enable flash boot mode so that flash booting after restart is protected by the RTC WDT. - wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true); - wdt_hal_write_protect_enable(&rtc_wdt_ctx); -#endif /* SOC_RTC_WDT_SUPPORTED */ - const uint32_t core_id = esp_cpu_get_core_id(); -#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE - const uint32_t other_core_id = (core_id == 0) ? 1 : 0; - esp_cpu_reset(other_core_id); - esp_cpu_stall(other_core_id); -#endif - -#if SOC_WDT_SUPPORTED - // Disable TG0/TG1 watchdogs - wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; - wdt_hal_write_protect_disable(&wdt0_context); - wdt_hal_disable(&wdt0_context); - wdt_hal_write_protect_enable(&wdt0_context); - - wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1}; - wdt_hal_write_protect_disable(&wdt1_context); - wdt_hal_disable(&wdt1_context); - wdt_hal_write_protect_enable(&wdt1_context); -#endif /* SOC_WDT_SUPPORTED */ // Disable cache Cache_Disable_Cache(CACHE_MAP_ALL); @@ -162,3 +129,57 @@ void esp_restart_noos(void) #endif ESP_INFINITE_LOOP(); } + +/* "inner" restart function for after RTOS, interrupts & anything else on this + * core are already stopped. Stalls other core, resets hardware, + * triggers restart. +*/ +void esp_restart_noos(void) +{ + // Disable interrupts + rv_utils_intr_global_disable(); +#if SOC_RTC_WDT_SUPPORTED + // Enable RTC watchdog for 1 second + wdt_hal_context_t rtc_wdt_ctx; + wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); + uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM); + wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC); + //Enable flash boot mode so that flash booting after restart is protected by the RTC WDT. + wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); +#endif /* SOC_RTC_WDT_SUPPORTED */ + +#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE + const uint32_t core_id = esp_cpu_get_core_id(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); +#endif + +#if SOC_WDT_SUPPORTED + // Disable TG0/TG1 watchdogs + wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; + wdt_hal_write_protect_disable(&wdt0_context); + wdt_hal_disable(&wdt0_context); + wdt_hal_write_protect_enable(&wdt0_context); + + wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1}; + wdt_hal_write_protect_disable(&wdt1_context); + wdt_hal_disable(&wdt1_context); + wdt_hal_write_protect_enable(&wdt1_context); +#endif /* SOC_WDT_SUPPORTED */ + +#ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM + if (esp_ptr_external_ram(esp_cpu_get_sp())) { + // If stack is in external RAM (CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM), switch SP to + // internal RAM before disabling the cache to avoid a "Cache disabled but cached memory + // region accessed" crash. + uint32_t new_sp = ALIGN_DOWN((uint32_t)&_bss_end, 16); + rv_utils_set_sp((void *)new_sp); + } +#endif + + esp_restart_noos_inner(); +} diff --git a/components/esp_system/port/soc/esp32p4/system_internal.c b/components/esp_system/port/soc/esp32p4/system_internal.c index 6e9de0ffc63..5456d680631 100644 --- a/components/esp_system/port/soc/esp32p4/system_internal.c +++ b/components/esp_system/port/soc/esp32p4/system_internal.c @@ -37,6 +37,10 @@ #include "hal/dw_gdma_ll.h" #include "hal/dma2d_ll.h" #include "hal/efuse_hal.h" +#include "esp_memory_utils.h" + +#define ALIGN_DOWN(val, align) ((val) & ~((align) - 1)) +extern int _bss_end; void esp_system_reset_modules_on_exit(void) { @@ -153,46 +157,9 @@ void esp_system_reset_modules_on_exit(void) #endif } -/* "inner" restart function for after RTOS, interrupts & anything else on this - * core are already stopped. Stalls other core, resets hardware, - * triggers restart. -*/ -void esp_restart_noos(void) +static void IRAM_ATTR __attribute__((noinline, noreturn)) esp_restart_noos_inner(void) { - // Disable interrupts - rv_utils_intr_global_disable(); -#if SOC_RTC_WDT_SUPPORTED - // Enable RTC watchdog for 1 second - wdt_hal_context_t rtc_wdt_ctx; - wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); - uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); - wdt_hal_write_protect_disable(&rtc_wdt_ctx); - wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM); - wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC); - //Enable flash boot mode so that flash booting after restart is protected by the RTC WDT. - wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true); - wdt_hal_write_protect_enable(&rtc_wdt_ctx); -#endif /* SOC_RTC_WDT_SUPPORTED */ - const uint32_t core_id = esp_cpu_get_core_id(); -#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE - const uint32_t other_core_id = (core_id == 0) ? 1 : 0; - esp_cpu_reset(other_core_id); - esp_cpu_stall(other_core_id); -#endif - -#if SOC_WDT_SUPPORTED - // Disable TG0/TG1 watchdogs - wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; - wdt_hal_write_protect_disable(&wdt0_context); - wdt_hal_disable(&wdt0_context); - wdt_hal_write_protect_enable(&wdt0_context); - - wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1}; - wdt_hal_write_protect_disable(&wdt1_context); - wdt_hal_disable(&wdt1_context); - wdt_hal_write_protect_enable(&wdt1_context); -#endif /* SOC_WDT_SUPPORTED */ // Disable cache #if CONFIG_SPIRAM @@ -237,3 +204,57 @@ void esp_restart_noos(void) ESP_INFINITE_LOOP(); } + +/* "inner" restart function for after RTOS, interrupts & anything else on this + * core are already stopped. Stalls other core, resets hardware, + * triggers restart. +*/ +void esp_restart_noos(void) +{ + // Disable interrupts + rv_utils_intr_global_disable(); +#if SOC_RTC_WDT_SUPPORTED + // Enable RTC watchdog for 1 second + wdt_hal_context_t rtc_wdt_ctx; + wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); + uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM); + wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC); + //Enable flash boot mode so that flash booting after restart is protected by the RTC WDT. + wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); +#endif /* SOC_RTC_WDT_SUPPORTED */ + +#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE + const uint32_t core_id = esp_cpu_get_core_id(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); +#endif + +#if SOC_WDT_SUPPORTED + // Disable TG0/TG1 watchdogs + wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; + wdt_hal_write_protect_disable(&wdt0_context); + wdt_hal_disable(&wdt0_context); + wdt_hal_write_protect_enable(&wdt0_context); + + wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1}; + wdt_hal_write_protect_disable(&wdt1_context); + wdt_hal_disable(&wdt1_context); + wdt_hal_write_protect_enable(&wdt1_context); +#endif /* SOC_WDT_SUPPORTED */ + +#ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM + if (esp_ptr_external_ram(esp_cpu_get_sp())) { + // If stack is in external RAM (CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM), switch SP to + // internal RAM before disabling the cache to avoid a "Cache disabled but cached memory + // region accessed" crash. + uint32_t new_sp = ALIGN_DOWN((uint32_t)&_bss_end, 16); + rv_utils_set_sp((void *)new_sp); + } +#endif + + esp_restart_noos_inner(); +} diff --git a/components/esp_system/port/soc/esp32s31/system_internal.c b/components/esp_system/port/soc/esp32s31/system_internal.c index bdc656ed639..470bd516425 100644 --- a/components/esp_system/port/soc/esp32s31/system_internal.c +++ b/components/esp_system/port/soc/esp32s31/system_internal.c @@ -26,6 +26,11 @@ #endif #include "esp_private/cache_err_int.h" #include "hal/uart_ll.h" +#include "esp_memory_utils.h" + +#define ALIGN_DOWN(val, align) ((val) & ~((align) - 1)) +extern int _bss_end; + #include "esp32s31/rom/cache.h" #include "esp32s31/rom/ets_sys.h" #include "esp32s31/rom/rtc.h" @@ -45,47 +50,9 @@ void esp_system_reset_modules_on_exit(void) } } -/* "inner" restart function for after RTOS, interrupts & anything else on this - * core are already stopped. Stalls other core, resets hardware, - * triggers restart. -*/ -void esp_restart_noos(void) +static void IRAM_ATTR __attribute__((noinline, noreturn)) esp_restart_noos_inner(void) { - // Disable interrupts - rv_utils_intr_global_disable(); -#if SOC_RTC_WDT_SUPPORTED - // Enable RTC watchdog for 1 second - wdt_hal_context_t rtc_wdt_ctx; - wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); - // uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); - uint32_t stage_timeout_ticks = (uint32_t)rtc_clk_slow_freq_get_hz(); - wdt_hal_write_protect_disable(&rtc_wdt_ctx); - wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM); - wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC); - //Enable flash boot mode so that flash booting after restart is protected by the RTC WDT. - wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true); - wdt_hal_write_protect_enable(&rtc_wdt_ctx); -#endif /* SOC_RTC_WDT_SUPPORTED */ - const uint32_t core_id = esp_cpu_get_core_id(); -#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE - const uint32_t other_core_id = (core_id == 0) ? 1 : 0; - esp_cpu_reset(other_core_id); - esp_cpu_stall(other_core_id); -#endif - -#if SOC_WDT_SUPPORTED - // Disable TG0/TG1 watchdogs - wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; - wdt_hal_write_protect_disable(&wdt0_context); - wdt_hal_disable(&wdt0_context); - wdt_hal_write_protect_enable(&wdt0_context); - - wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1}; - wdt_hal_write_protect_disable(&wdt1_context); - wdt_hal_disable(&wdt1_context); - wdt_hal_write_protect_enable(&wdt1_context); -#endif /* SOC_WDT_SUPPORTED */ // Disable cache #if CONFIG_SPIRAM @@ -129,3 +96,58 @@ void esp_restart_noos(void) ESP_INFINITE_LOOP(); } + +/* "inner" restart function for after RTOS, interrupts & anything else on this + * core are already stopped. Stalls other core, resets hardware, + * triggers restart. +*/ +void esp_restart_noos(void) +{ + // Disable interrupts + rv_utils_intr_global_disable(); +#if SOC_RTC_WDT_SUPPORTED + // Enable RTC watchdog for 1 second + wdt_hal_context_t rtc_wdt_ctx; + wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); + // uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); + uint32_t stage_timeout_ticks = (uint32_t)rtc_clk_slow_freq_get_hz(); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM); + wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC); + //Enable flash boot mode so that flash booting after restart is protected by the RTC WDT. + wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); +#endif /* SOC_RTC_WDT_SUPPORTED */ + +#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE + const uint32_t core_id = esp_cpu_get_core_id(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); +#endif + +#if SOC_WDT_SUPPORTED + // Disable TG0/TG1 watchdogs + wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; + wdt_hal_write_protect_disable(&wdt0_context); + wdt_hal_disable(&wdt0_context); + wdt_hal_write_protect_enable(&wdt0_context); + + wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1}; + wdt_hal_write_protect_disable(&wdt1_context); + wdt_hal_disable(&wdt1_context); + wdt_hal_write_protect_enable(&wdt1_context); +#endif /* SOC_WDT_SUPPORTED */ + +#ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM + if (esp_ptr_external_ram(esp_cpu_get_sp())) { + // If stack is in external RAM (CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM), switch SP to + // internal RAM before disabling the cache to avoid a "Cache disabled but cached memory + // region accessed" crash. + uint32_t new_sp = ALIGN_DOWN((uint32_t)&_bss_end, 16); + rv_utils_set_sp((void *)new_sp); + } +#endif + + esp_restart_noos_inner(); +} diff --git a/components/esp_system/test_apps/esp_system_unity_tests/main/test_reset_reason.c b/components/esp_system/test_apps/esp_system_unity_tests/main/test_reset_reason.c index baaa1048ea7..ab7d73fb2aa 100644 --- a/components/esp_system/test_apps/esp_system_unity_tests/main/test_reset_reason.c +++ b/components/esp_system/test_apps/esp_system_unity_tests/main/test_reset_reason.c @@ -323,15 +323,31 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_BROWNOUT after brownout event", check_reset_reason_brownout); #ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM -#ifndef CONFIG_FREERTOS_UNICORE +#if (defined(CONFIG_IDF_TARGET_ARCH_XTENSA) && !defined(CONFIG_FREERTOS_UNICORE)) || defined(CONFIG_IDF_TARGET_ARCH_RISCV) +#include "esp_memory_utils.h" +#include "freertos/task.h" #if CONFIG_IDF_TARGET_ARCH_XTENSA #include "xt_instr_macros.h" #include "xtensa/config/xt_specreg.h" +#endif +/* IDF's xTaskCreateStatic[PinnedToCore]() takes the stack size in BYTES + * (not in words like upstream FreeRTOS). */ static int size_stack = 1024 * 4; static StackType_t *start_addr_stack; -static int fibonacci(int n, void* func(void)) +static void func_do_exception(void) +{ + *((volatile int *) 0x01) = 0; +} + +#if CONFIG_IDF_TARGET_ARCH_XTENSA +/* On Xtensa (windowed ABI), recursing forces the register window to wrap, + * which triggers overflow/underflow exceptions that spill/fill registers + * to/from the (SPIRAM-backed) task stack. This makes sure SP is genuinely + * pointing into SPIRAM and that spilled frames live there too when func() + * is finally called from deep inside the recursion. */ +static int fibonacci(int n, void *func(void)) { int tmp1 = n, tmp2 = n; uint32_t base, start; @@ -349,6 +365,7 @@ static int fibonacci(int n, void* func(void)) printf("fib = %d\n", (tmp1 - tmp2) + fib); return fib; } +#endif // CONFIG_IDF_TARGET_ARCH_XTENSA static void test_task(void *func) { @@ -358,30 +375,13 @@ static void test_task(void *func) } else { printf("restart_task: uses internal stack, addr_stack = %p\n", start_addr_stack); } +#if CONFIG_IDF_TARGET_ARCH_XTENSA fibonacci(35, func); -} - -static void func_do_exception(void) -{ - *((int *) 0) = 0; -} - -static void init_restart_task(void) -{ - StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - printf("init_task: current addr_stack = %p, stack_for_task = %p\n", esp_cpu_get_sp(), stack_for_task); - static StaticTask_t task_buf; - xTaskCreateStaticPinnedToCore(test_task, "test_task", size_stack, esp_restart, 5, stack_for_task, &task_buf, 1); - while (1) { }; -} - -static void init_task_do_exception(void) -{ - StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - printf("init_task: current addr_stack = %p, stack_for_task = %p\n", esp_cpu_get_sp(), stack_for_task); - static StaticTask_t task_buf; - xTaskCreateStaticPinnedToCore(test_task, "test_task", size_stack, func_do_exception, 5, stack_for_task, &task_buf, 1); - while (1) { }; +#else + /* RISC-V has no register windows, so no need to recurse. Just invoke + * the test function directly from this task (whose stack is in SPIRAM). */ + ((void (*)(void))func)(); +#endif } static void test1_finish(void) @@ -396,6 +396,38 @@ static void test2_finish(void) printf("test - OK\n"); } +#if CONFIG_IDF_TARGET_ARCH_XTENSA +#ifndef CONFIG_FREERTOS_UNICORE +#define CREATE_TEST_TASK(stack, buf, entry) \ + xTaskCreateStaticPinnedToCore(test_task, "test_task", size_stack, (entry), 5, (stack), (buf), 1) +#endif // !CONFIG_FREERTOS_UNICORE +#else // RISCV +#define CREATE_TEST_TASK(stack, buf, entry) \ + xTaskCreateStatic(test_task, "test_task", size_stack, (entry), 5, (stack), (buf)) +#endif + +#if defined(CREATE_TEST_TASK) + +static void init_restart_task(void) +{ + StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(stack_for_task); + printf("init_task: current addr_stack = %p, stack_for_task = %p\n", esp_cpu_get_sp(), stack_for_task); + static StaticTask_t task_buf; + CREATE_TEST_TASK(stack_for_task, &task_buf, esp_restart); + while (1) { } +} + +static void init_task_do_exception(void) +{ + StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(stack_for_task); + printf("init_task: current addr_stack = %p, stack_for_task = %p\n", esp_cpu_get_sp(), stack_for_task); + static StaticTask_t task_buf; + CREATE_TEST_TASK(stack_for_task, &task_buf, func_do_exception); + while (1) { } +} + TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_SW after restart in a task with spiram stack", "[spiram_stack]", init_restart_task, test1_finish); @@ -404,8 +436,8 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_PANIC after an exception in a ta init_task_do_exception, test2_finish); -#endif //CONFIG_IDF_TARGET_ARCH_XTENSA -#endif // CONFIG_FREERTOS_UNICORE +#endif // CREATE_TEST_TASK +#endif // (XTENSA && !UNICORE) || RISCV #endif // CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM /* Not tested here: ESP_RST_SDIO */ diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index 7b9b6ddfbd6..44af2477d7e 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -106,6 +106,17 @@ FORCE_INLINE_ATTR void *rv_utils_get_sp(void) return sp; } +/* Switch the stack pointer to a new address. + * + * Unlike the Xtensa SET_STACK, no window register management is required on + * RISC-V; a plain register move is sufficient. The "memory" clobber prevents + * the compiler from reordering any accesses across the switch. + */ +FORCE_INLINE_ATTR void rv_utils_set_sp(void *new_sp) +{ + asm volatile ("mv sp, %0" :: "r"(new_sp) : "memory"); +} + FORCE_INLINE_ATTR uint32_t __attribute__((always_inline)) rv_utils_get_cycle_count(void) { #if !SOC_CPU_HAS_CSR_PC