bootloader和RO,RW,ZI在ARMC语言中

1420阅读 0评论2013-11-14 wangh0802
分类:C/C++

  Bootloader 即引导加载程序,是系统加电后运行的第一段软件代码。简单的说它们都是bootloader,所完成的任务也大同小异。

熟悉x86体系结构的朋友肯定知道,x86平台上bootloader 是由 BIOS和位于硬盘MBR中的OS Bootloader(比如Lilo 和 Grub)组成的。BIOS完成硬件的检测和资源的分配后,将硬盘MBR中的bootloader读到系统RAM中,之后此bootloader 就会开始进行主导,将内核搬到内存中以及进行一些必要的初始化工作,之后跳到内核的入口地址来执行,这样内核就开始启动,也就是系统就启动起来了。
这里不得不插入一个话题,通过上面的介绍,细心的朋友就会产生一个疑问:为什么要有bootloader?既然bootloader只是作硬件的初始化并将内核引导起来,那为什么不直接将这段代码加到内核中,直接启动内核就完成所有的工作?实际上要将bootloader与内核整合在一起是完全可以做到的,但是如果这样作的話,内核就会失去他的通用性和灵活性,并且将bootloader与内核分开会更有利于开发和管理,将启动过程中与平台硬件相关的代码集合成bootloader,内核就可以集中处理那些平台通用的部分了(当然实际上并没有这么严格的划分,内核中还是会有一些平台相关的代码,不过已经算是比较通用的了)。
=============================================================================================================================================================================
一个ARM程序包含3部分:RO,RW和ZI           
          RO是程序中的指令和常量;RO就是readonly,
        RW是程序中的已初始化变量; RW就是read/write,          
(2) ARM映像文件的组成
    所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。Image文件包含了RO和RW数据。之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。
    Q:为什么Image中必须包含RO和RW?         
    A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。   
  实际上,ROM中的指令至少应该有这样的功能:
            1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
            2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中.在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。
    下面我将给出几个例子,最直观的来说明RO,RW,ZI在C中是什么意思。
        1; RO
        看下面两段程序,他们之间差了一条语句,这条语句就是声明一个字符常量。因此按照我们之前说的,他们之间应该只会在RO数据中相差一个字节(字符常量为1字节)。        
Prog1:
            #include
            void main(void)
            {
                ;
            }
           
Prog2:
            #include
            const char a = 5;
            void main(void)
            {           
                ;
            }
 Prog1编译出来后的信息如下:          
================================================================================          
Code RO Data RW Data ZI Data Debug
            948 60 0 96 0 Grand Totals         
================================================================================        
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)         
================================================================================           
Prog2编译出来后的信息如下:          
================================================================================         
Code RO Data RW Data ZI Data Debug
            948 61 0 96 0 Grand Totals           
================================================================================          
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)          
================================================================================         
以上两个程序编译出来后的信息可以看出:
   Prog1和Prog2的RO包含了Code和RO Data两类数据。他们的唯一区别就是Prog2的RO Data比Prog1多了1个字节。这正和之前的推测一致。如果增加的是一条指令而不是一个常量,则结果应该是Code数据大小有差别。
     2; RW
     同样再看两个程序,他们之间只相差一个“已初始化的变量”,按照之前所讲的,已初始化的变量应该是算在RW中的,所以两个程序之间应该是RW大小有区别。     
    Prog3:
            #include
            void main(void)
            {
                ;
            }           
    Prog4:
            #include
            char a = 5;
            void main(void)
            {         
                ;
            }
            Prog3编译出来后的信息如下:           
================================================================================          
Code RO Data RW Data ZI Data Debug
            948 60 0 96 0 Grand Totals         
================================================================================           
Total RO Size(Code + RO Data) 1008 ( 0.98kB) 
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB) 
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)          
================================================================================       
Prog4编译出来后的信息如下:         
================================================================================      
Code RO Data RW Data ZI Data Debug
            948 60 1 96 0 Grand Totals          
================================================================================          
Total RO Size(Code + RO Data) 1008 ( 0.98kB) 
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)            
================================================================================       
可以看出Prog3和Prog4之间确实只有RW Data之间相差了1个字节,这个字节正是被初始化过的一个字符型变量“a”所引起的。
再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。          
Prog3:
            #include
            void main(void)
            {
            ;
            }        
Prog4:
            #include
            char a;
            void main(void)
            {      
                ;
            }
Prog3编译出来后的信息如下:        
================================================================================          
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals           
================================================================================           
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)         
================================================================================        
Prog4编译出来后的信息如下:         
================================================================================         
Code RO Data RW Data ZI Data Debug
            948 60 0 97 0 Grand Totals         
