Merge branch 'feat/update_esp32s31_system_support' into 'master'

feat(esp32s31): Add esp_system component support

See merge request espressif/esp-idf!43655
This commit is contained in:
C.S.M
2025-11-26 11:06:15 +08:00
8 changed files with 1071 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* ESP32-S31 Linker Script Memory Layout
* This file describes the memory layout (memory blocks) by virtual memory addresses.
* This linker script is passed through the C preprocessor to include configuration options.
* Please use preprocessor features sparingly!
* Restrict to simple macros with numeric values, and/or #if/#endif blocks.
*/
/* This file might not accurate, please check [ESP32S31] IDF-14669 */
#include "sdkconfig.h"
#include "ld.common"
#define SRAM_START 0x2F000000
#define SRAM_END 0x2F07AFC0 /* 2nd stage bootloader iram_loader_seg start address */
#define SRAM_SIZE SRAM_END - SRAM_START
#define IDROM_SEG_SIZE (CONFIG_MMU_PAGE_SIZE << 10)
MEMORY
{
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
/* PSRAM mapped instruction data */
irom_seg (RX) : org = 0x50000020, len = IDROM_SEG_SIZE - 0x20
#else
/* Flash mapped instruction data */
irom_seg (RX) : org = 0x40000020, len = IDROM_SEG_SIZE - 0x20
/**
* (0x20 offset above is a convenience for the app binary image generation.
* Flash cache has 64KB pages. The .bin file which is flashed to the chip
* has a 0x18 byte file header, and each segment has a 0x08 byte segment
* header. Setting this offset makes it simple to meet the flash cache MMU's
* constraint that (paddr % 64KB == vaddr % 64KB).)
*/
#endif // CONFIG_SPIRAM_FETCH_INSTRUCTIONS
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
/**
* Shared data RAM, excluding memory reserved for ROM bss/data/stack.
* Enabling Bluetooth & Trace Memory features in menuconfig will decrease the amount of RAM available.
*/
sram_seg (RWX) : org = SRAM_START, len = SRAM_SIZE
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
#if CONFIG_SPIRAM_RODATA
/* PSRAM mapped constant data */
drom_seg (R) : org = 0x50000020, len = IDROM_SEG_SIZE - 0x20
#else
/* Flash mapped constant data */
drom_seg (R) : org = 0x40000020, len = IDROM_SEG_SIZE - 0x20
#endif // CONFIG_SPIRAM_RODATA
/* (See irom_seg for meaning of 0x20 offset in the above.) */
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
/**
* lp ram memory (RWX). Persists over deep sleep. // TODO: IDF-5667
*/
/* TODO: ["ESP32S31"] IDF-14640 */
/* We reduced the size of lp_ram_seg by RESERVE_RTC_MEM value.
It reserves the amount of LP memory that we use for this memory segment.
This segment is intended for keeping:
- (lower addr) rtc timer data (s_rtc_timer_retain_mem, see esp_clk.c files).
- (higher addr) bootloader rtc data (s_bootloader_retain_mem, when a Kconfig option is on).
The aim of this is to keep data that will not be moved around and have a fixed address.
This segment is placed at the beginning of LP RAM, as the end of LP RAM is occupied by LP ROM stack/data
*/
/* TODO: ["ESP32S31"] IDF-14640 */
/* PSRAM seg */
extern_ram_seg(RWX) : org = 0x50000000, len = IDROM_SEG_SIZE
}
/* Heap ends at top of dram0_0_seg */
_heap_end = 0x40000000;
/**
* The lines below define location alias for .rtc.data section
* S31 has no distinguished LP(RTC) fast and slow memory sections, instead, there is a unified LP_RAM section
* Thus, the following region segments are not configurable like on other targets
*/
/* TODO: ["ESP32S31"] IDF-14640 */
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
REGION_ALIAS("text_seg", irom_seg);
#else
REGION_ALIAS("text_seg", sram_seg);
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
REGION_ALIAS("rodata_seg", drom_seg);
#else
REGION_ALIAS("rodata_seg", sram_seg);
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
#if CONFIG_SPIRAM_XIP_FROM_PSRAM
REGION_ALIAS("ext_ram_seg", drom_seg);
#else
REGION_ALIAS("ext_ram_seg", extern_ram_seg);
#endif //#if CONFIG_SPIRAM_XIP_FROM_PSRAM
/**
* If rodata default segment is placed in `drom_seg`, then flash's first rodata section must
* also be first in the segment.
*/
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
ASSERT(_flash_rodata_dummy_start == ORIGIN(rodata_seg),
".flash_rodata_dummy section must be placed at the beginning of the rodata segment.")
#endif
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
ASSERT ((__eh_frame_end > __eh_frame), "Error: eh_frame size is null!");
ASSERT ((__eh_frame_hdr_end > __eh_frame_hdr), "Error: eh_frame_hdr size is null!");
#endif

