作为一个嵌入式工程师,如果不能写bootloader程序,那就不能成为一个真正的嵌入式工程师.以前做linux driver,常对bootloader程序是一知半解,其实要写好bootloader程序还得好好去研读一下处理器 architecture.
ARM 处理器是一种很热门的嵌入式处理器,现在绝大部分的嵌入式产品都用到了ARM处理器,其低廉的价格和丰富的性能都受到的消费者的青睐.下面针对ARM处理器来具体谈一下ARM bootloader程序.
首先简单的了解一下ARM architecture.ARM主要由ARM内核单元,vector interface ,coprocessor,两种cache(instruction and data),两种TCM(instruction and data), MMU, power management等. cache是为了平衡数据之间的速度而使用的,而TCM是为了存储一些频繁使用的数据,如中断向量等,MMU主要处理虚拟地址到物理地址的转换,起到了对数据的保护作用,用在操作系统的体系中,很显然,bootloader程序就是要为后来的程序提供一个良好的运行环境,这里对ARM architecture的几个组成单元进行必要的配置.
综上所述,ARM bootloader程序主要完成下面几个功能:
1)初始化栈.
2)中断向量表的复制.
3)TCM 和cache的配置,如果有操作系统的话,就应该设置MMU.
4)建立一个程序执行文境.(具体一点,就是根据image文件的属性来处理image段的数据).
5)对内存如mddr,sram进行简单的测试.
6)需要设置一些外围设备,一般来说需要配置一下串口.
一、初始化栈:
应该对irq,firq,svc的栈进行指定,分别操作这几中模式的寄存器。如对irq模式.
mov r0 , #0x12; irq mode
msr CPSR , r0; set irq mode
ldr sp, =0x800; irq steak address
二、中断向量表的复制:
Boot_Def
ldr PC,Arm_Addr_Ini ;0x00
ldr PC,Arm_Addr_Und ;0x04
ldr PC,Arm_Addr_Swi ;0x08
ldr PC,Arm_Addr_Pre ;0x0C
ldr PC,Arm_Addr_Abt ;0x10
nop ;0x14
ldr PC,Arm_Addr_Irq ;0x18
ldr PC,Arm_Addr_Fiq ;0x1C
Arm_Addr_Ini DCD bootstrap_entry + 0x00 ;0x20
Arm_Addr_Und DCD undefined_exception_handler ;0x24
Arm_Addr_Swi DCD software_interrupt_handler ;0x28
Arm_Addr_Pre DCD prefetch_exception_handler ;0x2C
Arm_Addr_Abt DCD abort_exception_handler ;0x30
Arm_Addr_Irq DCD irq_interrupt_handler ;0x34
Arm_Addr_Fiq DCD fiq_interrupt_handler ;0x38
DCD 0x12345678 ; 0x3c
...
ldr r0,=Boot_Def
ldr r1, =0x0 ;address 0x0
mov r2,#0x3C
add r2, r2, r1
0
cmp r1, r2
bge %b1
ldrcc r3,[r0],#4
strcc r3,[r1],#4
b %B0
1
三、TCM的配置
TCM和cache是有区别的,虽然都是为了加快速度,但是TCM必须给它指定地址,TCM和cache的用途也不样,TCM保存常用到的数据如中断向量表和realtime数据,而cache只是为了加快处理器和外设之间的数据处理。
ldr r0, =0x0;
mrc p15, 0, r1, c9, c1, 1;读coprocessor instruction tcm region register.
orr r0, r0, r1;
mov r1, #1
orr r1, r1, r0;instruction tcm bit_o 置1,enable insturction tcm.
mcr p15,0,r1,c9,c1,1;写入
其它的cache配置也类似,主要读懂ARM document.
四、建立一个程序运行文境:
image(bin文件)包括俩部分:RO and RW,RO部分一般我们在加载前后的位置都不用变化,但是RW必须在加载后把他放到RAM中执行,所以RW部分我们把它映射到RAM地址中。
IMPORT |Image$$RO$$Base| ;image RO 开始地址
IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RO$$Length|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$RW$$Limit|
IMPORT |Image$$RW$$Length|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|
IMPORT |Image$$ZI$$Length|
ldr r0,=|Image$$RO$$Limit|
ldr r1,=|Image$$RW$$Base|
ldr r2,=|Image$$RW$$Limit|
3
cmp r1,r2
bge %B3
ldrcc r3,[r0],#4;从RO段的结束地址+1load rw段数据
strcc r3,[r1],#4;重新写入到rw段
b %B4
4
ldr r0,=|Image$$ZI$$Base|;处理ZI段数据
ldr r1,=|Image$$ZI$$Length|
add r1, r1, r0
ldr r1, =0X2000
ldr r2,=0
5
cmp r0, r1
bge %B6
strcc r2,[r0],#4;初始化ZI段数据为0
b %B5
6
五、对内存进行测试:
由于系统中有各种各样的内存,可以对内存进行简单测试。
六、UART的配置:
UART在嵌入式系统中很重要,虽然它很简单,但是在早期开发中,UART是必须的设备之一。UART作为下一篇中将进一步详细讨论。