================================================================================          
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)          
================================================================================           
编译的结果完全符合推测,只有ZI数据相差了1个字节。这个字节正是未初始化的一个字符型变量“a”所引起的。
注意:如果一个变量被初始化为0,则该变量的处理方法与未初始化华变量一样放在ZI区域。          
即:ARM C程序中,所有的未初始化变量都会被自动初始化为0。
    总结:  1; C中的指令以及常量被编译后是RO类型数据。
            2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
            3; C中的已被初始化成非0值的变量编译后市RW类型数据。
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
(5) Image$$??$$Limit 的含义
=0x0c100000+Tatal RO size+1
                   = 0x0c200000+0x37(4的倍数,0到55,共56个单元)


vivi是mizi开发的用于s3c241x/s3c244x 的linux bootloader,友善之臂移植了USB 下载功能后就成了现在看到的supervivi;u-boot是一个广泛用于ARM平台的bootloader, 目前也支持s3c241x/s3c244x,可以用来启动Linux;Eboot是WinCE平台下的bootloader。uboot就是通过usb来下载os image文件的bootloader; eboot就是通过ethernet下载os image的bootloader。

而嵌入式平台上就跟x86不一样了,但是很类似,而且因为不同的平台架构本身的特点,每种平台对应的bootloader做得事情会有所不同,相对x86平台,一般不会有bios(但是这些都不是绝对的,有一些平台也会有内嵌类似bios的启动程序),整个系统的引导加载都由存放在flash,rom等存储设备特定位置的bootloader来完成。如arm平台中的2410,2440,bootloader存在在flash中的0x0的地方,板子加电后,系统会将bootloader的最前面的4k代码通过硬件逻辑自动的装载到SRAM中,之后从SRAM中的0开始执行,在这4k的程序中会完成基本的硬件的初始化,将完整的bootloader搬到内存中,并跳转到ram中的bootloader来进行继续执行。

    回到之前所说的,bootloader启动起来之后,通常会有两种操作模式:

启动加载模式就是一上电,bootloader进行相关的初始化之后就马上把内核启动起来,注意关键的地方在整个过程中没有用户的参与,这种其实也就是bootloader的默认处理,一般的产品设计ok进行最后的发布时,就会处于此种状态。

下载模这种模式,大家肯定非常熟悉,就是大家在进行开发的时候所处的环境,我们经常使用的tftp, erase, cp.b 等命令将相关的bin,img文件烧到板子上,这种情况下其实就是处于bootloader的执行环境下,所以一定意义来说,大多的bootloader其实就是一个嵌入式操作系统,只是它的功能不强,不像linux的结构那么复杂,而且也不会支持多进程多线程处理。

bootloader 种类和分类

这里的分类实际上是依据上面的bootloader的操作模式来进行划分的,根据一个系统是否支持上面的下载模式我们这里将bootloader划分为bootloader和monitor(这不是我划分的,恩,是从别人的文章中引述过来的,不过我觉得他说的很有道理), 这里”bootloader”是指只是引导设备与执行主程序的固件,而”monitor”是指不仅拥有bootloader功能的,还能够进入下载模式的固件。

 (1)、ARM程序的组成

此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。

        ZI是程序中的未初始化的变量;ZI就是zero;

(3)ARM程序的执行过程         

从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。          

 3; ZI           

    (4) ROM主要指:NAND Flash,Nor Flash           

对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。

当把程序编写好以后,就要进行编译和链接了,在ADS1.2中选择MAKE按钮,会出现一个Errors and Warnings的对话框,在该栏中显示编译和链接的结果,如果没有错误,在文件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据:

Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028

Tatal RO size (Code+ RO Data)             184571 (180.25kB)

Tatal RW size(RW Data+ ZI Data)           17081(16.68 kB)

Tatal ROM size(Code+ RO Data+ RW Data)   184624(180.30 kB)

后面的字节数是根据用户不同的程序而来的,下面就以上面的数据为例来介绍那几个变量的计算。

     在ADS的Debug Settings中有一栏是Linker/ARM Linker,在output选项中有一个RO base选项,

  假如 RO base设置为0x0c100000,后面的RW base 设置为0x0c200000,然后在Options选项中有Image entry point ,是一个初始程序的入口地址,设置为0x0c100000 。

有了上面这些信息我们就可以完全知道这几个变量是怎么来的了:

|Image$$RO$$Base| = Image entry point =RO base =0x0c100000 ;表示程序代码存放的起始地址

|Image$$RO$$Limit|=程序代码起始地址+代码长度+1

                 = 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1

                  = 0x0c12d0fc

|Image$$RW$$Base| = 0x0c200000=RW base 地址指定

|Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Data 53

                   =0x0c200037

|Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038

|Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Data 17028

                   =0x0c200038 + 0x4284

                   =0x0c2042bc

也可以由此计算:

|Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData) 17081

                   =0x0c200000+0x42b9+3(要满足4的倍数)

                   =0x0c2042bc

上一篇:系统时钟和定时器简单记录
下一篇:没有了