陈 才,马连伟
(浙江科技学院 自动化与电气工程学院,杭州310023)
BootLoader是嵌入式系统上电后启动运行的第一段代码,用于对嵌入式系统硬件设备的最底层操作,不但包括嵌入式系统中的CPU、存储器、通信外设的初始化设置、嵌入式系统参数配置和人机接口实现,而且还包括对嵌入式操作系统的引导启动和下载固化等操作。因此,BootLoader在嵌入式系统设计中起极其重要的作用,是整个嵌入式系统应用的基础。现代嵌入式设备如手机Androd系统、平板电脑MacOS系统等底层软件开发过程中,BootLoader是其中不可缺少的环节,BootLoader的设计是其中的难点和核心点。采用Windows操作系统的嵌入式设备,用于启动引导和加载操作系统的程序代码BootLoader称为Eboot。Eboot程序框架代码的设计主要分为BLCommon代码、OEM代码、Eboot代码、存储管理代码和EDBG调试工具代码5个部分[1]。
BLCommon代码实现了通用的BootLoader框架,它提供了与 Windows CE内核访问的接口。BLCommon实现的功能包括:将BootLoader自身的代码从NAND拷贝到SDRAM中,以加快程序运行速度;将Windows CE内核镜像文件NK.bin解码成可执行的指令码,并进行代码校验、代码下载工作;提供OS启动或加载进度条的图形界面显示;调用OEM代码实现硬件初始化等。BLCommon代码被编译成BLCommon.lib静态库,供用户调用。
Eboot代码实现了对DHCP、TFTP、UDP等网络通信协议的支持。Eboot代码被编译为Eboot.lib静态库,是Eboot框架代码的数据传输的主要组成部分。
OEM代码是BootLoader中直接对硬件进行操作的函数,这些函数通常与硬件密切相关,在Window CE5.0中这些函数一般以OEM开头。比如:对Flash进行读、写、编程、擦除操作的OEM函数,对以太网卡CS9000或DM9000进行操作的OEM函数等。
存储管理代码主要是完成操作系统启动过程中对SDRAM、Cache、MMU等硬件的初始化和对各种数据结构、堆、栈等进行内存分配。
EDBG调试工具代码是方便调试跟踪BootLoader的代码,设计者可以通过这些代码输出Bootloader运行过程中的调试信息。
Windows CE BootLoader的设计又叫BootLoader的移值。一个功能齐全、结构清晰的BootLoader框架运行主要包括2个相对独立的阶段[2],每个阶段都操作特定的硬件并完成特定的功能。Windows CE BootLoader启动的前一个阶段叫作Nboot,第二个阶段叫作Eboot。
图1 Nboot阶段运行流程图Fig.1 Nboot stage run flow diagram
1)初始化硬件设备,主要包括:PLL设置、关闭中断、关闭MMU、清空TLB等。
2)配置SDRAM控制的参数。
3)为第二阶段代码运行初始化一段SDRAM内存,内存大小1M左右。
4)拷贝第二阶段代码到SDRAM中。
5)设置好高级语言运行环境。
6)跳转到第二阶段程序入口地址运行。
Nboot运行流程图如图1所示。
Nboot完成的功能主要采用汇编语言编写和C语言混合编写。相关源代码文件及功能说明如下:
Nboot阶段的代码经过编译、连接后形成可执行文件为Nboot.nb0。
Eboot又称为Ethernet boot,是一种高级的BootLoader,整个 Windows CE BootLoader的大部分功能在这一阶段完成,Eboot的运行流程图如图2所示。这一阶段完成的主要功能有:
1)初始化Eboot阶段所要用到的硬件设备,主要包括:串口驱动、网卡驱动、液晶屏驱动等[3]。
2)检测系统的内存映射。
3)将OS映像从FLASH拷贝到SDRAM中。
4)设置内核启动参数,并启动内核。
图2 Eboot运行流程图Fig.2 Eboot run flow diagram
EBOOT是由Main()函数进入的,其代码如下:
由代码可以看到Eboot阶段的Main函数的主要任务是调用系统的BootLoaderMain()函数,该函数是微软提供的专门用来加载启动Wince的OS镜像的函数,其定义位于%WINCE500%\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\Blcommon.c文件中。Eboot的代码框架如图3所示。
图3 Eboot的软件框架图Fig.3 Eboot software framework
在BootLoaderMain()函数中,主要调用如下几个函数:
各函数模块的主要功能:
1)KernelRelocated() 将Eboot中的全局变量全部拷贝到SDRAM中,确保Eboot中的全局变量能在RAM中读写。
2)OEMDebugInit() 初始化调试硬件接口,一般为串口,该函数将调用OEMInitDebugSerial()函数来初始化调试串口,用于在调试过程中打印相关调试信息到终端。
3)OEMPlatformInit() 该函数全面初始化目标板上的硬件设备。需要根据实际电路板的硬件资源来进行配置,一般主要包括LCD\RTC\以太网\串口(除调试串口外)等的配置工作。
4)OEMPreDownLoad() 完成以太网拷贝OS映像前的准备工作,主要包括获取和设定IP地址,初始化 DHTP\TFTP\UDP服务等[4-5]。
5)DownLoadImage() 从远程开发主机端下载操作系统映像到嵌入式目标板的FLASH或SDRAM中。这个函数是一个比较大的函数。也是Eboot功能的主要实现模块。
6)OEMLaunch() 将PC指针直接指向SDRAM中OS映像的执行地址,启动OS映像的执行。它是EBOOT执行的最后一个函数,同时也是OS中OAL.exe模块运行的起点。
在理解BootLoder的基本原理和 Windows CE BootLoader架构分析的基础上,可以进行 Windows CE BootLoader的移植工作。所谓“移植”就是将一定的软件框架运行到具体的硬件环境中。Windows CE BootLoader移植过程实际是实现BootLoader中与目标系统硬件操作的相关函数的编写,以适应不同的硬件环境。微软将这些直接操作硬件的函数称为OEM函数,函数名一般以OEM开头[5]。由于嵌入式系统硬件设备多种多样,每种设备的工作模式、配置、地址及功能选择都不相同,因此微软只是为这些操作起了一个函数名,而没有提供各种外设的OEM函数的源代码。这些函数的具体实现需要嵌入式设计人员根据具体的芯片选型和硬件电路设计来进行编写。即:微软只规定对哪些硬件需要做哪些操作,但没有提供具体操作方法和代码,这些由嵌入式设计人员根据实际硬件电路来具体完成。这种软件架构的主要思想是将软件中与硬件无关的部分和硬件相关的部分隔离开来。微软已经做好与硬件无关部分的软件设计,而与硬件相关部分的代码只留出了接口函数,用户使用时可根据具体的硬件来设计,这样的好处使得软件的重用性得到极大的提高。用户仅仅需实现Eboot中操作硬件有关的OEM代码。Eboot中对硬件操作OEM函数分类为:控制程序运行的OEM函数、与系统调试相关的OEM函数、与下载OS相关的OEM函数、与FLASH烧写相关的OEM函数、与以太网相关的OEM函数及与时钟相关的OEM函数,见表1。
表1 OEM函数分类Table 1 Classfication of OEM function
在移植过程中,应根据具体采用的FLASH、以太网、调试接口芯片和电路原理图来确定各个外设的物理地址和控制方式,然后依据相关的数据手册来实现以上的各个OEM函数。
在Windows CE BootLoader的移植过程中,要把握2个重点:一是需要对ARM体系结构和指令系统有比较深入的理解,熟练掌握ARM的硬件资源的使用;二是要掌握Eboot的软件架构,对一个软件体系的掌握和使用,关键是看对软件架构的理解。只有对这两方面都有比较深入的理解才能写出结构清晰、功能齐全、界面良好的BootLoader程序,为后续的WindowsCE操作系统移植打下一个良好的基础。
[1]刘林真,刘大茂.基于 WinCE.NET的BootLoader原理与启动分析[J].计算机与数字工程,2008,36(10):111-128.
[2]何宗健.Windows CE嵌入式系统[M].北京:北京航空航天大学出版社,2006:214-220.
[3]王黎明,陈双桥,闫晓玲,等.ARM9嵌入式系统开发与实践[M].北京:北京航空航天大学出版社,2008:407-453.
[4]吴细宝,高梅国.基于 WinCE的嵌入式远程监控系统的设计与实现[J].仪器仪表学报,2008,129(4):547-550.
[5]王浩.Windows CE嵌入式应用开发实训教程[M].北京:中国水利水电出版社,2010.