ESP32 (system startup process)-analysis of the system startup process in the Vscode IDF installation package (12)

Tip: This blog is used as a study note, and I hope to correct me if there are mistakes

1. ESP32 boot process introduction

  Reference materials: ESP IDF Programming Manual V4.4
  Here I mainly analyze the startup process of esp32 from the system code layer. The freertos operating system is used by default in the esp32 demo. When we first created the project or in the demo, there must be a void app_main(void) function. This is an official regulation. We will start from this function and analyze the calling process of the function. , Peel off the entire startup process layer by layer like peeling off the cocoon.
  The startup process is in two layers, one is the system layer freeRTOS, and the other is in the system layer, esp_system, I will start to analyze from these two layers.

Two, freeRTOS layer

  The app_main function existed at the beginning of creation. To analyze who is calling it, we can use vscode to open the freertos folder under components in the installation package and search for app_main.

/用户安装位置/esp-idf/components/freertos/port/port_common.c

   The following two functions exist in the port_common.c .c file

void esp_startup_start_app_common(void)
{
    
    
#if CONFIG_ESP_INT_WDT
    esp_int_wdt_init();
    //Initialize the interrupt watch dog for CPU0.
    esp_int_wdt_cpu_init();
#endif

    esp_crosscore_int_init();

#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
    esp_gdbstub_init();
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME

    portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main",
                                                ESP_TASK_MAIN_STACK, NULL,
                                                ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE);
    assert(res == pdTRUE);
    (void)res;
}

#if !CONFIG_FREERTOS_UNICORE
static volatile bool s_other_cpu_startup_done = false;
static bool other_cpu_startup_idle_hook_cb(void)
{
    
    
    s_other_cpu_startup_done = true;
    return true;
}
#endif

static void main_task(void* args)
{
    
    
#if !CONFIG_FREERTOS_UNICORE
    // Wait for FreeRTOS initialization to finish on other core, before replacing its startup stack
    esp_register_freertos_idle_hook_for_cpu(other_cpu_startup_idle_hook_cb, !xPortGetCoreID());
    while (!s_other_cpu_startup_done) {
    
    
        ;
    }
    esp_deregister_freertos_idle_hook_for_cpu(other_cpu_startup_idle_hook_cb, !xPortGetCoreID());
#endif

    // [refactor-todo] check if there is a way to move the following block to esp_system startup
    heap_caps_enable_nonos_stack_heaps();

    // Now we have startup stack RAM available for heap, enable any DMA pool memory
#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL
    if (g_spiram_ok) {
    
    
        esp_err_t r = esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL);
        if (r != ESP_OK) {
    
    
            ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool (error 0x%x)", r);
            abort();
        }
    }
#endif

    //Initialize task wdt if configured to do so
#ifdef CONFIG_ESP_TASK_WDT_PANIC
    ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true));
#elif CONFIG_ESP_TASK_WDT
    ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false));
#endif

    //Add IDLE 0 to task wdt
#ifdef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
    TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
    if(idle_0 != NULL){
    
    
        ESP_ERROR_CHECK(esp_task_wdt_add(idle_0));
    }
#endif
    //Add IDLE 1 to task wdt
#ifdef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
    TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
    if(idle_1 != NULL){
    
    
        ESP_ERROR_CHECK(esp_task_wdt_add(idle_1));
    }
#endif

    app_main();
    vTaskDelete(NULL);
}

  The main execution logic in main_task is some idle hook functions and watchdog stuff. Then call app_main, and finally delete the task.
  In esp_startup_start_app_common is also about the configuration of the watchdog and the initialization of the system core interrupt system for the CPU, and finally create the task of main_task.

/用户安装位置/esp-idf/components/freertos/port/xtensa/port.c的文件中

  In the above path, the esp_startup_start_app_common function is called in the esp_startup_start_app in the port.c .c file, and the task scheduling is started after executing the esp_startup_start_app_common function.
  The above is the process of system startup in the entire freertos.

Three, esp_system layer

  In the above freertos startup, we continue to analyze esp_startup_start_app, and continue to see how this function is called. This function can be found in esp_system in the components file to be called.

/用户安装位置/esp-idf/components/esp_system/startup.c

  The esp_startup_start_app function is called in the start_cpu0_default function, and the specific code is as follows.

