Merge branch 'feature/system_init_priorities' into 'master'

esp_system: introduce priorities for startup functions

Closes IDFGH-5683 and IDF-4954

See merge request espressif/esp-idf!18159
This commit is contained in:
Ivan Grokhotkov 2022-07-14 06:22:25 +08:00
commit 230e732018
16 changed files with 266 additions and 69 deletions

View File

@ -148,6 +148,15 @@ check_esp_err_to_name:
- ./gen_esp_err_to_name.py
- git diff --exit-code -- ../components/esp_common/src/esp_err_to_name.c || { echo 'Differences found. Please run gen_esp_err_to_name.py and commit the changes.'; exit 1; }
check_esp_system:
extends:
- .pre_check_base_template
- .rules:build
tags:
- build
script:
- python components/esp_system/check_system_init_priorities.py
scan_tests:
extends:
- .pre_check_base_template

View File

@ -8,6 +8,7 @@
#include "esp_log.h"
#include "esp_app_trace.h"
#include "esp_app_trace_port.h"
#include "esp_private/startup_internal.h"
#ifdef CONFIG_APPTRACE_DEST_UART0
#define ESP_APPTRACE_DEST_UART_NUM 0
@ -75,6 +76,11 @@ esp_err_t esp_apptrace_init(void)
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(esp_apptrace_init, ESP_SYSTEM_INIT_ALL_CORES, 115)
{
return esp_apptrace_init();
}
void esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size)
{
esp_apptrace_channel_t *ch;

View File

@ -12,6 +12,7 @@
#include "esp_app_trace.h"
#include "esp_log.h"
#include "esp_private/startup_internal.h"
const static char *TAG = "segger_rtt";
@ -288,4 +289,17 @@ int SEGGER_RTT_ConfigDownBuffer(unsigned BufferIndex, const char* sName, void* p
return 0;
}
/*************************** Init hook ****************************
*
* This init function is placed here because this port file will be
* linked whenever SystemView is used.
*/
ESP_SYSTEM_INIT_FN(sysview_init, BIT(0), 120)
{
SEGGER_SYSVIEW_Conf();
return ESP_OK;
}
/*************************** End of file ****************************/

View File

@ -45,6 +45,7 @@
#include "esp_private/brownout.h"
#include "esp_private/sleep_retention.h"
#include "esp_private/esp_clk.h"
#include "esp_private/startup_internal.h"
#ifdef CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
@ -1395,3 +1396,15 @@ void rtc_sleep_enable_ultra_low(bool enable)
{
s_ultra_low_enabled = enable;
}
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND && !CONFIG_PM_SLP_DISABLE_GPIO
ESP_SYSTEM_INIT_FN(esp_sleep_startup_init, BIT(0), 105)
{
// Configure to isolate (disable the Input/Output/Pullup/Pulldown
// function of the pin) all GPIO pins in sleep state
esp_sleep_config_gpio_isolate();
// Enable automatic switching of GPIO configuration
esp_sleep_enable_gpio_switch(true);
return ESP_OK;
}
#endif

View File

@ -0,0 +1,105 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2022 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.
# 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.
import difflib
import glob
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*([0-9]+)\)')
STARTUP_ENTRIES_FILE = 'components/esp_system/system_init_fn.txt'
class StartupEntry:
def __init__(self, filename: str, func: str, affinity: str, priority: int) -> None:
self.filename = filename
self.func = func
self.affinity = affinity
self.priority = priority
def __str__(self) -> str:
return f'{self.priority:3d}: {self.func} in {self.filename} on {self.affinity}'
def main() -> None:
try:
idf_path = os.environ['IDF_PATH']
except KeyError:
raise SystemExit('IDF_PATH must be set before running this script')
has_errors = False
startup_entries = [] # type: typing.List[StartupEntry]
#
# 1. Iterate over all .c and .cpp source files and find ESP_SYSTEM_INIT_FN definitions
#
source_files_iters = []
for extension in ('c', 'cpp'):
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') as f_obj:
file_contents = f_obj.read()
if ESP_SYSTEM_INIT_FN_STR not in file_contents:
continue
count_expected = len(ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.findall(file_contents))
found = ESP_SYSTEM_INIT_FN_REGEX.findall(file_contents)
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=os.path.relpath(filename, idf_path),
func=match[0],
affinity=match[1],
priority=int(match[2])
)
startup_entries.append(entry)
#
# 2. Sort the ESP_SYSTEM_INIT_FN functions in C source files by priority
#
startup_entries = list(sorted(startup_entries, key=lambda e: e.priority))
startup_entries_lines = [str(entry) for entry in startup_entries]
#
# 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') as startup_entries_expected_file:
for line in startup_entries_expected_file:
if line.startswith('#') or len(line.strip()) == 0:
continue
startup_entries_expected_lines.append(line.rstrip())
#
# 4. Print the list of differences, if any
#
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)
for line in diff_lines:
print(f'{line}', file=sys.stderr)
has_errors = True
if has_errors:
raise SystemExit(1)
if __name__ == '__main__':
main()

