更加深刻理解芯片的启动流程
背景介绍
本文章探讨的是cortex M 系列的芯片的启动过程,其它的相关的芯片可以进行借鉴。本文所引述的代码位cortex M0相关连的代码,参考的文章是arm官方的原版的文档。
文章转载请标注:https://blog.csdn.net/XG_2013/article/details/107508866
问题引入
笔者是芯片原厂的软件开发的组长,由于长时间和一些运用我们芯片做方案的方案商的开发人员接触,发现做方案的开发人员对汇编代码和芯片本身的理解比较欠缺。由于这些欠缺,可以很明显的看出在解决一些隐晦的bug和代码编写的性能方面显得非常吃力。为什么学习汇编代码是一个小系列,本文以汇编为路线详细阐释芯片的启动的流程。本文推荐阅读的书籍
上述的文档,可以在arm官网获得,也可以通过keil的book获得。
上图通过keil的开发工具找到的。
芯片启动的reset
上图摘自arm cortex M0的权威指南。
这个过程可以分为2个部分来看:
- 取第一条指令之前:这个过程是MCU的硬件逻辑处理的,软件没法干预。
- 取第一条指令之后:执行复位的中断函数
执行中断复位函数之前,为什么需要读取MSP的初始值?(硬件的数字逻辑自动完成的)
有些人肯定会说这是明白的事,软件运行肯定要设置主栈指针。这句话我觉得是对的,但是
在第一条指令过后还会有设置主栈指针的操作
,这会不会意味着硬件读取的MSP的操作不是很有必要呢?我们看下面这张图:
上图是一个典型的reset的中断复位函数,
注意中断向量表和中断复位函数是2个概念
。中断向量表放的是
函数指针
,这些函数指针指向一个个
中断复位函数
。
进入中断复位函数后,执行了一个SystemInit 的函数。汇编原理如下
LDR R0,==SystemInit ,把SystemInit 的函数的地址加载到寄存器R0
BLX R0 跳转到=SystemInit。
SystemInit 是一个用户自己的函数,可以完成一些系统相关的初始化,比如更新系统的工作频率。如果在System Init使用到了栈,比如局部变量的使用。我这儿强调一下,不是所有的函数调用都会用到栈。所以为了整个系统更加具备普适性和完毕性,硬件在复位的时候完成栈的初始化。
__main 函数到底运行了什么东西?
由于__main 是编译器自带的库函数很难找到源码,如果你懂汇编就能很清楚知道这个问题。我们通过fromelf工具获取到汇编文件,参考日下图的文章进行操作。
这类给一个典型的操作方式,鼓励大家从参考的文档中找到方法,鼓励培养英文的阅读能力。
我们在看看具体的汇编代码:
从大的轮廓能看到__main 函数是完成了,主栈指针的初始化,全局变量的初始化(典型的是把RW变量搬到ram,把ZI段在RAM初始为零),然后跳转的main函数,由于这块内容比较多,我着重解析如几点:
1、主栈的指针是存放在0地址,为什么软件读取主栈指针不从0地址读取呢?
原因:0地址的内容和软件读取的0x364的内容是
一样的
,为什么这样做还不知道。
2、下图是main函数地址,上图第三步跳转的是0xa92d,实际的main函数是0xa92c。
原因:thumb 指令需要最低为1,区分thumb 指令和arm 指令,实际执行的时候减一。
RW ZI RO 字段的细致分析
这个问题懂了就是非常简单,不懂就会很绕。我们很早学习单片机的时候都知道,指令在flash,变量的在ram。但是
变量是怎么跑到ram中呢?
flash 只有指令么?
flash的下载过程
我们通过keil 编译的bin文件通过jlink 下载到flash,这个过程很容易理解bin 文件和flash 存放的内容一摸一样。
bin 文件 == flash 内容
那bin 文件有那些内容呢:
- 代码指令
- 常量(RO)
- RW 变量(全局变量且初始化)
-
其它特殊东西
是不是很奇怪,为什么bin 文件有全局变量的RW 的内容,我们想想如果斌文件没有RW变量,
怎么能够在ram 初始化呢
,这意味着我们要在bin文件存放这个rw变量的
值
。
RW的变量的搬移过程
如上图:R2代表变量的个数,R0 代表flash存放的RW变量的位置,R1 代表ram实际变量运行的位置。这一段翻译如下:
for(uint32_t i = 0;i < 变量总数;i++)
*p_ram_var = *p_flash_var;
p_ram_var ++;
p_flash_var++;
到这RW变量应该非常清楚了吧,bin 文件存放一边,程序运行的时候在__main 函数搬移到ram
ZI 段为什么bin文件不需要存放:
由于ZI段时全局变量初始为0,或者没有初始化。所以bin文件不需要存放,*
这不是意味着ram值默认是0
。bin 文件只要存放ZI段的大小和ZI段的首地址即可。
如上图是吧ZI段初始化为0的操作,这个过程大家自己看吧。
上图是一个MAP文件内容,大家参考一下在反思一下。
进阶的问题探讨
1、是不是RW段一定需要搬移到RAM区?
关注作者,持续更新中。
结论
通过上述的内容,一些比较细致的过程,我们可以通过汇编代码了解的非常细致,纯粹的解释容易遗忘且逻辑性不是很强。