基于ARM的嵌入式BootLoader设计与启动过程
f, NOINT = 0x80
msr cpsr_cxsf,r1 ;转到IRQ模式
ldr sp,=IRQStack ;设置SP_irq
orr r1,r0,#FIQMODE
msr cpsr_cxsf,r1 ;转到FIQ模式
ldr sp,=FIQStack
3.4 C例程全局变量初始化
全局变量的初始化,就是完成从ROM到RAM的数据传输和内容清零。可执行程序的映像结构由RO段、RW段和ZI段三部分组成,分别为只读数据段、可写数据段和堆栈段。其中RO段在Flash和RAM里都可运行;而RW和ZI段是必须转移到RAM中去的。尽管RAM的运行速度比Flash快的多,但由于RO段比较小,拷贝到RAM也需要时间,还要程序跳转,一比较两者的启动时间差不多,最终我们选择让RO段在Flash中运行。
开发工具中的链接器(Linker)提供了一定的机制来帮助我们完成这部分工作,其中|Image$$ZI$$Base|,|Image$$ZI$$Limit|,|Image$$RW$$Base|,|Image$$RO$$Limit| 是由链接器定义输出的。主要是输出段的起始和终止定位信息,具体程序实现如下:
startram : LDR a1,=|Image$$ZI$$Base| ;ZI段在RAM里面的起始地址
MOV a3,#0 ;寄存器清0
LDR a2,=|Image$$ZI$$Limit| ;ZI段在RAM里面的结束地址
CMP a1,a2
BEQ move_data
clear_loop : STR a3,[a1],#4 ;清一个字为0, a1 += 4
CMP a1,a2
BNE clear_loop
move_data LDR a1,=|Image$$RW$$Base| ;RW段在RAM中的起始地址
LDR a2,=|Image$$RO$$Limit| ;RW段在ROM中的起始地址
LDR a3,=|Image$$ZI$$Base| ;RW段在RAM中的结束地址
CMP a1,a3
BEQ goto_main
move_loop : LDR a4,[a2],#4
STR a4,[a1],#4 ;拷贝一个字,a1 += 4, a2 += 4
CMP a1,a3
BNE move_loop
goto_main : BL Main
3.5 呼叫主程序
当系统初始化完成后,就要转入主程序,可由跳转指令来完成。这部分代码为C程序,主要负责uCLinux内核拷贝、 Ramdisk文件系统加载、设置启动参数和串口通信等功能.程序最终完成内核压缩文件和Ramdisk到RAM的拷贝,后跳转到RAM中Linux Kernel的首地址,并交出控制权,到此Bootloader的任务完成。其中uCLinux在RAM中的空间分配为0x0c000000
-0x0c008000(uCLinux内核调度用),0x0c008000-0x0c300000(uCLinux Kernel),0x0c300000
-0x0c800000(uCLinux Ramdisk)。以下程序为uCLinux内核和Ramdisk文件系统拷贝过程,FLASH_UC_KERNEL,FLASH_UC_RAMDISK分别为内核和文件系统在Flash的起始地址,RAM_UC
_ZIMAGE,RAM_UC_RAMDISK分别为RAM中uCLinux内核和文件系统的起始地址,FLASH_LEN_UC_
KERNEL,FLASH_LEN_UC_RAMDISK分别为两者大小,拷贝完后,PC指针指向RAM_UC_ZIMAGE。程序主要部分如下:
/* Copy uClinux Kernel */
pSource = (unsigned int *)FLASH_UC_KERNEL;
pDest = (unsigned int *)RAM_UC_ZIMAGE;
for (loopcnt = 0;loopcnt (FLASH_LEN_UC_KERNEL>>2);loopcnt++)
{ *pDest++ = *pSource ++; }
/* Copy ramdisk */
pSource = (unsigned int *)FLASH_UC_RAMDISK;
pDest = (unsigned int *)RAM_UC_RAMDISK;
for (loopcnt = 0;loopcnt (FLASH_LEN_UC_RAMDISK>>2); loopcnt++)
{ *pDest++ = *pSource ++; }
/* Start Kernel */
fp = (UClinux_Entry)RAM_UC_ZIMAGE;
(*fp)(0);
4 uCLinux操作系统启动过程
本系统采用RAM中启动方式,将Flash中的内核先拷贝到RAM的某一段地址区间,再从该区间的首地址运行 uCLinux内核。当Bootloader完成系统初始化和拷贝完内核和Ramdisk以后,交出控制权,系统开始从RAM中执行uCLinux内核的引导程序Head.s,并将控制权交给它。在uCLinux中,Head.s在uCLinux-dist/linux-2.4.x/arch /armnommu/boot/compressed/head.s
里。Head.s非常关键,它完成了加载内核的大部分工作,主要是调用Misc.c中的解压内核函数 (decompress_kernel)来完成,另外内核的加载还必须知道系统必要的硬件信息,该硬件信息在hardware.h中并被Head.s所引用。最后跳转到调用内核函数(call_kernel),将控制权交给解压后的uCLinux系统[4]。系统启动后将加载Ramdisk文件系统,进入用户shell程序。
5 结束语
本系统采用Bootloader完成操作系统内核和文件系统拷贝到RAM的设计方法,提高了系统的实时性。目前,启动代码已经正常引导uCLinux操作系统,并实现了对轮式移动机器人驱动系统的控制,该嵌入式系统运行稳定,完全实现了设计目的。由于所选处理器的关系,本文的Bootloader是不支持Remap芯片的,但启动装载程序的原理都一样,只要稍作修改就可用于支持Remap和MMU的芯片,而且对于其他操作系统同样适用。
参考文献
1 严国清
- 使用MMU进行地址重映射的启动代码结构探讨(11-10)
- ARM地址重映射的通俗解释(11-09)
- 地址重映射在ARM系统中的实现(04-12)
- U-Boot的编译与移植到QT-S3C44B0X开发板上(03-08)
- ARM S3C44B0X 之 看门狗定时器(11-20)
- S3C44B0X烧写flash 的经验(11-11)