View File

@ -7,6 +7,8 @@
#pragma once
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_bit_defs.h"
#include "soc/soc_caps.h"
#include "hal/cpu_hal.h"
@ -36,25 +38,44 @@ extern sys_startup_fn_t const g_startup_fn[1];
void startup_resume_other_cores(void);
#endif
/**
* Internal structure describing ESP_SYSTEM_INIT_FN startup functions
*/
typedef struct {
void (*fn)(void);
uint32_t cores;
esp_err_t (*fn)(void); /*!< Pointer to the startup function */
uint32_t cores; /*!< Bit map of cores where the function has to be called */
} esp_system_init_fn_t;
/*
* Declare an component initialization function that will execute on the specified cores (ex. if BIT0 == 1, will execute
* on CORE0, CORE1 if BIT1 and so on).
/**
* @brief Define a system initialization function which will be executed on the specified cores
*
* @note Initialization functions should be placed in a compilation unit where at least one other
* symbol is referenced 'meaningfully' in another compilation unit, otherwise this gets discarded during linking. (By
* 'meaningfully' we mean the reference should not itself get optimized out by the compiler/discarded by the linker).
* @param f function name (identifier)
* @param c bit mask of cores to execute the function on (ex. if BIT0 is set, the function
* will be executed on CPU 0, if BIT1 is set - on CPU 1, and so on)
* @param priority integer, priority of the initialization function. Higher values mean that
* the function will be executed later in the process.
* @param (varargs) optional, additional attributes for the function declaration (such as IRAM_ATTR)
*
* The function defined using this macro must return ESP_OK on success. Any other value will be
* logged and the startup process will abort.
*
* Initialization functions should be placed in a compilation unit where at least one other
* symbol is referenced in another compilation unit. This means that the reference should not itself
* get optimized out by the compiler or discarded by the linker if the related feature is used.
* It is, on the other hand, a good practice to make sure the initialization function does get
* discarded if the related feature is not used.
*/
#define ESP_SYSTEM_INIT_FN(f, c, ...) \
static void __attribute__((used)) __VA_ARGS__ __esp_system_init_fn_##f(void); \
static __attribute__((used)) esp_system_init_fn_t _SECTION_ATTR_IMPL(".esp_system_init_fn", f) \
esp_system_init_fn_##f = { .fn = ( __esp_system_init_fn_##f), .cores = (c) }; \
static __attribute__((used)) __VA_ARGS__ void __esp_system_init_fn_##f(void) // [refactor-todo] this can be made public API if we allow components to declare init functions,
// instead of calling them explicitly
#define ESP_SYSTEM_INIT_FN(f, 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), .cores = (c) }; \
static esp_err_t __esp_system_init_fn_##f(void)
#ifdef CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
#define ESP_SYSTEM_INIT_ALL_CORES BIT(0)
#else
#define ESP_SYSTEM_INIT_ALL_CORES (BIT(SOC_CPU_CORES_NUM) - 1)
#endif
extern uint64_t g_startup_time; // Startup time that serves as the point of origin for system time. Should be set by the entry
// function in the port layer. May be 0 as well if this is not backed by a persistent counter, in which case

