ARM 启动过程分析

开始学ARM 时就对芯片上电后的执行顺序很纳闷,但苦于网上的解释都不甚详细,而且常常有学弟学
妹来问,含糊其辞总怕误人子弟,想讲诉详细又总不能够。近些天闲来无事,决心好好整理一下启动流程,
一劳永逸。
我想每个学习51 单片机的同学都应该记得一句话:芯片上电从零地址开始执行程序。
说实话,不记得这句还好,记住就更乱了;不是说这句话错,而是说的太片面了。对于低级
单片机,的确是这样;但对于ARM 等高级芯片,启动方式就变得多种多样,而上电后开始执
行的地址也不再固定为0 地址了。因此如果你是一个忠实的51 使用者,那么我建议你不需
要听我废话了,你根本不会遇到这方面的理解障碍,使用51 时一般为ISP 下载,代码一般
就直接下载到零地址,不存在启动方式多样性的问题,但如果你使用过ARM,就会发现这一
问题非常缠人。
一般出现的理解障碍都是在“启动方式”和“启动流程”这两个字眼上。下面我们将一
步步解释这两个问题。
我们的程序一般分为两部分:启动代码和功能代码,“启动代码”的主要工作是初始化
芯片的各种工作模式下的堆栈(如果需要使用中断,还要定义中断向量表),一般由芯片设
计工程师用汇编语言编写,当然不同的芯片这个程序也会有所不同,以后基本上也不会再有
变动,所以要求非常精炼,嵌入式工程师直接拿来用就可以了,然后CPU 程序指针默认跳转
到__main()函数执行,此处主要初始化一些数据区域之类的,下面才是跳转到我们平时编写
的main()函数,也就是所谓的功能代码。
那这些代码在芯片上电前保存在哪里呢?
ARM 芯片上电后也是从默认地址开始执行程序,但一般此地址为ESRAM 或者SDRAM,这
两个存储器都是易失性存储器,没有断电存储代码的功能,也就是说当我们的芯片上电时这
里面全为不确定值(对ESRAM 来说全为0,对DDR 来说为随机值),那要执行的代码在哪里
呢?那么就引出启动方式的概念了!
ARM 一般有芯片内部Flash 启动、外部存储器启动(NAND、NOR、SD)两种启动方式:
1、内部Flash 启动是指芯片上电后PC 立即开始执行这个存储器里的代码,注意这里不
是零地址。但一般这个存储器的容量非常的小,因为芯片的成本与体积成指数关系,那么不
可能将我们编写的功能代码全部放进去,所以仅仅将启动代码保存至这个Flash 芯片中,功
能代码保存在大容价廉的外部存储器中,因此启动代码也又多了一项工作:从外部存储器搬
运功能代码到某个可以执行代码的存储器(如NOR、SDRAM、ESRAM 等),然后跳转到这个地
址去执行功能代码。
2、外部存储器启动一般有NAND、NOR、SD 启动三种,这几种启动方式流程大致相似,
这里我仅拿NAND 启动来讲:在这种启动方式里,断电时将启动代码和功能代码全部保存至
外部存储器中,芯片上电后内部的DMA 器件自动搬运NAND 的前8K 代码到ESRAM 或者SDRAM,
具体搬运到哪个地址也是在芯片设计时确定的,有的芯片就没有ESRAM 这个存储器;而且搬
运的代码大小也是在芯片设计时确定的,目前我参与的一个芯片项目是搬运NAND 的前8K
代码,SD 的前4K 代码。也就是说芯片上电后不再立即执行代码了,而是先启动总线到指定
地址搬运代码,然后再从ESRAM 或者SDRAM 开始执行代码,但8K 或者4K 代码也并不一定满
足我们的要求,特别是使用操作系统时,仅LINUX 内核就要2M 大小左右,所以习惯上这前
几K 的代码我们放的依然是启动代码,功能代码还是放在外部存储器的另一个地方,启动代
码中依然有搬运功能代码的部分,下面的执行部分就和第一种启动类似了。
当然如果你的功能代码非常少,仅有几K 大小,那么可以省略启动代码中的搬运部分,将两种代码直
接放在NAND 的前8K 空间里即可。
这里还要特别说一下NOR启动,它和一般FLASH存储器不同,它支持byte操作,也就是说在NOR里可以直接执行代码,它不需要功能代码搬运这一步,这就比其它两个外部启动方式更高效。但这种存储器还是比较昂贵的,芯片外接NOR的大小一般在8M左右,综合效率和成本,具体选不选用这种启动方式就不好说了。
可以说启动方式解决了我们的代码存储疑问,那么上电后的流程是什么样的呢?
下图是NAND启动流程以及中断执行流程。
NAND启动流程
中断执行流程
SD启动的流程非常类似,只不过一般搬运启动代码时搬运前4K代码,具体搬运多少是在设计芯片时决定的,一般芯片成片后均不能再更改了,但4K的启动代码我相信基本上是够用的了。
其实还有一种方式,但不能叫做启动方式,那就是JTAG调试。
JTAG调试是目前最流行的调试方式,它比ISP方式多出了很多功能,比如断点、单步调试等,特别在大型项目中,这些非常有用。
使用JTAG时可以通过JTAG将代码放到任意一个可byte寻址的地址,然后会直接从此处开始执行代码,说到这里,你们应该也明白了,这仅仅是一种调试方式,不可能你的产品还非得要连上一个JTAG调试器才能用吧?
对于嵌入式操作系统来说,启动过程又会变得稍微复杂些。
我对这种情况的启动顺序总结为:启动代码->bootload->操作系统,其中bootload和操作系统通称为功能代码。一般启动流程如下图所示:
DMAC搬运NAND的前8K数据到ESRAM
(这8K数据即启动代码,用于堆栈初始化)
PC跳转到ESRAM首地址执行启动代码并搬运功能代码,如果程序中使用了中断,还会将中断向量表放置在零地址(也就是ESRAM的重映射地址)。(一般在程序中启动程序文件名称为boot.s和int.s)
PC跳转到__main(),然后再跳转到main()里执行功能代码。至此,芯片启动完成。
中断源发出中断信号,经屏蔽寄存器以及使能寄存器筛选后,得到屏蔽后中断信号
CPU接收到屏蔽后中断信号,自动跳转到boot.s中定义的地址去执行中断初始化程序,主要是压栈,也即保护现场。
PC保护现场后才进入中断向量表查找产生的中断号所对应的中断服务子程序的后再恢复现场,即出函数名,然后执行此函数,最栈
启动代码初始化堆栈,并搬运bootload到SDRAM
可以看出bootload的功能是搬运操作系统,并提供交互界面供用户配置启动参数,大部分代码用C语言编写,与启动代码有点类似,但我还是将其归类为功能代码。比较常用的Bootload有U-boot、e-boot等,主要工作是搬运操作系统代码到SDRAM中,然后去执行操作系统代码,即系统引导,也就是交控制权给操作系统。
以上说的全是理论,真正在我们使用IDE进行项目开发时需要注意什么呢?
应该说不同的集成开发环境对启动设置的要求不同,开发ARM比较常用的IDE有KEIL、IAR、ADS、MDS、RVDS、CrossWorks等几种。其中RVDS是ARM公司收购KEIL并结合之前的开发工具ADS而推出的一款集大成的IDE,号称支持所有ARM芯片的JTAG调试,目前我使用的开发环境正是RVDS4.0。但它的一个缺点是只是使用RealView ICE这个JTAG调试器,这款调试器内部跑了一个linux操作系统,基本上相当于一款小型电脑,支持局域网共享,支持半主机功能,可以说是我用过的最稳定最强大的调试器(有的时候好的调试器对做项目能起到事半功倍的作用),但价格也贵得离谱,最近公司采购的一批单价高达2万多人民币,要知道平时使用的山寨版的调试器如ULink、JLink、LM-Link等只要100~400元不等,其他几种调试环境没这个问题,支持的调试器种类很多,但大大小小的毛病还是蛮烦人的。因此选择调试环境时还是要看个人的经济实力综合考虑呀!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值