View File

@@ -0,0 +1,421 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "ld.common"
/* Default entry point */
ENTRY(call_start_cpu0);
SECTIONS
{
/**
* RTC memory should be implemented here TODO: [ESP32S31] IDF-14640
*/
.iram0.text :
{
_iram_start = ABSOLUTE(.);
/* Vectors go to start of IRAM */
ASSERT(ABSOLUTE(.) % 0x40 == 0, "vector address must be 64 byte aligned");
KEEP(*(.exception_vectors_table.text));
KEEP(*(.exception_vectors.text));
/* Code marked as running out of IRAM */
_iram_text_start = ABSOLUTE(.);
arrays[iram0_text]
mapping[iram0_text]
/* mutable libs begin */
mutable[iram0_text]
FAST_REFLASHING_PADDING;
/* mutable libs end */
} > sram_seg
/* Marks the end of IRAM code segment */
.iram0.text_end (NOLOAD) :
{
/* Align the end of code region as per PMP region granularity */
. = ALIGN(_esp_pmp_align_size);
ALIGNED_SYMBOL(4, _iram_text_end)
} > sram_seg
.iram0.data :
{
ALIGNED_SYMBOL(16, _iram_data_start)
arrays[iram0_data]
mapping[iram0_data]
/* mutable libs begin */
mutable[iram0_data]
/* mutable libs end */
_iram_data_end = ABSOLUTE(.);
} > sram_seg
.iram0.bss (NOLOAD) :
{
ALIGNED_SYMBOL(16, _iram_bss_start)
arrays[iram0_bss]
mapping[iram0_bss]
/* mutable libs begin */
mutable[iram0_bss]
/* mutable libs end */
_iram_bss_end = ABSOLUTE(.);
ALIGNED_SYMBOL(16, _iram_end)
} > sram_seg
.dram0.data :
{
_data_start = ABSOLUTE(.);
*(.gnu.linkonce.d.*)
*(.data1)
__global_pointer$ = . + 0x800;
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
*(.gnu.linkonce.s2.*)
*(.jcr)
arrays[dram0_data]
mapping[dram0_data]
/* mutable libs begin */
mutable[dram0_data]
FAST_REFLASHING_PADDING;
/* mutable libs end */
_data_end = ABSOLUTE(.);
} > sram_seg
/**
* This section holds data that should not be initialized at power up.
* The section located in Internal SRAM memory region. The macro _NOINIT
* can be used as attribute to place data into this section.
* See the "esp_attr.h" file for more information.
*/
.noinit (NOLOAD):
{
ALIGNED_SYMBOL(4, _noinit_start)
*(.noinit .noinit.*)
ALIGNED_SYMBOL(4, _noinit_end)
} > sram_seg
.flash.text :
{
_stext = .;
/**
* Mark the start of flash.text.
* This can be used by the MMU driver to maintain the virtual address.
*/
_instruction_reserved_start = ABSOLUTE(.);
_text_start = ABSOLUTE(.);
arrays[flash_text]
mapping[flash_text]
/* mutable libs begin */
mutable[flash_text]
FAST_REFLASHING_PADDING;
/* mutable libs end */
*(.stub)
*(.gnu.linkonce.t.*)
*(.gnu.warning)
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
/**
* CPU will try to prefetch up to 16 bytes of of instructions.
* This means that any configuration (e.g. MMU, PMS) must allow
* safe access to up to 16 bytes after the last real instruction, add
* dummy bytes to ensure this
*/
. += _esp_flash_mmap_prefetch_pad_size;
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
/* Align the end of flash text region as per PMP granularity to allow using the
* page alignment gap created while mapping the flash region into the PSRAM memory.
*/
. = ALIGN(_esp_pmp_align_size);
#endif // CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
_text_end = ABSOLUTE(.);
/**
* Mark the flash.text end.
* This can be used for MMU driver to maintain virtual address.
*/
_instruction_reserved_end = ABSOLUTE(.);
_etext = .;
/**
* Similar to _iram_start, this symbol goes here so it is
* resolved by addr2line in preference to the first symbol in
* the flash.text segment.
*/
_flash_cache_start = ABSOLUTE(0);
} > text_seg
/**
* Dummy section represents the .flash.text section but in default_rodata_seg.
* Thus, it must have its alignment and (at least) its size.
*/
.flash_rodata_dummy (NOLOAD):
{
_flash_rodata_dummy_start = .;
. = ALIGN(ALIGNOF(.flash.text)) + SIZEOF(.flash.text);
/* Add alignment of MMU page size + 0x20 bytes for the mapping header. */
. = ALIGN(_esp_mmu_page_size) + 0x20;
} > rodata_seg
.flash.appdesc : ALIGN(0x10)
{
/**
* Mark flash.rodata start.
* This can be used for mmu driver to maintain virtual address
*/
_rodata_reserved_start = ABSOLUTE(.);
_rodata_start = ABSOLUTE(.);
/* !DO NOT PUT ANYTHING BEFORE THIS! */
/* Should be the first. App version info. */
*(.rodata_desc .rodata_desc.*)
/* Should be the second. Custom app version info. */
*(.rodata_custom_desc .rodata_custom_desc.*)
/**
* Create an empty gap within this section. Thanks to this, the end of this
* section will match .flash.rodata's begin address. Thus, both sections
* will be merged when creating the final bin image.
*/
. = ALIGN(ALIGNOF(.flash.rodata));
} > rodata_seg
ASSERT_SECTIONS_GAP(.flash.appdesc, .flash.rodata)
.flash.rodata : ALIGN(0x10)
{
_flash_rodata_start = ABSOLUTE(.);
arrays[flash_rodata]
mapping[flash_rodata]
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
*(.gnu.linkonce.r.*)
*(.rodata1)
*(.gcc_except_table .gcc_except_table.*)
*(.gnu.linkonce.e.*)
. = ALIGN(ALIGNOF(.flash.init_array));
} > rodata_seg
ASSERT_SECTIONS_GAP(.flash.rodata, .flash.init_array)
.flash.init_array :
{
/**
* C++ constructor tables.
*
* Excluding crtbegin.o/crtend.o since IDF doesn't use the toolchain crt.
*/
ALIGNED_SYMBOL(4, __preinit_array_start)
KEEP (*(.preinit_array))
__preinit_array_end = ABSOLUTE(.);
ALIGNED_SYMBOL(4, __init_array_start)
KEEP (*(SORT_BY_INIT_PRIORITY(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array.*)))
KEEP (*(EXCLUDE_FILE (*crtend.* *crtbegin.*) .init_array))
__init_array_end = ABSOLUTE(.);
/* Addresses of memory regions reserved via SOC_RESERVE_MEMORY_REGION() */
ALIGNED_SYMBOL(4, soc_reserved_memory_region_start)
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
ALIGNED_SYMBOL(4, _esp_system_init_fn_array_start)
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
/* mutable libs begin */
mutable[flash_rodata]
FAST_REFLASHING_PADDING;
/* mutable libs end */
_rodata_end = ABSOLUTE(.);
. = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA));
} > rodata_seg
ASSERT_SECTIONS_GAP(.flash.init_array, SECTION_AFTER_FLASH_RODATA)
#if EH_FRAME_LINKING_ENABLED
.eh_frame_hdr :
{
ALIGNED_SYMBOL(4, __eh_frame_hdr)
KEEP (*(.eh_frame_hdr))
__eh_frame_hdr_end = ABSOLUTE(.);
. = ALIGN(ALIGNOF(.eh_frame));
} > rodata_seg
ASSERT_SECTIONS_GAP(.eh_frame_hdr, .eh_frame)
.eh_frame :
{
ALIGNED_SYMBOL(4, __eh_frame)
KEEP (*(.eh_frame))
/**
* As we are not linking with crtend.o, which includes the CIE terminator
* (see __FRAME_END__ in libgcc sources), it is manually provided here.
*/
LONG(0);
__eh_frame_end = ABSOLUTE(.);
. = ALIGN(ALIGNOF(.flash.tdata));
} > rodata_seg
ASSERT_SECTIONS_GAP(.eh_frame, .flash.tdata)
#endif // EH_FRAME_LINKING_ENABLED
.flash.tdata :
{
_thread_local_data_start = ABSOLUTE(.);
*(.tdata .tdata.* .gnu.linkonce.td.*)
. = ALIGN(ALIGNOF(.flash.tbss));
#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
/* Align the end of flash rodata region as per PMP granularity to allow using the
* page alignment gap created while mapping the flash region into the PSRAM memory.
*/
. = ALIGN(_esp_pmp_align_size);
#endif // CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
_thread_local_data_end = ABSOLUTE(.);
} > rodata_seg
ASSERT_SECTIONS_GAP(.flash.tdata, .flash.tbss)
.flash.tbss (NOLOAD) :
{
_thread_local_bss_start = ABSOLUTE(.);
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon .tcommon.*)
_thread_local_bss_end = ABSOLUTE(.);
} > rodata_seg
/**
* This section contains all the rodata that is not used
* at runtime, helping to avoid an increase in binary size.
*/
.flash.rodata_noload (NOLOAD) :
{
/**
* This symbol marks the end of flash.rodata. It can be utilized by the MMU
* driver to maintain the virtual address.
* NOLOAD rodata may not be included in this section.
*/
_rodata_reserved_end = ADDR(.flash.tbss);
arrays[rodata_noload]
mapping[rodata_noload]
/* mutable libs begin */
mutable[rodata_noload]
/* mutable libs end */
} > rodata_seg
#if CONFIG_SPIRAM_XIP_FROM_PSRAM
/**
* This section is required to skip flash sections, because `extern_ram_seg`
* and `drom_seg` / `irom_seg` are on the same bus when xip on psram
*/
.ext_ram.dummy (NOLOAD):
{
. = ORIGIN(ext_ram_seg) + (_rodata_reserved_end - _flash_rodata_dummy_start);
. = ALIGN (_esp_mmu_page_size);
} > ext_ram_seg
#endif //CONFIG_SPIRAM_XIP_FROM_PSRAM
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
/* This section holds .ext_ram.bss data, and will be put in PSRAM */
.ext_ram.bss (NOLOAD) :
{
_ext_ram_bss_start = ABSOLUTE(.);
arrays[extern_ram]
mapping[extern_ram]
/* mutable libs begin */
mutable[extern_ram]
/* mutable libs end */
ALIGNED_SYMBOL(4, _ext_ram_bss_end)
} > ext_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/**
* This section holds data that won't be initialised when startup.
* This section locates in External RAM region.
*/
.ext_ram_noinit (NOLOAD) :
{
_ext_ram_noinit_start = ABSOLUTE(.);
*(.ext_ram_noinit*)
ALIGNED_SYMBOL(4, _ext_ram_noinit_end)
} > ext_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
.dram0.bss (NOLOAD) :
{
ALIGNED_SYMBOL(4, _bss_start)
/**
* ldgen places all bss-related data to mapping[dram0_bss]
* (See components/esp_system/app.lf).
*/
arrays[dram0_bss]
mapping[dram0_bss]
/* mutable libs begin */
mutable[dram0_bss]
/* mutable libs end */
ALIGNED_SYMBOL(4, _bss_end)
} > sram_seg
/* Marks the end of data, bss and possibly rodata */
.dram0.heap_start (NOLOAD) :
{
ALIGNED_SYMBOL(16, _heap_start)
} > sram_seg
#include "elf_misc.ld.in"
}