View File

@ -185,10 +185,6 @@ SECTIONS
*(.gnu.linkonce.s2.*)
*(.jcr)
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
@ -304,6 +300,10 @@ SECTIONS
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -47,10 +47,6 @@ SECTIONS
*(.gnu.linkonce.s2.*)
*(.jcr)
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
@ -208,6 +204,10 @@ SECTIONS
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -157,10 +157,6 @@ SECTIONS
*(.gnu.linkonce.s2.*)
*(.jcr)
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
@ -318,6 +314,10 @@ SECTIONS
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -160,10 +160,6 @@ SECTIONS
*(.gnu.linkonce.s2.*)
*(.jcr)
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
@ -321,6 +317,10 @@ SECTIONS
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -207,10 +207,6 @@ SECTIONS
*(.gnu.linkonce.s2.*)
*(.jcr)
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
@ -322,6 +318,10 @@ SECTIONS
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -194,10 +194,6 @@ SECTIONS
*(.gnu.linkonce.s2.*)
*(.jcr)
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT(.esp_system_init_fn) SORT(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
mapping[dram0_data]
_data_end = ABSOLUTE(.);
@ -347,6 +343,10 @@ SECTIONS
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
/* System init functions registered via ESP_SYSTEM_INIT_FN */
_esp_system_init_fn_array_start = ABSOLUTE(.);
KEEP (*(SORT_BY_INIT_PRIORITY(.esp_system_init_fn.*)))
_esp_system_init_fn_array_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -30,7 +30,6 @@
#include "esp_efuse.h"
#include "esp_flash_encrypt.h"
#include "esp_secure_boot.h"
#include "esp_sleep.h"
#include "esp_xt_wdt.h"
#if __has_include("esp_ota_ops.h")
@ -48,10 +47,6 @@
#include "esp_core_dump.h"
#endif
#if CONFIG_APPTRACE_ENABLE
#include "esp_app_trace.h"
#endif
#include "esp_private/dbg_stubs.h"
#if CONFIG_PM_ENABLE
@ -179,17 +174,25 @@ static void do_global_ctors(void)
#if __riscv
for (p = &__init_priority_array_start; p < &__init_priority_array_end; ++p) {
ESP_EARLY_LOGD(TAG, "calling init function: %p", *p);
ESP_LOGD(TAG, "calling init function: %p", *p);
(*p)();
}
#endif
for (p = &__init_array_end - 1; p >= &__init_array_start; --p) {
ESP_EARLY_LOGD(TAG, "calling init function: %p", *p);
ESP_LOGD(TAG, "calling init function: %p", *p);
(*p)();
}
}
/**
* @brief Call component init functions defined using ESP_SYSTEM_INIT_Fn 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
* 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.
*/
static void do_system_init_fn(void)
{
extern esp_system_init_fn_t _esp_system_init_fn_array_start;
@ -197,14 +200,20 @@ static void do_system_init_fn(void)
esp_system_init_fn_t *p;
for (p = &_esp_system_init_fn_array_end - 1; p >= &_esp_system_init_fn_array_start; --p) {
if (p->cores & BIT(cpu_hal_get_core_id())) {
(*(p->fn))();
int core_id = cpu_hal_get_core_id();
for (p = &_esp_system_init_fn_array_start; p < &_esp_system_init_fn_array_end; ++p) {
if (p->cores & BIT(core_id)) {
ESP_LOGD(TAG, "calling init function: %p on core: %d", p->fn, core_id);
esp_err_t err = (*(p->fn))();
if (err != ESP_OK) {
ESP_LOGE(TAG, "init function %p has failed (0x%x), aborting", p->fn, err);
abort();
}
}
}
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
s_system_inited[cpu_hal_get_core_id()] = true;
s_system_inited[core_id] = true;
#endif
}
@ -216,6 +225,9 @@ static void esp_startup_start_app_other_cores_default(void)
}
}
/* This function has to be in IRAM, as while it is running on CPU1, CPU0 may do some flash operations
* (e.g. initialize the core dump), which means that cache will be disabled.
*/
static void IRAM_ATTR start_cpu_other_cores_default(void)
{
do_system_init_fn();
@ -428,26 +440,8 @@ static void start_cpu0_default(void)
while (1);
}
IRAM_ATTR ESP_SYSTEM_INIT_FN(init_components0, BIT(0))
ESP_SYSTEM_INIT_FN(init_components0, BIT(0), 200)
{
esp_timer_init();
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND && !CONFIG_PM_SLP_DISABLE_GPIO
// Configure to isolate (disable the Input/Output/Pullup/Pulldown
// function of the pin) all GPIO pins in sleep state
esp_sleep_config_gpio_isolate();
// Enable automatic switching of GPIO configuration
esp_sleep_enable_gpio_switch(true);
#endif
#if CONFIG_APPTRACE_ENABLE
esp_err_t err = esp_apptrace_init();
assert(err == ESP_OK && "Failed to init apptrace module on PRO CPU!");
#endif
#if CONFIG_APPTRACE_SV_ENABLE
SEGGER_SYSVIEW_Conf();
#endif
#if CONFIG_ESP_DEBUG_STUBS_ENABLE
esp_dbg_stubs_init();
#endif
@ -474,4 +468,6 @@ IRAM_ATTR ESP_SYSTEM_INIT_FN(init_components0, BIT(0))
_Unwind_SetNoFunctionContextInstall(1);
_Unwind_SetEnableExceptionFdeSorting(0);
#endif // CONFIG_COMPILER_CXX_EXCEPTIONS
return ESP_OK;
}

