IAR Embedded Workbench中的MCU启动过程
在嵌入式系统中,当MCU复位之后,需要运行对应的启动代码来对系统进行初始化,然后才会调用main函数,开始运行用户的代码。通常情况下,对应的启动代码一般是工具厂商或者芯片厂商提供,嵌入式软件开发工程师不需要特别关注。不过掌握对应的MCU启动过程对于了解和掌握嵌入式系统还是非常有帮助的,特别是在启动过程中出现问题的时候。
本文主要以Arm Cortex-M为例,介绍IAR Embedded Workbench中的MCU启动过程。
MCU启动过程
MCU启动过程指的是从MCU复位到main函数之前的过程。
当MCU复位之后,MCU会从对应的复位向量开始运行,初始化Stack pointer指向指定Stack区域的末尾,然后调用__low_level_init函数进行相关的初始化。
接下来是全局和静态变量的初始化:初始值为0的变量对应的RAM区域会清零,初始值为非0的变量,会从ROM拷贝到RAM(注意:如果__low_level_init函数返回0,这一步将会跳过)。
然后是C++动态初始化:构造静态 C++ 对象,最后会调用main函数。
更具体一点:
当MCU复位之后,PC指针会指向对应的复位向量,然后运行对应的启动代码(startup code),启动代码首先会初始化Stack pointer指向指定Stack区域的末尾。
然后初始化初始值为0的存储在RAM中的全局和静态变量(比如 int i = 0;):
初始化初始值为非0的存储在RAM中的全局和静态变量(比如 int i = 1;),对应的初始值从相应的ROM拷贝到对应的RAM:
最后,调用main函数:
启动代码
通常情况下,如果ICF文件中添加了initialize by copy 命令,linker会自动选择并添加对应的启动代码来完成对应的启动过程。对应的启动代码通过库文件的方式进行link。对应的启动代码在安装目录arm\src\lib下面:
对应的启动代码和相关文件信息会在map文件里面列出来:
同时map文件里面INIT TABLE章节会列出对应的全局和静态变量的初始化信息:初始值为0的会使用__iar_zero_init3进行初始化,初始值为非0的会使用__iar_copy_init3进行初始化:
调试
为了能够调试查看对应的启动代码和启动过程,需要配置Debugger选项里面的Run to,即不要勾选Run to,这样调试的时候复位之后PC会停在复位向量而不是main函数,然后就可以调试对应的启动代码和启动过程。
复位之后,PC会停在复位向量Reset_Handler,Reset_Handler首先会调用SystemInit函数进行相关的配置和初始化(这个是Cortex-M CMSIS的标准),然后会调用__iar_program_start:
如果对应的MCU有FPU,__iar_program_start首先会调用__iar_init_vfp对FPU进行初始化:
然后__iar_program_start会调用__cmain:
__cmain首先会调用__low_level_init(默认实现为空,仅返回 1):
__cmain然后会调用__iar_data_init3进行全局和静态变量的初始化:
__iar_data_init3首先会调用__iar_zero_init3进行初始值为0的全局和静态变量的初始化:
__iar_data_init3然后会调用__iar_copy_init3进行初始值为非0的全局和静态变量的初始化:
最后__call_main会调用main函数跳转到main函数:
至此MCU从复位向量开始,运行启动代码之后就跳转到main函数,然后开始运行用户的代码:
注意事项
Cortex-M的MSP赋值是硬件自动操作的(复位之后从中断向量表0地址偏移取值然后赋给MSP),所以在上面的启动代码和启动过程没有体现。如果需要手动对MSP进行赋值(比如bootloader跳转到application,需要手动对application的MSP进行赋值),那么需要在启动代码最开始进行操作。
IAR默认的启动代码是linker在link的时候自动添加的,需要在ICF文件添加initialize by copy命令。
__low_level_init函数给用户提供了一个初始化的接口,用户可以实现对应的__low_level_init函数,如果MCU的RAM有ECC机制,需要在__low_level_init函数里面按照ECC的位宽对RAM区域进行一次写操作,避免后续RAM操作产生ECC错误。需要注意的是,__low_level_init函数在全局和静态变量初始化之前,所以里面不能使用全局和静态变量。同时,__low_level_init函数的返回值决定全局和静态变量是否需要初始化(返回值为1代表全局和静态变量需要初始化,返回值为0代表全局和静态变量不需要初始化)。
__iar_program_start是IAR里面默认的程序开始标签,如果代码里面使用了其它的程序开始标签,需要使用linker选项–entry指定对应的程序开始标签:
总结
本文主要以Arm Cortex-M为例,介绍IAR Embedded Workbench中的MCU启动过程:当MCU复位之后,PC指针会指向对应的复位向量,然后运行对应的启动代码(startup code) :如果MCU有FPU,__iar_program_start首先会调用__iar_init_vfp对FPU进行初始化,然后__iar_program_start会调用__cmain。__cmain首先会调用__low_level_init,然后会调用__iar_data_init3进行全局和静态变量的初始化。__iar_data_init3首先会调用__iar_zero_init3进行初始值为0的全局和静态变量的初始化,然后会调用__iar_copy_init3进行初始值为非0的全局和静态变量的初始化。最后__call_main会调用main函数跳转到main函数。
作者:IAR_Systems