View File

@@ -0,0 +1,8 @@
set(srcs "clk.c"
"reset_reason.c"
"system_internal.c"
"cache_err_int.c")
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})

View File

@@ -0,0 +1,14 @@
choice ESP_DEFAULT_CPU_FREQ_MHZ
prompt "CPU frequency"
default ESP_DEFAULT_CPU_FREQ_MHZ_40 if IDF_ENV_FPGA || ESP_BRINGUP_BYPASS_CPU_CLK_SETTING
help
CPU frequency to be set on application startup.
config ESP_DEFAULT_CPU_FREQ_MHZ_40
bool "40 MHz"
depends on IDF_ENV_FPGA || ESP_BRINGUP_BYPASS_CPU_CLK_SETTING
endchoice
config ESP_DEFAULT_CPU_FREQ_MHZ
int
default 40 if ESP_DEFAULT_CPU_FREQ_MHZ_40

View File

@@ -0,0 +1,94 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
The cache has an interrupt that can be raised as soon as an access to a cached
region (flash) is done without the cache being enabled. We use that here
to panic the CPU, which from a debugging perspective is better than grabbing bad
data from the bus.
*/
#include "esp_rom_sys.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "soc/periph_defs.h"
#include "riscv/interrupt.h"
#include "hal/cache_ll.h"
#include "esp_private/cache_err_int.h"
static const char *TAG = "CACHE_ERR";
const char cache_error_msg[] = "Cache access error";
// TODO: ["ESP32S31"] IDF-14650
void esp_cache_err_get_panic_info(esp_cache_err_info_t *err_info)
{
if (err_info == NULL) {
return;
}
uint32_t access_err_status = cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK) | cache_ll_l2_get_access_error_intr_status(0, CACHE_LL_L2_ACCESS_EVENT_MASK);
/* Return the error string if a cache error is active */
err_info->err_str = access_err_status ? cache_error_msg : NULL;
}
bool esp_cache_err_has_active_err(void)
{
bool has_active_err = cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK) | cache_ll_l2_get_access_error_intr_status(0, CACHE_LL_L2_ACCESS_EVENT_MASK);
return has_active_err;
}
void esp_cache_err_int_init(void)
{
const uint32_t core_id = 0;
/* Disable cache interrupts if enabled. */
ESP_INTR_DISABLE(ETS_CACHEERR_INUM);
/**
* Bind all cache errors to ETS_CACHEERR_INUM interrupt. we will deal with
* them in handler by different types
*
* On ESP32S31 boards, the cache is a shared one but buses are still
* distinct. So, we have an bus0 and a bus1 sharing the same cache.
* This error can occur if a bus performs a request but the cache
* is disabled.
*/
esp_rom_route_intr_matrix(core_id, ETS_CACHE_INTR_SOURCE, ETS_CACHEERR_INUM);
/* Set the type and priority to cache error interrupts. */
esprv_int_set_type(ETS_CACHEERR_INUM, INTR_TYPE_LEVEL);
esprv_int_set_priority(ETS_CACHEERR_INUM, SOC_INTERRUPT_LEVEL_MEDIUM);
ESP_DRAM_LOGV(TAG, "access error intr clr & ena mask is: 0x%x", CACHE_LL_L1_ACCESS_EVENT_MASK);
/* On the hardware side, start by clearing all the bits responsible for cache access error */
cache_ll_l1_clear_access_error_intr(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
cache_ll_l2_clear_access_error_intr(0, CACHE_LL_L2_ACCESS_EVENT_MASK);
/* Then enable cache access error interrupts. */
cache_ll_l1_enable_access_error_intr(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
cache_ll_l2_enable_access_error_intr(0, CACHE_LL_L2_ACCESS_EVENT_MASK);
/* Enable the interrupts for cache error. */
ESP_INTR_ENABLE(ETS_CACHEERR_INUM);
}
void esp_cache_err_clear_active_err(void)
{
}
int esp_cache_err_get_cpuid(void)
{
if (cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_CORE0_EVENT_MASK)) {
return 0;
} else if (cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_CORE1_EVENT_MASK)) {
return 1;
} else {
return -1;
}
}

