BootLoader简介:
当完成用户程序的编译并下载到目标板上运行时,总是要首
先进行存储器的映射,然后通过 ADS(或 SDT)调试环境下载,显然,这个过程对
普通用户来说显得特别烦琐,然而,要在裸板(没有任何程序的系统板)上调试运
行程序,也只能采用这种方法。
如果能在用户设计的系统板上烧写一段 BootLoader程序,就可以将该过程屏蔽
起来,让用户通过一些简单的操作,就可完成程序的下载、调试等工作。在嵌入式
系统中,BootLoader的作用与 PC 机上的 BIOS 类似,通过 BootlLoader可以完成对
系统板上的主要部件如 CPU、SDRAM、Flash、串行口等进行初始化,也可以下载
文件到系统板、对 Flash 进行擦除与编程。事实上,一个功能完善的 BootLoader 已
经相当于一个微型的操作系统了。
BootLoader 作为系统复位或上电后首先运行的代码,一般应写入 Flash 存储器
中并从起始物理地址 0x0 开始。BootLoader 根据实现的功能不同,不相同。一个简单的 BootLoader程序可以仅仅完成串行口的初始化,并进行
通信,而功能完善的 BootLoader可以支持比较复杂的命令集,对系统的软硬件资源
进行合理的配置与管理。因此,用户可根据自身的需求实现相应的功能
涉及具体汇编代码前,有些术语要必须弄懂的
O:映像文件(image):指一个可执行文件,在执行的时候被加载到处理器中,它是ELF(Executable and Linking Format)格式的。
O:RO :是Read-Only的简写形式。一般存放的代码
O:RW:是Read-Write 的简写形式,一般存放初始化的数据
O:ZI:是zero-Initialized的简写形式。一般是存放初始化数据
O:输入段(Output Section):它包含一系列具有相同RO,RW,ZI属性的输入段
对于
一个ARM bootloader系统来说,本质上,bootloader作为引导与加载内核镜像的工具,必须提供以下几个功能,更确切地说,必须做到以下几点:
~1~初始化RAM(必需):bootloader应该能初始化RAM,因为将来系统要通过它来保存一些Volatile数据,但具体地实现要求依赖与具体的CPU以及硬件系统。~2~初始化串口(可选),bootloader应该要初始化及使能一个串口,通过
~1~ 它与控制台联系进行一些debug的工作;甚至与PC通信。
创建内核参数列表(针对linux操作系统,推荐)。
启动内核镜像(必需):根据内核镜像保存的存储介质不同,可以有两种启动方式:FALSH启动以及RAM启动;但是无论是哪种启动方式,下面的系统状态必须得到满足:
Bootloader 阶段1的代码实现:
1,定义ARM个模式的栈大小
2,申明各模式的栈
3,将各模式的栈与栈大小结合起来,既为各栈分配栈大小
4,申明一些标号量
5,以某标号标识,一开始处设置异常中断向量表,当冷启动时,直接跳转至对应处启动
6系统正常启动时:1使能各协处理器,2关闭MMU,内部指令/数据cache以及缓冲区,ARM体系bootloader中都无需MMU的功能,所有的地址都直接使用物理地址:cache也都关闭,原因.3
清空TLB,Caches以及写缓冲区,当系统冷启动时所有的保留数据都以无效处理,因此都要清空,况且cache都已经关闭 4
开系统各存储空间域的访问权限;ARM体系中系统的存储空间分为最多的16个域,每个域对应一定的内存区域,该区域具有相同的访问控制属性。MMU中寄存器C3用于控制与域相关属性的配置。(
以上涉及的是相关存储方面通用的处理,接着就要开始具体的硬件初始化的工作了,与
具体的CPU以及具体的存储芯片大小有关。)
1. 屏蔽所有的中断:为中断提供服务通常是操作系统设备驱动程序的责任,因此在bootloader的执行全过程中可以不必相应任何中断,中断屏蔽是通过写CPU提供的中断屏蔽寄存器来完成的。
2. 设置CPU的速度与时钟频率:系统复位后对于CPU的速度与时钟频率都有一个初始的默认值,可以直接使用该值,到内核启动初始化的时候再设置;也可以在此处直接设置。
3. SDRAM 的初始化以及使能,这个设置与具体的RAM大小有关,包括正确地设置系统的内存控制器以及各内存库控制寄存器。具体代码稍长,在这里相关代码略去。
相关基本硬件初始化之后,开始设置模式。
1. 为ARM的各个操作模式设置称栈指针:在前面的伪代码中已经为各个操作模式分配相应的栈区,而且ARM各个模式下都有自己通用的栈顶指针SP,现在要做的是在不同模式下将该模式下的SP指向栈顶。每个模式的处理都类似,都是通过设置状态寄存器CPSR(可以参看前面章节的介绍)来完成,并且每个模式都要完成。以IRQ模式为例:
到此时,用汇编编写的硬件基本初始化代码完成,对于阶段1来说,剩下的工作就是将镜像2拷贝到SDRAM的指定处,这个工作我们通过一小段C代码来完成,文件名为cstart.c,完成拷贝功能的函数设为cstart。则在汇编末尾可以通过跳转指令进入阶段1的这一小段C代码:
/***************************有关中断向量表**************/
为什么在中断向量表中不直接LDR PC,"异常地址".而是使用一个标号,然有再在后面
使用DCD 定义这个标号
A:因为LDR 指令只能跳到当前PC 4kB 范围内,而B 指令能跳转到32MB 范围,而现在这样
在LDR PC, "xxxx"这条指令不远处用"xxxx"DCD 定义一个字,而这个字里面存放最终异
常服务程序的地址,这样可以实现4GB 全范围跳转
如:在PROTEUS 中的芯片组:
LPC2220的 复位与存储器映射
芯片复位后, MAP=00 ,启动 boot 装载程序, boot 装载程序检测 P0.14 口的状态和用户的异常向量表,判断是进入
ISP 还是启动用户程序,若启动用户程序,则自动设置 MAP=1 或 3 。若用户程序需要更改异常向量表,可以将异常向量表复制到片内 0x40000000 ,然后设置 MAP=2 进行重新映射, 0x40000000 地址上的向量表就可以更改了。
LPC2220 在复位
运行的第一段程序并不是用户程序,而是 boot block( 引导模块 ) ,是设计厂家在 ARM 内部固化的一段代码,用户无法修改或删除,其主要功能为:
判断运行那个存储器上的程序。
检查用户代码是否有效。(主要检测异常向量表的机器码值之和是否为 0 )
判断芯片是否被加密。
芯片的在应用编程( IAP )和在系统编程( ISP )
boot block( 包括 bootload 和 64 字节异常向量表 ) 。
对于LPC2220,复位后(也即是运行完Boot Block后),用户所能见到的存储空间,及其各存储空间上的内容。
0xF0000000----0xFFFFFFFF:AHB外设;
0xE0000000----0xEFFFFFFF:VPB外设;
0x80000000----0xDFFFFFFF;保留给外部存储器;
0x7FFFDFFF---0x7FFFE000 ; BOOT BLOCK ;
0x40004000----0x4000FFFF;64KB片内RAM;
0x00000000—0x0000003f,ARM异常向量位置;
存储器映射控制
MAP=00 :由任何硬件复位激活, boot block 中断向量映射到存储器的底部以允许处理异常。
MAP=01 :中断向量表没有被重新映射,它位于存储器的底部。
MAP=10 :由用户程序激活,中断向量表重新映射到静态 RAM 的底部。
MAP=11 :用户外部模式,由 BOOT[1:0] 来控制存储器的引导方式,中断向量表重新映射到外部存储器的底部。
例如:每当产生一个软件中断, ARM 内核就从 0x00000008 处取出 32 位数据,当 MAP=0x11 时,这就意味着从 0x00000008 处取值既是对 0x80000008 处取值。
存储器重新映射控制用于改变从地址 0x00000000 开始的中断向量表的映射,使就使得运行在不同存储器空间中的代码对中断的控制。
拿LPC说
bootblock 出厂时固化的Loader 主要实现ISP功能!
bootloader 自己写的程序
再到main()
上电后bootblock 检测是否有ISP的相关引脚被设定,如果设定进入ISP模式,如果没有设定 运行用户程序(一般来说时用户的BootLoader),然后启动内核或者其他的。
关于异常向量表,配置好你自己的就可以了,不用管 bootblock 上的!
1 设置异常中断向量表
ARM处理器的中断向量表从地址0x0处开始存放,连续有8×4字节的空间。在ARM存储空间里每个字32位,占4个字节。可以通过图1来描述中断向量表的地址分配。
每当有中断或者异常发生时,ARM处理器便强制把PC指针指向向量表中对应中断类型的地址值。为了加快中断响应,我们在Flash的0x0地址存放能跳转到0x0c000008地址处中断向量的跳转指令,即在RAM中建立一个二级中断向量表,起始地址为0x0c000008,除复位外,其它异常入口地址由Flash跳转得到,如表1所示: