Merge branch 'feat/esp-system-linux-support-update' into 'master'

feat(esp_system): Add linux support for freertos hooks and system init function

See merge request espressif/esp-idf!46570
This commit is contained in:
Guillaume Souchere
2026-04-02 11:31:42 +02:00
21 changed files with 405 additions and 54 deletions
+10
View File
@@ -8,8 +8,18 @@ if(${target} STREQUAL "linux")
"port/soc/linux/reset_reason.c"
"port/soc/linux/system_internal.c"
"port/esp_system_linux.c"
"freertos_hooks.c"
"startup.c"
INCLUDE_DIRS "include"
PRIV_REQUIRES spi_flash)
# Add custom ld file (used to place system init functions).
# Mach-O (Apple) does not support linker scripts; section bounds are resolved
# at runtime via getsectiondata() so no -T flag is needed there.
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_options(${COMPONENT_TARGET} INTERFACE "-Wl,-T,${CMAKE_CURRENT_SOURCE_DIR}/ld/linux/sections.ld")
endif()
return()
endif()
+1
View File
@@ -74,6 +74,7 @@ menu "ESP System Settings"
config ESP_SYSTEM_SINGLE_CORE_MODE
bool
default y if IDF_TARGET_LINUX
default n
help
Only initialize and use the main core.
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# 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.
@@ -13,12 +13,14 @@ import itertools
import os
import re
import sys
import typing
ESP_SYSTEM_INIT_FN_STR = r'ESP_SYSTEM_INIT_FN'
ESP_SYSTEM_INIT_FN_REGEX_SIMPLE = re.compile(r'ESP_SYSTEM_INIT_FN')
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]+)\)')
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]+)\)'
)
STARTUP_ENTRIES_FILE = 'components/esp_system/system_init_fn.txt'
EXCLUDED_SOURCE_DIRS = {'test_apps', 'host_test', 'host_tests'}
class StartupEntry:
@@ -33,6 +35,15 @@ class StartupEntry:
return f'{self.stage}: {self.priority:3d}: {self.func} in {self.filename} on {self.affinity}'
def should_skip_source_file(filename: str, idf_path: str) -> bool:
relpath_parts = os.path.relpath(filename, idf_path).split(os.sep)
return any(part in EXCLUDED_SOURCE_DIRS for part in relpath_parts)
def strip_comments(contents: str) -> str:
return COMMENT_REGEX.sub('', contents)
def main() -> None:
try:
idf_path = os.environ['IDF_PATH']
@@ -40,7 +51,7 @@ def main() -> None:
raise SystemExit('IDF_PATH must be set before running this script')
has_errors = False
startup_entries = [] # type: typing.List[StartupEntry]
startup_entries: list[StartupEntry] = []
#
# 1. Iterate over all .c and .cpp source files and find ESP_SYSTEM_INIT_FN definitions
@@ -50,24 +61,32 @@ def main() -> None:
glob_iter = glob.glob(os.path.join(idf_path, 'components', '**', f'*.{extension}'), recursive=True)
source_files_iters.append(glob_iter)
for filename in itertools.chain(*source_files_iters):
with open(filename, 'r', encoding='utf-8') as f_obj:
file_contents = f_obj.read()
if ESP_SYSTEM_INIT_FN_STR not in file_contents:
if should_skip_source_file(filename, idf_path):
continue
count_expected = len(ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.findall(file_contents))
found = ESP_SYSTEM_INIT_FN_REGEX.findall(file_contents)
relpath = os.path.relpath(filename, idf_path)
with open(filename, encoding='utf-8') as f_obj:
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
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)
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=os.path.relpath(filename, idf_path),
func=match[0],
stage=match[1],
affinity=match[2],
priority=int(match[3])
filename=relpath, func=match[0], stage=match[1], affinity=match[2], priority=int(match[3])
)
startup_entries.append(entry)
@@ -77,7 +96,7 @@ def main() -> None:
# 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) -> typing.Tuple[str, int, str]:
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)
@@ -88,7 +107,7 @@ def main() -> None:
# 3. Load startup entries list from STARTUP_ENTRIES_FILE, removing comments and empty lines
#
startup_entries_expected_lines = []
with open(os.path.join(idf_path, STARTUP_ENTRIES_FILE), 'r', encoding='utf-8') as startup_entries_expected_file:
with open(os.path.join(idf_path, STARTUP_ENTRIES_FILE), encoding='utf-8') as startup_entries_expected_file:
for line in startup_entries_expected_file:
if line.startswith('#') or len(line.strip()) == 0:
continue
@@ -99,8 +118,13 @@ def main() -> None:
#
diff_lines = list(difflib.unified_diff(startup_entries_expected_lines, startup_entries_lines, lineterm=''))
if len(diff_lines) > 0:
print(('error: startup order doesn\'t match the reference file. '
f'please update {STARTUP_ENTRIES_FILE} to match the actual startup order:'), file=sys.stderr)
print(
(
"error: startup order doesn't match the reference file. "
f'please update {STARTUP_ENTRIES_FILE} to match the actual startup order:'
),
file=sys.stderr,
)
for line in diff_lines:
print(f'{line}', file=sys.stderr)
has_errors = True
+3 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,14 +10,14 @@
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_freertos_hooks.h"
#include "esp_cpu.h"
#include "sdkconfig.h"
#include "esp_cpu.h"
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#include "esp_private/pm_impl.h"
#endif
#endif // CONFIG_PM_ENABLE
//We use just a static array here because it's not expected many components will need
//an idle or tick hook.
@@ -57,7 +57,6 @@ void esp_vApplicationIdleHook(void)
#else
esp_cpu_wait_for_intr();
#endif
}
esp_err_t esp_register_freertos_idle_hook_for_cpu(esp_freertos_idle_cb_t new_idle_cb, UBaseType_t cpuid)
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -69,14 +69,14 @@ typedef struct {
* discarded if the related feature is not used.
*/
#define ESP_SYSTEM_INIT_FN(f, stage_, c, priority, ...) \
static esp_err_t __VA_ARGS__ __esp_system_init_fn_##f(void); \
static __attribute__((used)) _SECTION_ATTR_IMPL(".esp_system_init_fn", priority) \
esp_system_init_fn_t esp_system_init_fn_##f = { \
.fn = ( __esp_system_init_fn_##f), \
static esp_err_t __VA_ARGS__ __esp_sys_init_fn_##f(void); \
static _SECTION_ATTR_IMPL_GENERIC("esp_sys_init_fn", priority) \
esp_system_init_fn_t esp_sys_init_fn_##f = { \
.fn = ( __esp_sys_init_fn_##f), \
.cores = (c), \
.stage = ESP_SYSTEM_INIT_STAGE_##stage_ \
}; \
static esp_err_t __esp_system_init_fn_##f(void)
static esp_err_t __esp_sys_init_fn_##f(void)
#ifdef CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
#define ESP_SYSTEM_INIT_ALL_CORES BIT(0)
@@ -368,9 +368,9 @@ SECTIONS
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(.);
ALIGNED_SYMBOL(4, _esp_sys_init_fn_start)
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_sys_init_fn.*)))
_esp_sys_init_fn_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
@@ -372,9 +372,9 @@ SECTIONS
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(.);
ALIGNED_SYMBOL(4, _esp_sys_init_fn_start)
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_sys_init_fn.*)))
_esp_sys_init_fn_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
@@ -381,9 +381,9 @@ SECTIONS
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(.);
ALIGNED_SYMBOL(4, _esp_sys_init_fn_start)
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_sys_init_fn.*)))
_esp_sys_init_fn_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
+3 -3
View File
@@ -135,9 +135,9 @@
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(.);
ALIGNED_SYMBOL(4, _esp_sys_init_fn_start)
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_sys_init_fn.*)))
_esp_sys_init_fn_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
. = ALIGN(ALIGNOF(SECTION_AFTER_FLASH_RODATA));
@@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
SECTIONS
{
.esp_sys_init_fn :
{
PROVIDE(_esp_sys_init_fn_start = .);
KEEP(*(SORT(.esp_sys_init_fn*)))
PROVIDE(_esp_sys_init_fn_end = .);
}
}
INSERT AFTER .data;
+47 -9
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -34,14 +34,41 @@ uint64_t g_startup_time = 0;
extern void esp_startup_start_app(void);
// Entry point for core 0 from hardware init (port layer)
#if defined(__APPLE__) && defined(__MACH__)
static void start_cpu0_default(void);
__attribute__((weak, noreturn)) void start_cpu0(void)
{
start_cpu0_default();
__builtin_unreachable();
}
#else
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn));
#endif
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
// Entry point for core [1..X] from hardware init (port layer)
#if defined(__APPLE__) && defined(__MACH__)
static void start_cpu_other_cores_default(void);
__attribute__((weak, noreturn)) void start_cpu_other_cores(void)
{
start_cpu_other_cores_default();
__builtin_unreachable();
}
#else
void start_cpu_other_cores(void) __attribute__((weak, alias("start_cpu_other_cores_default"))) __attribute__((noreturn));
#endif
// App entry point for core [1..X]
#if defined(__APPLE__) && defined(__MACH__)
static void esp_startup_start_app_other_cores_default(void);
__attribute__((weak, noreturn)) void esp_startup_start_app_other_cores(void)
{
esp_startup_start_app_other_cores_default();
__builtin_unreachable();
}
#else
void esp_startup_start_app_other_cores(void) __attribute__((weak, alias("esp_startup_start_app_other_cores_default"))) __attribute__((noreturn));
#endif
static volatile bool s_system_inited[SOC_CPU_CORES_NUM] = { false };
@@ -79,10 +106,14 @@ static void __do_global_ctors_1(void)
}
#endif // CONFIG_COMPILER_CXX_EXCEPTIONS
/* declare the start and stop symbols surrounding the array of init functions
* registered by calling the system init function macros */
_SECTION_ATTR_SYMBOL_DECL_GENERIC(esp_system_init_fn_t, esp_sys_init_fn)
/**
* @brief Call component init functions defined using ESP_SYSTEM_INIT_Fn macros.
* @brief Call component init functions defined using the system init function macros.
* The esp_system_init_fn_t structures describing these functions are collected into
* an array [_esp_system_init_fn_array_start, _esp_system_init_fn_array_end) by the
* an array [_esp_sys_init_fn_start, _esp_sys_init_fn_end) by the
* linker. The functions are sorted by their priority value.
* The sequence of the init function calls (sorted by priority) is documented in
* system_init_fn.txt file.
@@ -91,13 +122,10 @@ static void __do_global_ctors_1(void)
__attribute__((no_sanitize_undefined)) /* TODO: IDF-8133 */
static void do_system_init_fn(uint32_t stage_num)
{
extern esp_system_init_fn_t _esp_system_init_fn_array_start;
extern esp_system_init_fn_t _esp_system_init_fn_array_end;
esp_system_init_fn_t *p;
const esp_system_init_fn_t *p;
int core_id = esp_cpu_get_core_id();
for (p = &_esp_system_init_fn_array_start; p < &_esp_system_init_fn_array_end; ++p) {
for (p = _SECTION_START(esp_sys_init_fn); p < _SECTION_END(esp_sys_init_fn); ++p) {
if (p->stage == stage_num && (p->cores & BIT(core_id)) != 0) {
// During core init, stdout is not initialized yet, so use early logging.
ESP_EARLY_LOGD(TAG, "calling init function: %p on core: %d", p->fn, core_id);
@@ -171,7 +199,6 @@ static void do_secondary_init(void)
static void start_cpu0_default(void)
{
extern void __libc_init_array(void);
// Initialize core components and services.
// Operations that needs the cache to be disabled have to be done here.
do_core_init();
@@ -180,7 +207,10 @@ static void start_cpu0_default(void)
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
__do_global_ctors_1();
#endif
#if !CONFIG_IDF_TARGET_LINUX
extern void __libc_init_array(void);
__libc_init_array();
#endif // !CONFIG_IDF_TARGET_LINUX
/* ----------------------------------Separator-----------------------------
* After this stage, other CPU start running with the cache, however the scheduler (and ipc service) is not available.
@@ -199,3 +229,11 @@ static void start_cpu0_default(void)
ESP_INFINITE_LOOP();
}
#if CONFIG_IDF_TARGET_LINUX && !defined(ESP_SYSTEM_LINUX_NO_MAIN)
__attribute__((weak)) int main(int argc, char **argv)
{
start_cpu0();
return 0;
}
#endif // CONFIG_IDF_TARGET_LINUX
@@ -0,0 +1,9 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(test_sys_init_fn)
@@ -0,0 +1,4 @@
idf_component_register(SRCS "test_sys_init_fn.c"
"test_init_fn_defs.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_system)
@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This file defines system init functions whose execution is verified
* by the test cases in test_sys_init_fn.c.
*
* The functions are placed into the esp_sys_init_fn linker section via the
* system init macro. On Linux this uses ELF section sorting; on macOS
* the same section is resolved at runtime via getsectiondata().
*
* Important: this file must be compiled into the same binary as the test
* runner so the linker/loader sees the section entries.
*/
#include "esp_private/startup_internal.h"
#include "test_init_fn_defs.h"
/* ---- Global state read back by the test cases ---- */
int trace_log[INIT_FN_TRACE_MAX];
int trace_count = 0;
bool core_prio_200_executed = false;
bool core_prio_250_executed = false;
bool secondary_prio_200_executed = false;
bool secondary_prio_250_executed = false;
/* ---- Helper ---- */
static void trace_append(int tag)
{
if (trace_count < INIT_FN_TRACE_MAX) {
trace_log[trace_count++] = tag;
}
}
/* ---- CORE-stage init functions (executed during do_core_init) ---- */
/*
* Two CORE-stage functions with different priorities.
* Priority 200 must execute before priority 250.
* We use tag values equal to the priority for easy identification.
*/
ESP_SYSTEM_INIT_FN(test_core_prio_200, CORE, BIT(0), 200)
{
core_prio_200_executed = true;
trace_append(200);
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(test_core_prio_250, CORE, BIT(0), 250)
{
core_prio_250_executed = true;
trace_append(250);
return ESP_OK;
}
/* ---- SECONDARY-stage init functions (executed during do_secondary_init) ---- */
ESP_SYSTEM_INIT_FN(test_secondary_prio_200, SECONDARY, BIT(0), 200)
{
secondary_prio_200_executed = true;
trace_append(1200); /* offset by 1000 so we can distinguish stage in trace */
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(test_secondary_prio_250, SECONDARY, BIT(0), 250)
{
secondary_prio_250_executed = true;
trace_append(1250);
return ESP_OK;
}
@@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
/**
* Maximum number of init function invocations we track for ordering tests.
*/
#define INIT_FN_TRACE_MAX 16
/**
* Global trace log filled by init functions to record their execution order.
* Each init function appends its own tag (an arbitrary integer) to
* trace_log[trace_count] and increments trace_count.
*/
extern int trace_log[INIT_FN_TRACE_MAX];
extern int trace_count;
/* Flags set by individual init functions so tests can verify they executed */
extern bool core_prio_200_executed;
extern bool core_prio_250_executed;
extern bool secondary_prio_200_executed;
extern bool secondary_prio_250_executed;
@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Test cases for the ESP_SYSTEM_INIT_FN mechanism.
*
* The init functions are defined in test_init_fn_defs.c and are executed
* automatically during startup (before app_main). These tests inspect the
* side-effects left by those functions to verify:
*
* 1. Init functions actually executed.
* 2. CORE-stage functions executed before SECONDARY-stage functions.
* 3. Within the same stage, lower priority values execute first.
* 4. All expected functions ran (no silent drops).
*/
#include <stdio.h>
#include "unity.h"
#include "unity_test_runner.h"
#include "test_init_fn_defs.h"
/* ---------- Test cases ---------- */
TEST_CASE("CORE init functions executed", "[sys_init_fn]")
{
TEST_ASSERT_TRUE_MESSAGE(core_prio_200_executed,
"CORE priority-200 init function did not execute");
TEST_ASSERT_TRUE_MESSAGE(core_prio_250_executed,
"CORE priority-250 init function did not execute");
}
TEST_CASE("SECONDARY init functions executed", "[sys_init_fn]")
{
TEST_ASSERT_TRUE_MESSAGE(secondary_prio_200_executed,
"SECONDARY priority-200 init function did not execute");
TEST_ASSERT_TRUE_MESSAGE(secondary_prio_250_executed,
"SECONDARY priority-250 init function did not execute");
}
TEST_CASE("all four init functions traced", "[sys_init_fn]")
{
/* We registered 4 init functions total (2 CORE + 2 SECONDARY) */
TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(4, trace_count,
"expected at least 4 init function traces");
}
TEST_CASE("CORE stage runs before SECONDARY stage", "[sys_init_fn]")
{
/*
* In the trace log, CORE entries have tags < 1000,
* SECONDARY entries have tags >= 1000.
* All CORE entries must appear before any SECONDARY entry.
*/
int first_secondary = -1;
int last_core = -1;
for (int i = 0; i < trace_count; i++) {
if (trace_log[i] < 1000) {
last_core = i;
} else if (first_secondary < 0) {
first_secondary = i;
}
}
TEST_ASSERT_TRUE_MESSAGE(last_core >= 0, "no CORE trace entries found");
TEST_ASSERT_TRUE_MESSAGE(first_secondary >= 0, "no SECONDARY trace entries found");
TEST_ASSERT_LESS_THAN_INT_MESSAGE(first_secondary, last_core,
"a CORE init function ran after a SECONDARY one");
}
TEST_CASE("priority ordering within CORE stage", "[sys_init_fn]")
{
/*
* Within the CORE stage, priority 200 (tag 200) must appear
* before priority 250 (tag 250) in the trace log.
*/
int pos_200 = -1;
int pos_250 = -1;
for (int i = 0; i < trace_count; i++) {
if (trace_log[i] == 200 && pos_200 < 0) {
pos_200 = i;
}
if (trace_log[i] == 250 && pos_250 < 0) {
pos_250 = i;
}
}
TEST_ASSERT_TRUE_MESSAGE(pos_200 >= 0, "CORE prio-200 not in trace");
TEST_ASSERT_TRUE_MESSAGE(pos_250 >= 0, "CORE prio-250 not in trace");
TEST_ASSERT_LESS_THAN_INT_MESSAGE(pos_250, pos_200,
"CORE prio-200 did not run before prio-250");
}
TEST_CASE("priority ordering within SECONDARY stage", "[sys_init_fn]")
{
int pos_1200 = -1;
int pos_1250 = -1;
for (int i = 0; i < trace_count; i++) {
if (trace_log[i] == 1200 && pos_1200 < 0) {
pos_1200 = i;
}
if (trace_log[i] == 1250 && pos_1250 < 0) {
pos_1250 = i;
}
}
TEST_ASSERT_TRUE_MESSAGE(pos_1200 >= 0, "SECONDARY prio-200 not in trace");
TEST_ASSERT_TRUE_MESSAGE(pos_1250 >= 0, "SECONDARY prio-250 not in trace");
TEST_ASSERT_LESS_THAN_INT_MESSAGE(pos_1250, pos_1200,
"SECONDARY prio-200 did not run before prio-250");
}
/* ---------- Entry point ---------- */
void app_main(void)
{
printf("Running sys_init_fn host test app\n");
unity_run_menu();
}
@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded_idf.dut import IdfDut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.host_test
@idf_parametrize('target', ['linux'], indirect=['target'])
def test_sys_init_fn_linux(dut: IdfDut) -> None:
dut.run_all_single_board_cases(timeout=60)
@pytest.mark.host_test
@pytest.mark.macos
@idf_parametrize('target', ['linux'], indirect=['target'])
def test_sys_init_fn_macos(dut: IdfDut) -> None:
dut.run_all_single_board_cases(timeout=60)
@@ -0,0 +1 @@
CONFIG_IDF_TARGET="linux"
@@ -98,7 +98,7 @@ static void main_task(void* args)
vTaskDelete(NULL);
}
int main(int argc, const char **argv)
void esp_startup_start_app(void)
{
// This makes sure that stdio is always synchronized so that idf.py monitor
// and other tools read text output on time.