static void start_cpu0_default(void)
{
    
    

    ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
    int cpu_freq = esp_clk_cpu_freq();
    ESP_EARLY_LOGI(TAG, "cpu freq: %d", cpu_freq);

    // Display information about the current running image.
    if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) {
    
    
        const esp_app_desc_t *app_desc = esp_ota_get_app_description();
        ESP_EARLY_LOGI(TAG, "Application information:");
#ifndef CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR
        ESP_EARLY_LOGI(TAG, "Project name:     %s", app_desc->project_name);
#endif
#ifndef CONFIG_APP_EXCLUDE_PROJECT_VER_VAR
        ESP_EARLY_LOGI(TAG, "App version:      %s", app_desc->version);
#endif
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
        ESP_EARLY_LOGI(TAG, "Secure version:   %d", app_desc->secure_version);
#endif
#ifdef CONFIG_APP_COMPILE_TIME_DATE
        ESP_EARLY_LOGI(TAG, "Compile time:     %s %s", app_desc->date, app_desc->time);
#endif
        char buf[17];
        esp_ota_get_app_elf_sha256(buf, sizeof(buf));
        ESP_EARLY_LOGI(TAG, "ELF file SHA256:  %s...", buf);
        ESP_EARLY_LOGI(TAG, "ESP-IDF:          %s", app_desc->idf_ver);
    }

    // Initialize core components and services.
    do_core_init();

    // Execute constructors.
    do_global_ctors();

    // Execute init functions of other components; blocks
    // until all cores finish (when !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE).
    do_secondary_init();

    // Now that the application is about to start, disable boot watchdog
#ifndef CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE
    wdt_hal_context_t rtc_wdt_ctx = {
    
    .inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
    wdt_hal_write_protect_disable(&rtc_wdt_ctx);
    wdt_hal_disable(&rtc_wdt_ctx);
    wdt_hal_write_protect_enable(&rtc_wdt_ctx);
#endif

#if SOC_CPU_CORES_NUM > 1 && !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
    s_system_full_inited = true;
#endif

    esp_startup_start_app();
    while (1);
}

  In this function, some esp32 system information is mainly printed, such as: CPU frequency, partition, project name, APP version, compilation time, filling the provided buffer with the SHA256 of the ELF file, etc., and then calling esp_startup_start_app, and finally entering the dead in the loop.
  Among them, start_cpu0_default uses weak connections. For a detailed introduction to __attribute__, please refer to this blog. The use of attribute in C , the use of __attribute__ in C language , the weak and alias attributes of attribute _

// Entry point for core 0 from hardware init (port layer)
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn));

  Due to the role of attribute, we continue to search for start_cpu0. In the source code startup.c, we find the following array of g_startup_fn.

const sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM] = {
    
     [0] = start_cpu0,
#if SOC_CPU_CORES_NUM > 1
    [1 ... SOC_CPU_CORES_NUM - 1] = start_cpu_other_cores
#endif
};

  And the data type of this array is sys_startup_fn_t type, let's take a look at what type this type is. In the header file start_internal.h we can see the original definition of sys_startup_fn_t.

typedef void (*sys_startup_fn_t)(void);

  Here, a function pointer mainly defined by typedef, that is, g_startup_fn is an array of function pointers. Here is an article about the declaration of typedef's function pointer: Function pointer defined by typedef
  Among them, I understand the distinction between function pointer and pointer function according to Chinese grammar and C language bracket priority, otherwise it is easy to confuse.
  Ordinary function: function as the subject, which we often use is also the easiest to understand. A function consists of a return data type, a function name, and function parameters.
  Ordinary functions are defined as follows:

<返回数据类型> <函数名> (<参数>) {
    
    
	代码;
}

  Pointer function: The function is the subject and is modified by the pointer, so the subject is a function, and the function naturally has a return type and parameters of the function.
  Pointer functions are defined as follows:

<返回数据类型> *<函数名>( <参数>) {
    
    
	代码;
}

  Function pointer: The pointer is the subject and is modified by the function, so the subject is a pointer, and the pointer naturally has its data type.

<函数返回数据类型> (* <函数名>) (<参数>) {
    
    
	代码;
}

  The following are the definition examples of ordinary functions, pointer functions, and function pointers. The difference between pointer functions and ordinary functions is that there is an extra "*" in front of the function name.

void   fun(int x, int y);	//普通函数
void  *fun(int x, int y);	//指针函数
void  (*fun)(int x, int y);	//函数指针

  Continue to see a #define macro definition in the header file start_internal.h

// Utility to execute `sys_startup_fn_t` for the current core.
#define SYS_STARTUP_FN()  ((*g_startup_fn[(cpu_hal_get_core_id())])())

  Then let's see where to call SYS_STARTUP_FN(), call the macro definition SYS_STARTUP_FN() in /installation path/esp-idf/components/esp_system/port/cpu_start.c,
call SYS_STARTUP_FN () in the functions of call_start_cpu0 and call_start_cpu1 ( ), some series of ESP32 are dual-core, adapt and call different call_start_cpu_x according to different series of chips.
At the end is ENTRY (call_start_cpu0);
  At this point, the analysis of the startup process of ESP32 is over, and the entire framework is peeled off layer by layer like cocoon peeling. There are mainly two layers here, one is the freeRTOS layer, and the other is The layer is the entire system layer esp_system for esp32. Understanding the core startup process facilitates the understanding of the entire system framework and the direction of troubleshooting for possible problems that may arise later.
  There are a lot of startup files overall, and a lot of conditional compilation is used to adapt to different cpus. There are also advanced usages of C language in it, which can be learned by the way. Some analysis may be incorrect, and I hope everyone can correct me.

Guess you like

Origin blog.csdn.net/believe666/article/details/127150011