View File

@@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/time.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_clk_internal.h"
#include "esp32s31/rom/ets_sys.h"
#include "esp32s31/rom/uart.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_periph.h"
#include "soc/i2s_reg.h"
#include "soc/hp_sys_clkrst_reg.h"
#include "esp_cpu.h"
#include "soc/hp_sys_clkrst_reg.h"
#include "soc/lp_clkrst_reg.h"
#include "soc/lp_system_reg.h"
#include "soc/spi_mem_c_reg.h"
#include "soc/spi_mem_s_reg.h"
#include "soc/usb_serial_jtag_reg.h"
#include "soc/hp_alive_sys_reg.h"
#include "hal/uart_ll.h"
#include "hal/wdt_hal.h"
#include "esp_private/esp_modem_clock.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_clk.h"
#include "esp_rom_serial_output.h"
#include "esp_rom_sys.h"
// TODO: ["ESP32S31"] IDF-14733
/* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
* Larger values increase startup delay. Smaller values may cause false positive
* detection (i.e. oscillator runs for a few cycles and then stops).
*/
#define MHZ (1000000)
static void select_rtc_slow_clk(soc_rtc_slow_clk_src_t rtc_slow_clk_src);
static const char *TAG = "clk";
void IRAM_ATTR esp_rtc_init(void)
{
#if SOC_PMU_SUPPORTED
pmu_init();
#endif //SOC_PMU_SUPPORTED
}
__attribute__((weak)) void esp_clk_init(void)
{
assert(rtc_clk_xtal_freq_get() == SOC_XTAL_FREQ_40M);
rtc_clk_8m_enable(true);
#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
// WDT uses a SLOW_CLK clock source. After a function select_rtc_slow_clk a frequency of this source can changed.
// If the frequency changes from 150kHz to 32kHz, then the timeout set for the WDT will increase 4.6 times.
// Therefore, for the time of frequency change, set a new lower timeout value (1.6 sec).
// This prevents excessive delay before resetting in case the supply voltage is drawdown.
// (If frequency is changed from 150kHz to 32kHz then WDT timeout will increased to 1.6sec * 150/32 = 7.5 sec).
wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
uint32_t stage_timeout_ticks = (uint32_t)(1600ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_feed(&rtc_wdt_ctx);
//Bootloader has enabled RTC WDT until now. We're only modifying timeout, so keep the stage and timeout action the same
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
#endif
#if defined(CONFIG_RTC_CLK_SRC_EXT_CRYS)
select_rtc_slow_clk(SOC_RTC_SLOW_CLK_SRC_XTAL32K);
#else
select_rtc_slow_clk(SOC_RTC_SLOW_CLK_SRC_RC_SLOW);
#endif
#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
// After changing a frequency WDT timeout needs to be set for new frequency.
stage_timeout_ticks = (uint32_t)((uint64_t)CONFIG_BOOTLOADER_WDT_TIME_MS * rtc_clk_slow_freq_get_hz() / 1000);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_feed(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
#endif
rtc_cpu_freq_config_t old_config, new_config;
rtc_clk_cpu_freq_get_config(&old_config);
const uint32_t old_freq_mhz = old_config.freq_mhz;
const uint32_t new_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
bool res = rtc_clk_cpu_freq_mhz_to_config(new_freq_mhz, &new_config);
assert(res);
// Wait for UART TX to finish, otherwise some UART output will be lost
// when switching APB frequency
if (CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM != -1) {
esp_rom_output_tx_wait_idle(CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM);
}
if (res) {
rtc_clk_cpu_freq_set_config(&new_config);
}
// Re calculate the ccount to make time calculation correct.
esp_cpu_set_cycle_count((uint64_t)esp_cpu_get_cycle_count() * new_freq_mhz / old_freq_mhz);
}
static void select_rtc_slow_clk(soc_rtc_slow_clk_src_t rtc_slow_clk_src)
{
uint32_t cal_val = 0;
/* number of times to repeat 32k XTAL calibration
* before giving up and switching to the internal RC
*/
do {
if (rtc_slow_clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
/* 32k XTAL oscillator needs to be enabled and running before it can
* be used. Hardware doesn't have a direct way of checking if the
* oscillator is running. Here we use rtc_clk_cal function to count
* the number of main XTAL cycles in the given number of 32k XTAL
* oscillator cycles. If the 32k XTAL has not started up, calibration
* will time out, returning 0.
*/
ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up");
if (rtc_slow_clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
rtc_clk_32k_enable(true);
}
} else if (rtc_slow_clk_src == SOC_RTC_SLOW_CLK_SRC_RC32K) {
rtc_clk_rc32k_enable(true);
}
rtc_clk_slow_src_set(rtc_slow_clk_src);
// Disable unused clock sources after clock source switching is complete.
// Regardless of the clock source selection, the internal 136K clock source will always keep on.
if (rtc_slow_clk_src != SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
rtc_clk_32k_enable(false);
}
if (rtc_slow_clk_src != SOC_RTC_SLOW_CLK_SRC_RC32K) {
rtc_clk_rc32k_enable(false);
}
} while (cal_val == 0);
ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %" PRIu32, cal_val);
esp_clk_slowclk_cal_set(cal_val);
}
void rtc_clk_select_rtc_slow_clk(void)
{
select_rtc_slow_clk(SOC_RTC_SLOW_CLK_SRC_XTAL32K);
}
/* This function is not exposed as an API at this point.
* All peripheral clocks are default enabled after chip is powered on.
* This function disables some peripheral clocks when cpu starts.
* These peripheral clocks are enabled when the peripherals are initialized
* and disabled when they are de-initialized.
*/
__attribute__((weak)) void esp_perip_clk_init(void)
{
}

View File

@@ -0,0 +1,112 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_system.h"
#include "esp_rom_sys.h"
#include "esp_private/system_internal.h"
#include "soc/rtc_periph.h"
#include "soc/chip_revision.h"
#include "hal/efuse_hal.h"
#include "esp32s31/rom/rtc.h"
// TODO: ["ESP32S31"] IDF-14672
static void esp_reset_reason_clear_hint(void);
static esp_reset_reason_t s_reset_reason;
static esp_reset_reason_t get_reset_reason(soc_reset_reason_t rtc_reset_reason, esp_reset_reason_t reset_reason_hint)
{
switch (rtc_reset_reason) {
case RESET_REASON_CHIP_POWER_ON:
return ESP_RST_POWERON;
case RESET_REASON_CPU0_SW:
case RESET_REASON_CORE_SW:
if (reset_reason_hint == ESP_RST_PANIC ||
reset_reason_hint == ESP_RST_BROWNOUT ||
reset_reason_hint == ESP_RST_TASK_WDT ||
reset_reason_hint == ESP_RST_INT_WDT) {
return reset_reason_hint;
}
return ESP_RST_SW;
case RESET_REASON_CORE_PMU_PWR_DOWN:
return ESP_RST_DEEPSLEEP;
case RESET_REASON_CPU_MWDT:
case RESET_REASON_CPU_RWDT:
case RESET_REASON_SYS_SUPER_WDT:
case RESET_REASON_SYS_RWDT:
case RESET_REASON_CORE_MWDT:
case RESET_REASON_CORE_RWDT:
/* Code is the same for INT vs Task WDT */
return ESP_RST_WDT;
case RESET_REASON_SYS_BROWN_OUT:
return ESP_RST_BROWNOUT;
case RESET_REASON_CORE_USB_UART:
case RESET_REASON_CORE_USB_JTAG:
return ESP_RST_USB;
case RESET_REASON_CPU_JTAG:
return ESP_RST_JTAG;
case RESET_REASON_CPU_LOCKUP:
return ESP_RST_CPU_LOCKUP;
case RESET_REASON_CORE_EFUSE_CRC:
return ESP_RST_EFUSE;
case RESET_REASON_CORE_PWR_GLITCH:
return ESP_RST_PWR_GLITCH;
default:
return ESP_RST_UNKNOWN;
}
}
static void __attribute__((constructor)) esp_reset_reason_init(void)
{
esp_reset_reason_t hint = esp_reset_reason_get_hint();
s_reset_reason = get_reset_reason(esp_rom_get_reset_reason(PRO_CPU_NUM), hint);
if (hint != ESP_RST_UNKNOWN) {
esp_reset_reason_clear_hint();
}
}
esp_reset_reason_t esp_reset_reason(void)
{
return s_reset_reason;
}
/* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG,
* a.k.a. RTC_ENTRY_ADDR_REG. It is safe to use this register both for the
* deep sleep wake stub entry address and for reset reason hint, since wake stub
* is only used for deep sleep reset, and in this case the reason provided by
* esp_rom_get_reset_reason is unambiguous.
*
* Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG):
* the value is replicated in low and high half-words. In addition to that,
* MSB is set to 1, which doesn't happen when RTC_CNTL_STORE6_REG contains
* deep sleep wake stub address.
*/
/* in IRAM, can be called from panic handler */
void IRAM_ATTR esp_reset_reason_set_hint(esp_reset_reason_t hint)
{
// TODO: ["ESP32S31"] IDF-14672
}
esp_reset_reason_t esp_reset_reason_get_hint(void)
{
return 0; // TODO: ["ESP32S31"] IDF-14672
}
static inline void esp_reset_reason_clear_hint(void)
{
// TODO: ["ESP32S31"] IDF-14672
}

View File

@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "sdkconfig.h"
#include "esp_macros.h"
#include "esp_system.h"
#include "esp_private/system_internal.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "riscv/rv_utils.h"
#include "esp_rom_serial_output.h"
#include "soc/gpio_reg.h"
#include "esp_cpu.h"
#include "soc/rtc.h"
#include "esp_private/rtc_clk.h"
#include "soc/rtc_periph.h"
#include "soc/uart_reg.h"
#include "hal/wdt_hal.h"
#include "esp_private/cache_err_int.h"
#include "hal/uart_ll.h"
#include "esp32s31/rom/cache.h"
#include "esp32s31/rom/rtc.h"
#include "soc/hp_sys_clkrst_reg.h"
#include "soc/lp_clkrst_reg.h"
#include "soc/hp_system_reg.h"
// TODO: [ESP32S31] IDF-14841
void IRAM_ATTR esp_system_reset_modules_on_exit(void)
{
// Flush any data left in UART FIFOs
for (int i = 0; i < SOC_UART_HP_NUM; ++i) {
if (uart_ll_is_enabled(i)) {
esp_rom_output_tx_wait_idle(i);
}
}
}
/* "inner" restart function for after RTOS, interrupts & anything else on this
* core are already stopped. Stalls other core, resets hardware,
* triggers restart.
*/
void IRAM_ATTR esp_restart_noos(void)
{
// Disable interrupts
rv_utils_intr_global_disable();
// 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);
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
// 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);
// Disable cache
#if CONFIG_SPIRAM
Cache_WriteBack_All(CACHE_MAP_L1_DCACHE);
#endif
esp_system_reset_modules_on_exit();
// Set CPU back to XTAL source (and MEM_CLK, APB_CLK back to power-on reset frequencies), same as hard reset, keep CPLL on.
#if !CONFIG_IDF_ENV_FPGA
rtc_clk_cpu_set_to_default_config();
#endif
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
// clear entry point for APP CPU
ets_set_appcpu_boot_addr(0);
#endif
#if CONFIG_SPIRAM_INSTRUCTIONS_RODATA
//TODO: IDF-7556
// disable remap if enabled in menuconfig
REG_CLR_BIT(HP_SYS_HP_PSRAM_FLASH_ADDR_INTERCHANGE_REG, HP_SYS_HP_PSRAM_FLASH_ADDR_INTERCHANGE_DMA | HP_SYS_HP_PSRAM_FLASH_ADDR_INTERCHANGE_CPU);
#endif
// Reset CPUs
if (core_id == 0) {
// Running on PRO CPU: APP CPU is stalled. Can reset both CPUs.
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
esp_cpu_reset(1);
#endif
esp_cpu_reset(0);
}
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
else {
// Running on APP CPU: need to reset PRO CPU and unstall it,
// then reset APP CPU
esp_cpu_reset(0);
esp_cpu_unstall(0);
esp_cpu_reset(1);
}
#endif
ESP_INFINITE_LOOP();
}