ESP32-C3 应用程序的启动流程
前言
对于 ARM 内核的 STM32 的启动流程,我以前的博文详细分析过,搞懂了 STM32 的启动流程对于芯片的使用和理解来说就会更上一个等级。现在我们新接触的 risc-v 内核的 ESP32-C3,如果能够搞明白他的启动流程,就能更深的理解 ESP32-C3。
在写文章之前也看了很多网上的文章,然后官方的说明也看过了,网上绝大多数都是官网文档复制一遍,这倒没什么,毕竟官网权威,问题是,复制一遍过来没有做过多的解释,没有额外的分析,还写个原创,我真是 XX 了。当然不排除我没看到的好的文章,也正是因为基本没有看到更加详细的分析,我决定要自己写一篇记录,一来当做自己记录,二来底层有些地方目前来说我确实不是理解透彻,肯定有不到位的地方,希望大家多提意见,能让文章更加完善。
按照惯例,我把能找到的文章和官方的资料都看了好多遍,官方提到的 3 大步骤:
对于上述提到的 3 大步骤,最简单的清晰的是第三步,第二步也有源码,倒是可以查看试着分析一下,但是第一步确实,只是知道这么回事,具体的实现方式因为程序是在 ESP32-C3 的内部 ROM 中,确实找不到可以分析的源码和资料。
芯片的启动流程,大多离不开启动文件和链接文件。ESP32-C3 应该也是这样,本来按照理解,应该找到底层的启动文件和连接文件开始按照步骤分析,但是查看了一会底层代码,因为对底层架构深入了解得还不够,没有找到= =! 所以想着怎么办?
不能按照从最开始到结束的顺序来,那么就按照从结束到开始的顺序来!我们从app_main
函数反过来一层一层网前看,看看经过了一些什么处理,程序才执行到app_main
函数的!
注意,文章以倒叙的方式说明~ ~
本文是基于 VScode 插件的工程结构来说明(Ubuntu 环境),环境搭建见下面博文:
ESP32-C3 VScode 开发环境搭建(基于乐鑫官方 ESP-IDF——Windows 和 Ubuntu 双环境)
https://xie.infoq.cn/article/5b639e112cabba00cc1b8941a
一、应用程序启动阶段
1.1 app_main.c
我们从app_main.c
的主函数app_main
中,我们直接通过转到定义看看上一层:
1.2 port_common.c
app_main 往上找的文件是 port_common.c
,路径如下:
是哪一个任务调用了app_main
呢,我们直接往上就能看到,main_task
调用了app_main
,而main_task
这个任务就在esp_startup_start_app_common
函数中创建:
知道了esp_startup_start_app_common
函数创建了main_task
这个任务,那么这个函数在哪边有调用呢?我们继续往上看,找到esp_startup_start_app
函数中调用了esp_startup_start_app_common
,调用完成以后就开启了 FreeRTOS 任务调度。
对应的,我们看一下启动 LOG:
1.3 port.c
我们又进入了一个新的文件 port.c
,路径如下:
接着上面的,从esp_startup_start_app
函数往上找,又找到一个start_cpu0_default
函数,如图:
1.4 startup.c
又找到一个新文件startup.c
,路径如下:
start_cpu0_default
在 startup.c
文件的 start_cpu0_default
函数,其中进行了很多关键步骤,具体可以自行查看代码:
对应的,我们看一下启动 LOG:
我们还想看 start_cpu0_default
函数从哪里调用,但是这时候已经无法跳转了,这时候我们依然在startup.c
文件中搜索,可以找到下面一句话:
这句话的意思就是使得 start_cpu0_default
函数弱连接到start_cpu0
函数,如果没有额外的声明,start_cpu0_default
函数等价于start_cpu0
函数。(有错误请指出)
继续看看与 start_cpu0
函数 有关的程序,g_startup_fn
好像是一个数组?如图:
尝试着对 g_startup_fn
使用转到定义,我们进入了另外一个新的文件,可以找到一个宏定义SYS_STARTUP_FN()
:
1.5 startup_internal.h
老样子先看文件路径如下:
这是一个头文件,所以这里面肯定都是函数的声明和宏定义,我们在这个文件中需要关注的地方就是一个宏定义#define SYS_STARTUP_FN() ((*g_startup_fn[(cpu_hal_get_core_id())])())
:
我们接下来要找的就是这个宏定义SYS_STARTUP_FN()
在哪里调用了。
因为是头文件,找函数调用就复杂一点,但是问题不大,如下图:
点进去确实发现是我所需要查找的文件,如果找不到,可以在目录下面搜索,先小目录,还是找不到,再用上层目录。 这也是看底层源码的基本方法。
1.6 cpu_start.c
老样子先看文件路径如下:
在cpu_start.c
文件中,可以看到确实有上面头文件中宏定义SYS_STARTUP_FN()
的调用,里面主要就是几个启动 cpu 的函数,其中是call_start_cpu0
函数调用了这个宏定义:
函数的内容是很多的,这里我们就不一一分析。但是到了这里,我们再结合官方的说明文档再看一遍:
这样子去理解,感觉官方说得好简单,实际上都是层层调用。
分析到这里,我们都已经进入入口函数了,那么程序是如何调用。按照以前的理解,这个入口函数应该会在一个连接脚本里面使用 ENTRY
调用,就像ENTRY(call_start_cpu0);
实际上,如果去文件夹搜索可以找到很多文件中有ENTRY(call_start_cpu0);
,即便我们值选择与 ESP32-C3 有关的,也有不同的地方,那么到底是哪个呢?我们还得继续顺藤摸瓜!
1.7 esp32c3.project.ld.in
在组件的esp32c3
目录下面搜索call_start_cpu0
,找到一个使用了ENTRY(call_start_cpu0);
的链接文件:(有错误请指出)
这个和我们的 STM32 中分析中的类似,就是进入到入口函数执行:
文件路径:
我们还得往下简单看看二级引导程序。
二、二级引导程序
二级引导程序,基于现在我的理解,还不足以能够完全的讲清楚,官方提到源码在 ESP-IDF 的 components/bootloader 目录下,我也简单的看了一下,在目录中找到比较“可能”的程序:
在这个bootloader_start.c
文件中,正如官方的说明一样,ESP-IDF 使用二级引导程序可以增加 Flash 分区的灵活性(使用分区表)等一些功能:
继续查看了一些代码(具体的就是在bootloader_start.c
文件相关联的地方查找),确定bootloader_start.c
就是二级引导程序的代码,根据 LOG 输出判断的 = =!
在 bootloader 目录下也有 ESP32-C3 的链接文件:
在其中也有ENTRY(call_start_cpu0);
的调用,这个应该是和二级引导程序中bootloader_start.c
中对应的链接文件(有问题清指出)。
结语
文章在前面 C 语言启动的部分倒是还算满意,到底层以后,对陌生的 risc-v 内核和架构还是处于模糊的状态,希望小伙伴多多给些意见。
文章整体只能算是代码的流程解析,并没有分析函数的功能,这是我不满意的地方,冰冻三尺非一日之寒,加油!文章会随着博主的理解深入保持更新,希望以后能有时间从内存,从架构好好的完善一下文章!
版权声明: 本文为 InfoQ 作者【矜辰所致】的原创文章。
原文链接:【http://xie.infoq.cn/article/c7c59030b3e22ef1dcaa51368】。文章转载请联系作者。
评论