目录
1. IAR启动流程概述
2.可以不用__iar_program_start吗
3.小结
大家好,今天的肌肉也不是很快乐。
今天聊聊IAR特有的一些启动流程以及在调试的时候遇到的一些问题。
1. IAR启动流程概述
ARM M内核芯片里的启动代码通常会提供Arm、gcc、iar等编译器的模板,其中让人比较迷糊的是IAR的模板。
以STM32F7xx的startup.s为例,它的reset_Handler非常简单,就是一个系统初始化,一个跳转,如下:
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
从上面汇编可以看到,Reset_Handler里跳转至__iar_program_start,使命就结束了。
这个__iar_program_start里面具体定义了什么内容?搜遍整个工程都没有发现,因此还得从使用手册查起。
在IAR工程配置里有这么一个选项:
这就意味着如果使能该选项,那么应用程序就在这个标签开始。所以很明显这里干的事情就是在运行应用代码前完成必要的初始化等。
根据IAR 开发手册描述,在IAR工程下硬件初始化通常由cstartup.s进行完成,我尝试将工程进行反汇编,得到__iar_program_start里的一些内容:
从这里发现了很多标签,例如__cmain,__low_level_init等,那说明这些都是编译器自带的,因此我在IAR编译的map路径下找到了对应的源码,如下:
- __iar_program_start的定义(仅针对arm-M)在:IAR Systems\Embedded Workbench 9.2\arm\src\lib\thumb\cstartup_M.s;__cmain路径一样;
- __low_level_init的定义在IAR Systems\Embedded Workbench 9.2\arm\src\lib\runtime
- __iar_data_init3在IAR Systems\Embedded Workbench 9.2\arm\src\lib\init
- 其中,__iar_program_start设置堆栈指针等,__cmain则负责调度_low_level_init、data_init以及最终跳转至main。
这样整体逻辑就比较清晰了,如下图:
在low_level_init.c里可以完成一些基础的初始化,例如看门狗的设置、中断等级的设置。比较好玩的是在示例代码里,函数里面是空的,如下:
在反汇编出来的代码里我们可以看到,要不要调用__iar_data_init3是需要根据__low_level_init的返回值来决定,如下:
如果上述函数返回值为0,则直接跳转_call_main,如果返回值为1,则继续跳转至__iar_data_init3。注意这个函数,很神奇,我们在map里可以看到它是定义在data_init.c里的,但是这个文件没有发现任何这个函数的说明,如下:
但其他的针对数据初始化,又可以在同目录zero_init3.c里找到,具体如下:
2.可以不用__iar_program_start吗
答案肯定是可以的,我们可以使用指令--entry来改变程序入口的符号;或者简单粗暴一点,直接在工程上进行修改,将Entry symbol设置如下图:
不过这就没有使用IAR自带的初始化,这就需要变量的手动copy赋值。
对应变量的初始值非0,使用initialize manually,并且需要添加对应的代码把ROM中的Initializer bytes拷贝到对应的RAM区域,在ICF linker文件定义如下:
define block MY_DATA { section .data, section MY_SECTION };
define block MY_DATA_INIT { section .data_init, section MY_SECTION_init };
initialize manually { section .data, section MY_SECTION };
变量的初始值为0则使用do not initialize,同样需要手写代码赋0。
3.小结
关于第二点,如果不使用__iar_program_start,我发现会编译报错,如下:
因此,为了糊弄过去,在启动代码里定义了这么一个符号。但回头来想,既然都没有使用IAR的初始化,理应后续代码不会参与编译呀,一定是工程配置还有问题。留个坑,后面来填!