Merge branch 'feat/sys_event_in_freertos' into 'master'

feat(system_init): Introduce pre-scheduler and pre-app main handler registrations

Closes IDF-16133

See merge request espressif/esp-idf!52296
This commit is contained in:
Marius Vikhammer
2026-09-09 09:28:52 +08:00
21 changed files with 348 additions and 129 deletions
+5
View File
@@ -198,6 +198,11 @@ endif()
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_system_include_startup_funcs")
if(CONFIG_ESP_INT_WDT)
# Pull in the PRE_SCHEDULER registrations from int_wdt.c.
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_int_wdt_init")
endif()
# [refactor-todo] requirements due to init code, should be removable
# once link-time registration of component init functions is used.
if(NOT CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT)
@@ -3,7 +3,7 @@
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
# This file is used to check the order of execution of ESP_SYSTEM_INIT_FN functions.
# This file checks the order of execution of system startup handlers.
# It compares the priorities found in .c source files to the contents of system_init_fn.txt
# In case of an inconsistency, the script prints the differences found and returns with a
# non-zero exit code.
@@ -15,12 +15,47 @@ import re
import sys
COMMENT_REGEX = re.compile(r'//.*?$|/\*.*?\*/', re.DOTALL | re.MULTILINE)
ESP_SYSTEM_INIT_FN_REGEX_SIMPLE = re.compile(r'\bESP_SYSTEM_INIT_FN\s*\(')
ESP_SYSTEM_INIT_FN_REGEX = re.compile(
r'ESP_SYSTEM_INIT_FN\(([a-zA-Z0-9_]+)\s*,\s*([a-zA-Z\ _0-9\(\)|]+)\s*,\s*([a-zA-Z\ _0-9\(\)|]+)\s*,\s*([0-9]+)\)'
ESP_SYSTEM_INIT_FN_REGEX = (
r'{macro}\((?P<func>[a-zA-Z0-9_]+)\s*,\s*'
r'(?P<stage>[a-zA-Z\ _0-9\(\)|]+)\s*,\s*'
r'(?P<affinity>[a-zA-Z\ _0-9\(\)|]+)\s*,\s*'
r'(?P<priority>[0-9]+)\)'
)
APP_INIT_FN_REGEX = r'{macro}\s*\(\s*(?P<func>[a-zA-Z0-9_]+)\s*,\s*(?P<priority>[0-9]+)\s*\)'
STARTUP_REGISTRATIONS = (
(
'ESP_SYSTEM_INIT_FN',
re.compile(ESP_SYSTEM_INIT_FN_REGEX.format(macro='ESP_SYSTEM_INIT_FN')),
'',
'',
),
(
'ESP_PRE_SCHEDULER_HANDLER_REGISTER',
re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_SCHEDULER_HANDLER_REGISTER')),
'PRE_SCHEDULER',
'BIT(0)',
),
(
'ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU',
re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU')),
'PRE_SCHEDULER',
'ESP_SYSTEM_INIT_ALL_CORES',
),
(
'ESP_PRE_APP_MAIN_HANDLER_REGISTER',
re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_APP_MAIN_HANDLER_REGISTER')),
'PRE_APP_MAIN',
'MAIN_TASK',
),
)
STARTUP_ENTRIES_FILE = 'components/esp_system/system_init_fn.txt'
EXCLUDED_SOURCE_DIRS = {'test_apps', 'host_test', 'host_tests'}
STAGE_ORDER = {
'CORE': 0,
'SECONDARY': 1,
'PRE_SCHEDULER': 2,
'PRE_APP_MAIN': 3,
}
class StartupEntry:
@@ -69,36 +104,36 @@ def main() -> None:
file_contents = f_obj.read()
file_contents_no_comments = strip_comments(file_contents)
if not ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.search(file_contents_no_comments):
continue
for macro, pattern, default_stage, default_affinity in STARTUP_REGISTRATIONS:
count_expected = len(re.findall(rf'\b{macro}\s*\(', file_contents_no_comments))
found = list(pattern.finditer(file_contents_no_comments))
if len(found) != count_expected:
print(
f'error: In {filename}, found {macro} {count_expected} time(s), '
f'but regular expression matched {len(found)} time(s)',
file=sys.stderr,
)
has_errors = True
count_expected = len(ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.findall(file_contents_no_comments))
found = ESP_SYSTEM_INIT_FN_REGEX.findall(file_contents_no_comments)
if len(found) != count_expected:
print(
(
f'error: In {filename}, found ESP_SYSTEM_INIT_FN {count_expected} time(s), '
f'but regular expression matched {len(found)} time(s)'
),
file=sys.stderr,
)
has_errors = True
for match in found:
entry = StartupEntry(
filename=relpath, func=match[0], stage=match[1], affinity=match[2], priority=int(match[3])
)
startup_entries.append(entry)
for match in found:
startup_entries.append(
StartupEntry(
relpath,
match.group('func'),
match.groupdict().get('stage') or default_stage,
match.groupdict().get('affinity') or default_affinity,
int(match.group('priority')),
)
)
#
# 2. Sort the ESP_SYSTEM_INIT_FN functions in C source files.
# 2. Sort the startup handlers in C source files.
# In addition to the stage and priority, we also add filename to the sort key,
# to have a stable sorting order in case when the same startup function is defined in multiple files,
# for example for different targets.
#
def sort_key(entry: StartupEntry) -> tuple[str, int, str]:
# luckily 'core' and 'secondary' are in alphabetical order, so we can return the string
return (entry.stage, entry.priority, entry.filename)
def sort_key(entry: StartupEntry) -> tuple[int, int, str]:
return (STAGE_ORDER[entry.stage], entry.priority, entry.filename)
startup_entries = list(sorted(startup_entries, key=sort_key))
startup_entries_lines = [str(entry) for entry in startup_entries]
+9
View File
@@ -11,6 +11,7 @@
#include "esp_intr_alloc.h"
#include "esp_debug_helpers.h"
#include "esp_private/crosscore_int.h"
#include "esp_private/esp_sys_event_app_init.h"
#include "soc/periph_defs.h"
#include "soc/system_intr.h"
#include "hal/crosscore_int_ll.h"
@@ -113,6 +114,14 @@ void esp_crosscore_int_init(void)
ESP_ERROR_CHECK(err);
}
ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(init_crosscore_interrupt, 120)
{
(void)user_arg;
(void)ctx;
esp_crosscore_int_init();
return ESP_OK;
}
static void ESP_SYSTEM_IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask)
{
assert(core_id < CONFIG_FREERTOS_NUMBER_OF_CORES);
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_cpu.h"
#include "esp_private/esp_sys_event.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Register a handler to run on core 0 immediately before the scheduler starts
*/
#define ESP_PRE_SCHEDULER_HANDLER_REGISTER(fn, priority) \
static esp_err_t fn(void *user_arg, void *ctx); \
_ESP_SYS_EVENT_REGISTER(PRE_SCHEDULER, priority, __esp_pre_scheduler_##fn) \
{ \
if (esp_cpu_get_core_id() != 0) { \
return ESP_OK; \
} \
return fn(user_arg, ctx); \
} \
static esp_err_t fn(void *user_arg, void *ctx)
/**
* @brief Register a handler to run on every core immediately before its scheduler starts
*/
#define ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(fn, priority) \
_ESP_SYS_EVENT_REGISTER(PRE_SCHEDULER, priority, fn)
/**
* @brief Register a handler to run in the main task immediately before app_main
*
* Handlers run in ascending priority order after all CPUs have completed
* FreeRTOS initialization. The scheduler and heap allocator are available.
*/
#define ESP_PRE_APP_MAIN_HANDLER_REGISTER(fn, priority) \
_ESP_SYS_EVENT_REGISTER(PRE_APP_MAIN, priority, fn)
#ifdef __cplusplus
}
#endif
@@ -34,6 +34,8 @@
#define _ESP_SYS_EVENT_SECTION_SHUTDOWN esysev_shdn
#define _ESP_SYS_EVENT_SECTION_SYSTEM_INIT_CORE esysev_initc
#define _ESP_SYS_EVENT_SECTION_SYSTEM_INIT_SECONDARY esysev_inits
#define _ESP_SYS_EVENT_SECTION_PRE_SCHEDULER esysev_presc
#define _ESP_SYS_EVENT_SECTION_PRE_APP_MAIN esysev_pream
#define _ESP_SYS_EVENT_SECTION_PANIC esysev_panic
#define _ESP_SYS_EVENT_SECTION_PANIC_EARLY_BREAK esysev_paneb
#endif
@@ -42,5 +44,7 @@
ESP_SYS_EVENT_DEF(SHUTDOWN)
ESP_SYS_EVENT_DEF(SYSTEM_INIT_CORE)
ESP_SYS_EVENT_DEF(SYSTEM_INIT_SECONDARY)
ESP_SYS_EVENT_DEF(PRE_SCHEDULER)
ESP_SYS_EVENT_DEF(PRE_APP_MAIN)
ESP_SYS_EVENT_DEF(PANIC)
ESP_SYS_EVENT_DEF(PANIC_EARLY_BREAK)
+17
View File
@@ -23,6 +23,7 @@
#include "esp_freertos_hooks.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_int_wdt.h"
#include "esp_private/esp_sys_event_app_init.h"
#include "mwdt_priv.h"
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_MWDT_SUPPORT_SLEEP_RETENTION
@@ -194,6 +195,22 @@ void esp_int_wdt_cpu_init(void)
esp_intr_enable_source(ETS_INT_WDT_INUM);
}
ESP_PRE_SCHEDULER_HANDLER_REGISTER(init_int_wdt, 100)
{
(void)user_arg;
(void)ctx;
esp_int_wdt_init();
return ESP_OK;
}
ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(init_int_wdt_cpu, 110)
{
(void)user_arg;
(void)ctx;
esp_int_wdt_cpu_init();
return ESP_OK;
}
void ESP_SYSTEM_IRAM_ATTR esp_int_wdt_pause(void)
{
wdt_hal_write_protect_disable(&iwdt_context);
@@ -22,6 +22,16 @@ SECTIONS
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_inits.*)))
PROVIDE(_esysev_inits_end = .);
. = ALIGN(4);
PROVIDE(_esysev_presc_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_presc.*)))
PROVIDE(_esysev_presc_end = .);
. = ALIGN(4);
PROVIDE(_esysev_pream_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_pream.*)))
PROVIDE(_esysev_pream_end = .);
. = ALIGN(4);
PROVIDE(_esysev_panic_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_panic.*)))
+12
View File
@@ -12,6 +12,14 @@ entries:
entries:
.esysev_inits+
[sections:esysev_presc]
entries:
.esysev_presc+
[sections:esysev_pream]
entries:
.esysev_pream+
[sections:esysev_panic]
entries:
.esysev_panic+
@@ -26,6 +34,8 @@ entries:
esysev_shdn -> flash_rodata
esysev_initc -> flash_rodata
esysev_inits -> flash_rodata
esysev_presc -> flash_rodata
esysev_pream -> flash_rodata
esysev_panic -> flash_rodata
esysev_paneb -> flash_rodata
@@ -36,6 +46,8 @@ entries:
esysev_shdn -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_shdn),
esysev_initc -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_initc),
esysev_inits -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_inits),
esysev_presc -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_presc),
esysev_pream -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_pream),
esysev_panic -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_panic),
esysev_paneb -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_paneb)
+13
View File
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "sdkconfig.h"
#include "esp_attr.h"
@@ -13,8 +14,10 @@
#include "esp_check.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_chip_info.h"
#include "esp_xt_wdt.h"
#include "esp_cpu.h"
#include "esp_private/esp_sys_event_app_init.h"
#include "esp_private/startup_internal.h"
#include "freertos/FreeRTOS.h"
#include "soc/soc_caps.h"
@@ -50,6 +53,16 @@ void esp_system_include_startup_funcs(void)
// [refactor-todo] Most of these init functions should be moved to the respective components.
#if !CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
ESP_PRE_SCHEDULER_HANDLER_REGISTER(check_cache_lock_bug, 90)
{
(void)user_arg;
(void)ctx;
assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!");
return ESP_OK;
}
#endif
ESP_SYSTEM_INIT_FN(init_show_cpu_freq, CORE, BIT(0), 10)
{
ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
+36 -5
View File
@@ -1,7 +1,7 @@
# This file documents the expected order of execution of ESP_SYSTEM_INIT_FN functions.
# This file documents the expected order of execution of system startup handlers.
#
# When adding new ESP_SYSTEM_INIT_FN functions or changing init priorities of existing functions,
# keep this file up to date. This is checked in CI.
# When adding startup handlers or changing their priorities, keep this file up to date.
# This is checked in CI.
# When adding new functions or changing the priorities, please read the comments and see if
# they need to be updated to be consistent with the changes you are making.
#
@@ -9,7 +9,7 @@
# Each line has the following format:
# stage: prio: function_name in path/to/source_file on affinity_expression
# Where:
# stage: which startup stage the function is executed in (CORE or SECONDARY)
# stage: which startup stage the function is executed in (CORE, SECONDARY, PRE_SCHEDULER, or PRE_APP_MAIN)
# prio: priority value (higher value means function is executed later)
# affinity_expression: bit map of cores the function is executed on
@@ -148,4 +148,35 @@ SECONDARY: 240: psram_adjust_timing_point_via_temperature in components/esp_hw_s
# Now that the application is about to start, disable boot watchdog
SECONDARY: 999: init_disable_rtc_wdt in components/esp_system/startup_funcs.c on BIT(0)
# DO NOT add new init functions here. Add them to the correct stage above.
# DO NOT add new init functions at the end of a stage. Add them at the correct priority.
########### PRE_SCHEDULER startup stage ###########
# If the interrupt watchdog is disabled, validate the ESP32 ECO3 cache lock configuration instead.
PRE_SCHEDULER: 90: check_cache_lock_bug in components/esp_system/startup_funcs.c on BIT(0)
# Initialize the interrupt watchdog globally, then enable it on each core.
PRE_SCHEDULER: 100: init_int_wdt in components/esp_system/int_wdt.c on BIT(0)
PRE_SCHEDULER: 110: init_int_wdt_cpu in components/esp_system/int_wdt.c on ESP_SYSTEM_INIT_ALL_CORES
# Cross-core interrupts must be available before each core starts its scheduler.
PRE_SCHEDULER: 120: init_crosscore_interrupt in components/esp_system/crosscore_int.c on ESP_SYSTEM_INIT_ALL_CORES
# Runtime GDB stub initialization requires cross-core interrupts.
PRE_SCHEDULER: 130: init_gdbstub in components/esp_gdbstub/src/gdbstub.c on BIT(0)
########### PRE_APP_MAIN startup stage ###########
# Reclaim startup stacks only after every core has completed FreeRTOS initialization.
PRE_APP_MAIN: 100: enable_nonos_stack_heaps in components/heap/heap_caps_init.c on MAIN_TASK
# Reserve the internal DMA pool after startup stack memory has been reclaimed.
PRE_APP_MAIN: 110: reserve_dma_pool in components/esp_psram/system_layer/esp_psram.c on MAIN_TASK
# Initialize the task watchdog after the scheduler is available.
PRE_APP_MAIN: 120: init_task_wdt in components/esp_system/task_wdt/task_wdt.c on MAIN_TASK
# Confirm the running OTA image immediately before calling app_main.
PRE_APP_MAIN: 200: esp_ota_auto_confirm in components/app_update/ota_auto_confirm.c on MAIN_TASK
+23
View File
@@ -23,6 +23,7 @@
#include "esp_task_wdt.h"
#include "esp_private/system_internal.h"
#include "esp_private/crosscore_int.h"
#include "esp_private/esp_sys_event_app_init.h"
#include "esp_private/esp_task_wdt.h"
#include "esp_private/esp_task_wdt_impl.h"
@@ -58,6 +59,28 @@ extern bool g_panic_abort;
/* Global flag marking whether the current ISR is a Task Watchdog ISR. */
bool g_twdt_isr = false;
#if CONFIG_ESP_TASK_WDT_INIT
ESP_PRE_APP_MAIN_HANDLER_REGISTER(init_task_wdt, 120)
{
(void)user_arg;
(void)ctx;
esp_task_wdt_config_t config = {
.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000,
.idle_core_mask = 0,
#if CONFIG_ESP_TASK_WDT_PANIC
.trigger_panic = true,
#endif
};
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
config.idle_core_mask |= BIT(0);
#endif
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
config.idle_core_mask |= BIT(1);
#endif
return esp_task_wdt_init(&config);
}
#endif
// --------------------------------------------------- Definitions -----------------------------------------------------
// ---------------------- Typedefs -------------------------