人生有多少个十年,做人最要紧的就是痛快。
分类: LINUX
2015-01-19 14:51:59
通常PC在开机之后,会进入带有PC机厂商信息的BIOS画面,并且会显示出当前PC机的硬件信息,比如:内存大小,CPU信息等,它其实是PC机启动之后运行的第一段程序,它主要完成一些基本硬件初始化操作和硬件检测工作,保证拥有操作系统正常运行的软硬件环境,随后会加载并且启动操作系统。该段小程序是烧制到主板上的BIOS存储硬件里的。由此可见计算机系统在启动过程中必须依赖软硬件,在嵌入式系统中同样需要软硬件互相协调来完成启动过程。
1.嵌入式系统启动 - 硬件支持
在嵌入式系统中,通常并没有像 BIOS那样的固件程序,而是将用于引导系统的二进制映像文件(image文件)烧写到只读的ROM中,系统启动之后从ROM里加载并执行该映像文件。根据只读ROM类型的不同,启动方式也不尽相同。
嵌入式系统通常有两种启动方式:
(1)“硬盘”方式启动
所谓“硬盘”方式启动是指,启动程序被烧写在“硬盘”中,系统上电后CPU读取并执行“硬盘”中的指令数据,由于“硬盘”不支持随机寻址(支持向任意有效地址进行读写操作,硬盘里最小的寻址单元是扇区),并且读写速度太慢,通常是将“硬盘”中的引导程序指令数据拷贝到RAM(随机访问存储器)中,然后执行这些指令。
嵌入式系统中通常不使用PC机的硬盘作为文件存储器,而是使用类似硬盘的一些小容量Flash如Nandflash。S3C2440A处理器支持从Nandflash启动,这是由于S3C2440A Nandflash控制器中含有一个特殊的RAM – Stepping stone。
图2-38 Stepping stone原理图
当系统选择从Nandflash启动时,硬件会完成以下操作:
(a)通过Nandflash控制器将Nandflash中前4K的指令数据拷贝到Stepping stone中
(b)将Stepping stone所在地址0x40000000映射到0x0地址
(c)PC从0x0地址处取址执行
(2)ROM方式启动
以这种方式启动的系统通常会有一个专门用来存放启动程序的存储固件,当系统以该方式启动时,CPU直接从存储固件里运行启动程序。启动程序一般不会被擦除并且要求掉电时数据不能丢失,因此该存储固件通常是XIP(eXectute In Place片内可执行,代码不被压缩,并且支持随机寻址)类型的ROM(掉电非易失只读存储器)。嵌入式系统中经常使用Norflash作为启动程序存储固件。S3C2440同样也支持这种启动方式。
2.嵌入式系统启动 -软件支持
在嵌入式系统启动时,通常也有类似于BIOS的启动程序,该启动程序被称为Bootloader,由于Bootloader是被编译生成的,并且它的正确执行通常和系统硬件密切相关。
(1)二进制映像文件(image):
二进制映像文件是指烧写到ROM中的bin文件,也称为image文件。通常image文件是由编译器将源码编译而成的可执行二进制文件,源码中的语句,初始化变量,未初始化变量,和初始化为0的变量分别被编译成对应的操作指令和变量数据域,这些编译成的操作指令和变量数据域被链接器统一编址进image文件,作为image文件的输入。用户可以指定这些image文件的输入数据的属性,可以为只读的RO(Read-Only),可读写的RW(Read-Write)和初始化为0的ZI(Zero-Initial)。通常源码文件中含有以下属性数据段:
(a)只读属性RO的操作指令,如:控制语句,表达式
(b)只读属性RO的数据段,如:常量
(c)可读写属性RW的数据段,如:变量
(d)初始化为0的数据段ZI属性,如:初始化为0结构体
多个代码源文件具有相同属性的数据段为了方便存储器对其存储空间访问权限进行管理,通常被链接器放置在Image的相同位置,具有相同属性的输入数据段组成image文件的输出域。链接器允许用户指定每个image文件的输出域的起始地址。如图2-39所示。
图2-39image文件结构图
(2)RAM中的执行程序:
外部存储设备上的image没有被加载到内存中时,和普通文件一样,是不能被执行的,image要想被CPU执行,必须要被程序加载器Load到内存中,并且设置好执行环境。
由于执行程序是内存中的image文件,因此二者内容是相同的,但还是有一些差别:
(a)image文件是存储在外部存储设备里的(嵌入式系统中通常是ROM,如:Nandflash,Norflash),而执行程序只能运行在内存RAM中。
(b)程序中初始化为0的变量(ZI数据段,也称为BSS数据段)在image文件里是不存在的变量要想被当前程序访问必须指定一个有效的内存地址,而未初始化的变量通常被设置为0,因此全部为0的数据是没有必要放到image文件里的,这样反而增加了image文件的体积。
由此可见,image文件被加载到内存之后,还要为ZI段准备地址空间来存放初始化为0的ZI数据段。
ZI段准备操作如下:
l 准备ZI数据段地址空间
l 将空间内容清0
对于运行在操作系统中的程序,该项工作可由操作系统的运行环境(程序加载器)来帮之实现,而对于没有操作系统的裸机程序,这项工作只能由RO段的代码自己实现,它也是启动程序最主要的一项工作之一。
3.嵌入式系统启动过程
嵌入式系统启动过程就是指处理器从复位到进入操作系统或程序能够运行的状态。该过程是由系统加载引导程序(Bootloader)来实现的,它主要完成以下工作:
l 初始化必要硬件,如:关闭看门狗,初始化内存SDRAM,提供硬件执行环境
l 初始化C程序软件执行环境
l 将启动代码从ROM拷贝到RAM中
l 跳转到RAM里继续执行启动代码
(1)初始化必要硬件
(a)关闭看门狗
看门狗是许多嵌入式系统里带有的硬件装置,主要用于在外界环境,如:噪声,电磁干扰,系统错误等造成的系统故障时自动重启系统,恢复系统正常工作。通常在系统启动过程中,直接将其关闭,详情查看看门狗控制器章节。
(b)初始化内存
虽然程序可以在外部存储器ROM里执行,但是由于ROM是只读的,不能像内存一样执行写入操作,而在C程序中必须要求将变量安放到可以执行写入操作的内存中,因此在系统启动之前必须要对内存进行初始化。
(2)初始化C程序软件执行环境
(a)初始化C程序栈指针
在C程序执行过程中,要进行出栈,入栈操作,在C程序执行之前,必须要将栈指针初始化为有效内存地址。
(b)清零ZI段
因为ZI段并不存在于Image中,所以需要程序根据编译器给出的ZI段地址及其大小来将相应的RAM区域清零。RO段中的指令只有完成了上述两项工作之后,C程序才能正常执行访问变量,否则只能执行不含变量的只读指令代码。
(3)将启动代码从ROM拷贝到RAM(内存)中
由前面知识可知,启动程序通常存放在ROM中。ROM(只读存储器)的特点是里面数据是只读的,不能执行修改写入操作,在程序执行时,对变量的赋值操作其实就是对内存执行写入操作,如果该程序在ROM里执行,变量的赋值操作是不能实现的。RAM(随机访问存储器)可以支持随机地址读写,因此程序代码通常是在RAM里被执行的,而内存是典型的RAM,这样就要将代码从ROM拷贝到内存中。
在执行ROM里数据指令拷贝时需要知道下面三个参数:
l ROM中数据指令开始地址
l RAM中数据指令目标存放地址
l 要拷贝启动程序的大小
其中,第一个参数是由CPU体系结构决定的,像S3C2440 CPU,它在开机上电后会自动从0x0地址处取指运行,第二个参数由用户自己定义,可以将ROM里数据指令存放到任意有效内存区域(保证有足够空间存放),第三个参数则需要借助编译器来实现了。
ADS1.2集成开发环境下实现ROM到RAM拷贝
(a)Image文件的加载时地址与运行时地址
Image文件的加载地址是指由程序源文件编译生成的image文件被加载到内存时的地址,该地址由链接器的参数选项指定,在ADS1.2开发环境下,可以在下图中方框中位置中设置。
图2-40设置Image文件加载地址
Image文件的运行时地址是指,当前image文件被加载到内存后,运行时的地址,也就是指令真正的内存地址。通常情况下,加载时地址和运行时地址是一致的,只要设置了其加载时地址其运行时地址也就确定了。但是通常编译生成的image文件会被烧写到ROM中,而在CPU上电后,会将ROM地址映射为0x0地址,CPU自动从ROM开始处取指执行,也就是说指令在ROM中执行时,指令的实际地址并非真正的运行时地址,由于ARM指令中大部分指令是相对寻址的,这并不会影响大部分指令的执行。
(b)链接器自动生成的符号变量
ADS1.2链接器在对各数据段链接完成之后,会自动生成一些符号变量,用户程序可以通过引用这些符号变量取得Image文件各输出域的信息。
表2-4 ADS中输出域符号变量
符号 |
含义 |
Image$$RO$$Base |
RO输出域运行时起始地址 |
Image$$RO$$Limit |
RO输出域运行时结束地址 |
Image$$RW$$Base |
RW输出域运行时起始地址 |
Image$$RW$$Limit |
RW输出域运行时结束地址 |
Image$$ZI$$Base |
ZI输出域运行时起始地址 |
Image$$ZI$$Limit |
ZI输出域运行时结束地址 |
有了以上链接器提供的符号变量,在程序里就可以动态获得链接后的各个输出域的起始,结束地址了,也就能得到每个输出域的大小了。在ADS1.2开发环境下使用链接器符号变量时要将符号变量使用“|”括起来,可以通过下面代码实现从ROM到RAM的拷贝。
IMPORT |Image$$RO$$Base| ; 引入链接器自动生成符号变量Image$$RO$$Base
IMPORT |Image$$ZI$$Limit| ; 引入链接器自动生成符号变量Image$$RO$$Base
copy_code ; 代码拷贝开始符号
mov r0, #0x0 ; R0中为数据开始地址 (ROM数据保存在0地址开始处)
ldr r1, =|Image$$ RO$$Base| ; R1中存放RO输出域运行地址,
; 该值由符号变量Image$$RO$$Base取得
ldr r2, =|Image$$ZI$$Limit| ; R2中存放ZI输出域结束地址,
; 该值由符号变量Image$$ZI$$Limit取得
sub r2, r2, r1 ; R2 = R2 – R1,得出待拷贝数据长度
bl CopyCode2Ram ; 将R0,R1,R2三个参数传递给CopyCode2Ram函数执行拷贝
(4)跳转到RAM里继续执行启动代码
将代码从ROM拷贝到RAM之后,当前启动代码就有了两份,这时要让CPU去执行RAM里的启动代码,通常这会使用伪指令ldr来实现该功能:
LDR PC , =label
label
MOV R0, #0 ;内存中代码
上述加载伪指令LDRPC, =label是将label标签处的运行时地址赋值给PC(运行时地址由用户指定),同时该指令的跳转范围可以寻址4GB地址空间,可以实现从ROM地址空间跳转到RAM地址空间。上述指令执行完之后CPU就会跳转到内存中启动代码label处取指执行。
4. S3C2440的两种启动方式
由前面知识可知,通常嵌入式系统由两种启动方式:硬盘启动、ROM启动。S3C2440内核支持上述两种启动方式。
(1)S3C2440的Nandflash启动
当系统选择从Nandflash启动之后,硬件自动将0x0地址映射到Stepping stone,同时将Nandflash前4K代码拷贝到Stepping stone,由于Stepping stone大小只有4K,而系统启动程序大小往往超过4K,这就需要将全部的启动代码从Stepping stone搬运到空间更大的内存中运行。又由于内存,Nandflash等硬件还没有被驱动起来,不能正常使用,这就要保证Stepping stone里的代码必须要先对搬运过程中相关硬件进行初始化,然后执行搬运工作,搬运完成之后,跳入到内存中继续运行,以上工作都要在Stepping stone里实现。如图2-41所示。
图2-41 Nandflash启动过程
(2)S3C2440的Norflash启动
通常开发板上通常焊接Norflash来实现ROM启动方式,由于Norflash支持随机寻址和片内执行XIP,并且Norflash的容量一般足够大来存放Bootloader,但是由于Norflash是ROM,虽然可以像内存一样进行读操作,却不可以像内存一样支持写入操作,Bootloader中image文件的RW段和ZI段,只有放入到RAM中才可以正常执行写入,因此从Norflash启动也需要代码搬运工作。
++++++++++++++++++++++++++++++++++++++++++
转载自唐老师blog: