为什么要学习汇编代码一(更加深刻理解芯片的启动流程)

  • Post author:
  • Post category:其他




背景介绍

本文章探讨的是cortex M 系列的芯片的启动过程,其它的相关的芯片可以进行借鉴。本文所引述的代码位cortex M0相关连的代码,参考的文章是arm官方的原版的文档。

文章转载请标注:https://blog.csdn.net/XG_2013/article/details/107508866



问题引入

笔者是芯片原厂的软件开发的组长,由于长时间和一些运用我们芯片做方案的方案商的开发人员接触,发现做方案的开发人员对汇编代码和芯片本身的理解比较欠缺。由于这些欠缺,可以很明显的看出在解决一些隐晦的bug和代码编写的性能方面显得非常吃力。为什么学习汇编代码是一个小系列,本文以汇编为路线详细阐释芯片的启动的流程。本文推荐阅读的书籍

arm 官方的文档

上述的文档,可以在arm官网获得,也可以通过keil的book获得。

keil books

上图通过keil的开发工具找到的。



芯片启动的reset

芯片复位

上图摘自arm cortex M0的权威指南。

这个过程可以分为2个部分来看:

  1. 取第一条指令之前:这个过程是MCU的硬件逻辑处理的,软件没法干预。
  2. 取第一条指令之后:执行复位的中断函数



执行中断复位函数之前,为什么需要读取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 文件有那些内容呢:

  1. 代码指令
  2. 常量(RO)
  3. RW 变量(全局变量且初始化)
  4. 其它特殊东西

    是不是很奇怪,为什么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区?

关注作者,持续更新中。



结论

通过上述的内容,一些比较细致的过程,我们可以通过汇编代码了解的非常细致,纯粹的解释容易遗忘且逻辑性不是很强。



版权声明:本文为XG_2013原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。