View File

@ -0,0 +1,28 @@
# This file documents the expected order of execution of ESP_SYSTEM_INIT_FN functions.
#
# 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 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.
#
# Entries are ordered by the order of execution (i.e. from low priority values to high ones).
# Each line has the following format:
# prio: function_name in path/to/source_file on affinity_expression
# Where:
# prio: priority value (higher value means function is executed later)
# affinity_expression: bit map of cores the function is executed on
# esp_timer has to be initialized early, since it is used by several other components
100: esp_timer_startup_init in components/esp_timer/src/esp_timer.c on BIT(0)
# esp_sleep doesn't have init dependencies
105: esp_sleep_startup_init in components/esp_hw_support/sleep_modes.c on BIT(0)
# app_trace has to be initialized before systemview
115: esp_apptrace_init in components/app_trace/app_trace.c on ESP_SYSTEM_INIT_ALL_CORES
120: sysview_init in components/app_trace/sys_view/esp/SEGGER_RTT_esp.c on BIT(0)
# the rest of the components which are initialized from startup.c
# [refactor-todo]: move init calls into respective components
200: init_components0 in components/esp_system/startup.c on BIT(0)

View File

@ -460,6 +460,11 @@ out:
return ESP_ERR_NO_MEM;
}
ESP_SYSTEM_INIT_FN(esp_timer_startup_init, BIT(0), 100)
{
return esp_timer_init();
}
esp_err_t esp_timer_deinit(void)
{
if (!is_initialized()) {

View File

@ -142,7 +142,7 @@ The primary system initialization stage includes:
- Initialize SPI flash API support.
- Call global C++ constructors and any C functions marked with ``__attribute__((constructor))``.
Secondary system initialization allows individual components to be initialized. If a component has an initialization function annotated with the ``ESP_SYSTEM_INIT_FN`` macro, it will be called as part of secondary initialization.
Secondary system initialization allows individual components to be initialized. If a component has an initialization function annotated with the ``ESP_SYSTEM_INIT_FN`` macro, it will be called as part of secondary initialization. Component initialization functions have priorities assigned to them to ensure the desired initialization order. The priorities are documented in :component_file:`esp_system/system_init_fn.txt` and ``ESP_SYSTEM_INIT_FN`` definition in source code are checked against this file.
.. _app-main-task: