(原创文章,转载请注明出处,谢谢)
呵呵,最近欠博客的账太多了,这种连载类的,还是要继续。今天和大家聊聊ARM的启动代码问题。我有不少朋友,都是写代码的高手,但搞起ARM的启动代码的时候还是犯迷糊。其实,多半是没有理解ARM工作的机制所造成的。
我在该连载的介绍中和大家谈了很多,但要记住的只有一点:ARM 上电执行第一条代码是从 0x0开始的。这是谁都不能改变的。但是0x0的地址可以通过很多方法进行映射,执行不同的代码。有些厂家在裸ARM芯片上增加了一小段启动代码。用以完成从特殊设备上加载代码,启动ARM。这种情况是非常多得,一些设计良好的ARM芯片,都可以从spi flash上加载,从uart上加载,大大的简化了ARM单板第一次烧写程序的复杂度。这个需要对芯片的Datasheet做细致的解读。
我们以AT91SAM9260为例,分析它的启动代码如何撰写。
AT91SAM9260是一款设计良好的ARM芯片。支持从nandflash和spiflash上启动。从芯片手册上可知,spiflash支持从SPI0口的NPCS0或者NPCS1启动,或者从其他设备启动。AT91SAM9260最好和AT45DBXXX类芯片配合。AT91SAM9260公板就是使用的AT45做的启动存储。芯片接在SPI0的NPCS1口上。固化在9260内部的系统的代码遵循以下流程:
固化在系统内部的这个启动代码(Boot Program),还不能算作一个合格的Bootloader。主要有以下的原因:
1.首先这个启动代码不知道9260接了哪些硬件。客户一般会按照自己的需要接芯片,比如说接SDRAM,启动代码是无法知道SDRAM所需要的访问时序和SDRAM的大小。这个是和客户的选择有很大的关系。或者客户接了自己的SRAM,等等……启动代码是无法事先知道的。因而也无法初始化这些硬件,更谈不上使用这些硬件。
2.启动代码不可能太大。因为片内运行这个代码需要的资源太多了,不光光是存储代码的ROM,还需要片内集成足够大的SRAM。这使得芯片的成本上升。这样做也没有必要,因为可以有更好的方式去解决。启动代码不能太大,也意味着也不复杂,功能单一。
所以,只能在启动代码加载的程序上动脑筋了。Boot Program加载的程序称为 Bootstrap, 也不叫Bootloader;为什么叫Bootstrap不叫Bootloader?并不是ATMEL想标新立异,找卖点。Boot program 加载 bootstrap, 由bootstrap再加载bootloader或者用户的应用程序。
由于Boot Program只能初始化已知道的芯片的内部资源。2个SRAM还有内部的PLL等寄存器。都是芯片的内部资源。Boot Program的代码存储在ROM中,它运行必然要占一些数据,要么放SRAM0里,要么放SRAM1里。Boot Program加载Bootstrap到9260里,也只能放到SRAM0里或者SRAM1里。SRAM0和SRAM1各有4K。这里需要注意:在加载bootstrap的过程中,Boot Program还需要正常工作。如果Boot program使用得是SRAM1存储运行时刻的变量等数据……那么Boot Program是不能破坏SRAM1里的数据,只能把Bootstrap代码拷贝到SRAM0里执行。
有童鞋问,可不可以把SRAM1里 Boot Program没用的部分也给bootstrap使用呢?回答是不可以。但大家要明白一点,从理论上讲是完全没有问题的。如果想实现,Bootstrap必须将其代码分成两个部分,4KB的一个部分,剩下的一部分放入SRAM1中(可能只有2K,具体视Boot Program占用多大)。这必然会使boot program复杂化,芯片成本上升。并且,实现分段加载,在编译bootstrap时要修改链接文件,生成elf文件,带加载信息;或者就生成两个bin文件。这个操作起来太复杂,实践证明,也没有这个必要。所以,AT91SAM9260硬件这一特点就限制了bootstrap大小,只能为4KB,数据占用大小也不能超过4KB。:-)。
4KB大小的ARM代码能做些什么呢?ATMEL的设计人员给我们做了解答。bootstrap 做了初始化硬件,从指定的设备上加载OS或者应用程序。由于这个代码是可以更改的,我们可以针对硬件做一些修改。比如说可以初始化SDRAM,如果没有SDRAM,只有外扩的SRAM,也可以初始化系统的总线,适配这种外扩SRAM。
初始化完成,就可以从任意设备上加载下一步的代码。可以从网络、UART和Flash上加载。从网络上加载,需要集成网络协议栈,UART的话也需要集成相应的协议,必须考虑一个问题bootstrap中实现没有问题,但是,4KB的代码空间是否能容纳得下呢?所以,实际使用中,一般都是从flash指定的位置上获取代码而已。获取代码的格式,也很简单,就是加载到内存中的代码镜像(raw binary)。这里牵涉到3个问题:
1.bootstrap从flash的什么位置加载应用程序?加载多大?
2.bootstrap将这些程序加载到什么地方去?
3.bootstrap将这些代码加载完成后,它将控制权交给谁?换句话说,让PC指针指向哪里,开始执行加载后的代码?
ATMEL官方提供了一些配置宏,用于改变前两个地址。官方代码中并为提供对第三个地址的配置,第三个地址与第二个地址是相同的。 当然你可以修改这个代码,第三个地址与第二个地址不同,可通过宏配置。
对于这三个地址,我们头脑一定要清楚:
1.bootstrap 从flash的什么位置加载应用程序,这个地址有可能是flash芯片的内部地址,对ARM芯片来说,不可能通过简单的方式访问到(即一条数据指令就访问到)。必须要通过复杂的方法,数十条甚至上百上千条汇编指令才能访问到。
2.bootstrap加载这个应用程序到什么地方去,这个地址一定是ARM芯片用一条数据访问指令能访问到的地址!不能多于1条指令访问到,否则,这个地址不能用作运行时刻的代码存储。
3.bootstrap跳到什么位置,这个是应用程序的第一条指令的位置。这个是由这个应用程序的链接脚本所决定的,不是由bootstrap决定的,bootstrap没有决定这个应用程序第一条指令位置的权利,它必须知道这个位置,才能使这个应用程序顺利的运行起来。
这个应用程序可以是真正的bootloader,也可以直接就是用户的代码。到这一步,ARM基本上已经告别荒芜的原始社会了。这三个地址理解清楚了,就可以洋洋洒洒写代码了……下次我们侃侃如何实践……
(原创文章,转载请注明出处,谢谢)