邓正维,邓小武,2,邓绍伟,2,李森林,2
(1.怀化学院 计算机科学与工程学院,湖南 怀化 418008;
2.武陵山片区生态农业智能控制技术湖南省重点实验室,湖南 怀化 418008)
随着计算机技术、微电子技术、现代制造工艺和设计能力的不断进步和发展,硬件的集成度越来越高,使嵌入式系统在生产、生活中被广泛采用。但随之而来的是用户需求也越来越高,为了充分发挥嵌入式设备的性能,需要一个强大的操作系统,而在操作系统运行之前需要一段程序完成基本的初始化操作进而启动操作系统,Boot Loader就是这段程序。此举实现了系统从硬件启动到操作系统启动的过渡,它固化在硬件中,被认为是嵌入式系统不可缺少的一部分,其作用类似于PC上的BIOS和GRUB。但当前嵌入式设备上实现的Boot Loader大多只支持一个操作系统的引导,大大限制了嵌入式设备的使用。
本文结合在ARM平台上广泛使用的U-Boot,详细讨论了嵌入式Boot Loader的整体架构和运行流程。阐述了系统主要硬件的原理和裸机驱动程序设计,包含系统时钟设置,CPU模式设置,内存初始化,NAND FLASH配置和操作方法,WinCE和嵌入式Linux在ARM平台下启动的实现原理和方法。最终实现了在ARM平台上的双启动Boot Loader。
本文使用的开发平台为友善之臂Mini6410开发板,使用Samsung公司设计生产的S3C6410处理器,使用2片K4X1G163PC构成256 M SDRAM,将K9GAG08U0E作为永久存储器。
S3C6410基于ARM1176JZF-S内核[1-2],包括分立的16 kB指令和16 kB数据Cache,16 kB指令和数据TCM,及1个完全的MMU,用以处理虚拟存储管理。S3C6410内部有3个PLL,分别是APLL,MPLL和EPLL。APLL用于ARM时钟操作,MPLL用于主时钟操作,EPLL用于特殊用途。时钟操作被分为三组:APLL产生ARM时钟;MPLL产生主系统时钟,用于操作AXI,AHB和APB总线;EPLL产生的时钟主要用于外设IPS,对CPU的设置主要采用CPU模式、中断和时钟设置,Boot Loader和操作系统运行在SVC模式,在U-Boot中无需中断,时钟的选择根据应用环境而定。
本系统选用两片64 M×16 bit的Mobile DDR芯片,连接到S3C6410 SRAM控制器,工作频率为133 MHz,在处理器内部时钟为533 MHz时,能够接近最高使用效率。内存的初始化主要是对SRAM控制器的初始化,具体的初始化步骤在S3C6410手册上有详细说明[3]。具体流程如下:
(1)使SRAM控制器进入配置模式;
(2)填写内存芯片的时序参数,并开始内存芯片的初始化序列;
(3)配置完成后检查状态标志位。
NAND FLASH作为嵌入式系统中广泛使用的永久性存储器[4],具有容量大、改写速度快等优点,适用于大量数据的存储,但是由于NAND FLASH的设计原理,在操作时有自己独特的要求:只能以页为单位读写,而且写时只能将1写为0。所以在写入前必须以块为单位擦除。
S3C6410内部包含一个NAND FLASH控制器,用户只需配置好NAND FLASH的时序参数,NAND FLASH控制器即可自动产生所需时序[5]。主要配置的时序为CLE/ALE拉高到nWE拉低的等待时间,nWE为低的持续时间,nWE拉高后CLE/ALE继续保持为高的时间。
将NAND FLASH分为如下区域:Boot Loader,Linux_kernel,Linux_rootfs,WinCE等。通过外部跳线,NAND FLASH启动后,S3C6410将自动加载前8 k内容到S3C6410的内部SRAM(Steppingstone)并开始从地址0处运行,最终加载并引导操作系统,如图1(a)所示。
引导操作系统时需要用户在控制台中输入命令选择启动的操作系统,系统将加载制定NAND FLASH中的内容到内存指定位置,图1(b)为物理内存使用规划,该配置内容将保存在开发板配置文件$(board).h中。
图1 物理内存和NAND FLASH划分
U-Boot(Universal Boot Loader)是遵循 GPL协议的开放源码项目。具有系统引导、上电自检、CRC32校验、设备驱动、支持 NFS挂载、支持多种方式存储等功能。在嵌入式领域被广泛使用。
U-Boot属于两阶段启动的Boot Loader,在第一阶段由汇编语言完成,与处理器直接相关,完成CPU模式切换,禁止看门狗,初始化内存控制器,设置堆栈,重定位U-Boot运行位置等操作,最后跳转到第二阶段代码运行。第二阶段代码主要由C语言完成,与目标板相关,主要完成所有设备的初始化工作,最后加载并启动操作系统,具体流程如图2所示。
图2 U-Boot流程
U-Boot目标文件通过<$(board)/u-boot.lds>链接脚本控制,该文件定义了连接到目标文件中各段的名称和位置,从该文件可以看出,U-Boot由<$(CPU)/start.o>文件开始,start.o由start.S编译生成,包含了U-Boot第一阶段的主要代码。第二阶段代码以<$(arch)/board.c>为入口。
U-Boot支持多种平台,其驱动程序代码位于drivers文件夹下,各设备的板级配置信息由
为提高软件的复用性,系统将操作系统的引导函数作为独立的U-Boot命令添加到U-Boot中,支持多种命令,其命令的实现代码保存在Common中,以“cmd_”作为文件前缀,添加U_Boot命令时需使用U-Boot提供的U_BOOT_CMD宏声明。
U_BOOT_CMD各参数的意义:
(1)Name:命令的名称,用于唯一区别命令,在U-Boot中如果命令的前缀不同,在终端可以直接输入前缀执行命令。
(2)Maxages:命令可以接收的最多的参数个数。
(3)Cmd:命令的实现函数,命令被执行时,该函数被调用。
(4)Uasege:短的帮助信息,使用help时将打印该信息。
(5)Help:长的帮助信息,使用help Cmd时打印该信息。
在添加完代码后,还需在
U-Boot要启动操作系统,必须首先完成必要硬件的初始化,设置好操作系统的运行环境后加载并启动操作系统。
Mini6410使用三星公司设计生产的基于 ARM1176JZF-S内核的S3C6410处理器[6]。为提高代码复用度,减小移植难度,选用三星公司修改的U-Boot1.1.6版本,该版本支持与本开发板类似的SMDK6410,适当修改后可以应用在本开发板上,可实现项目的主要功能。
修改/Makefile:添加mini6410的编译命令:
-CROSS_COMPILE= /usr/local/arm/4.2.2-eabi/usr/bin/arm
linux-
+ CROSS_COMPILE = arm-linux-
+ mini6410_nand_conf i g : unconf i g
+@$(MKCONFIG)mini6410 arm s3c64xx mini6410 samsung
s3c6410 NAND ram256
复制参考板代码:
board/samsung/smdk6410-> board/samsung/mini6410
include/conf i gs/smdk6410.h-> include/conf i g/mini6410.h
修改平台相关代码:
(1)Include/conf i gs/Mini6410.h
Mini6410.h包含了所有Mini6410开发板的配置选项,如SDRAM的大小、位置,NAND FLASH的大小,串口的配置,网卡的型号,MAC地址,IP地址等信息,以及默认的内核启动参数等。
(2)CPU/s3c64xx/Start.s
Start.s是整个U-Boot的入口,包含了最基本的设置CPU模式的代码,可调用内存初始化函数、系统时钟初始化函数、第二阶段入口函数。本文件主要修改的内容是去掉SMDK6410中包含的ONENAND初始化代码。
(3)Board/Samsung/Mini6410/Mini6410.c
Mini6410.c包含了一些板级初始化代码,包含网卡初始化函数的调用、LCD初始化函数调用代码等,以及虚拟内存地址到物理内存地址的转换函数。本文件主要修改的内容是去掉SMDK6410中包含的CS8900网卡的初始化函数,添加了DM9000的初始化函数和USB下载功能的支持函数。
(4)Common/Main.c
Main.c包含了U-Boot接收并执行命令的主循环函数man_loop(),本文件的代码与体系无关,不用做其他修改。但为了提高U-Boot的可用性添加了菜单函数。
Boot Loader引导WinCE需要完成以下操作:
(1)设置CPU为SVC模式。
(2)完成CPU,内存控制器,系统时钟,串口,Caches,TLBs的初始化。
(3)解压WinCE内核镜像文件头,检查校验和,加载内核到指定位置。
(4)跳转之前禁用中断和MMU。
解压文件头需要分析WinCE文件头格式[7],WinCE镜像存在两种格式,分别为nb0和bin。nb0是原始的二进制镜像,可以直接烧到FLASH/ROM中,它不包括头,可以直接跳转到其入口执行,一般情况下采用nb0将内核下载到设备的RAM中运行。bin是一种二进制镜像格式,以片断为单位组织数据,每个片断都包括一个头,头中指定有起始地址、长度、校验值。Platform Builder将WinCE内核所有文件以bin格式合并成一个文件,默认文件名为nk.bin。Boot Loader需要将nk.bin分解成多个文件放到RAM中。
启动WinCE操作系统只需要完成必要的硬件初始化,设置好WinCE系统的运行环境,然后读取并校验WinCE镜像文件,由镜像文件头部获取WinCE的加载地址,并将WinCE保存到指定的位置后,跳转到其开始地址,就可以成功引导WinCE操作系统,如图3所示。
Boot Loader引导Linux内核需要完成以下操作:(1)设置CPU为SVC模式。
(2)完成CPU,内存控制器,系统时钟,串口,Caches,TLBs的初始化。
(3)加载Linux内核镜像文件到内存指定位置。
(4)设置Linux内核启动参数并跳转到内核。
图3 启动WinCE
通过Boot Loader启动内核要传递三个参数:将第一个参数放在寄存器0中,一般r0=0;第二个参数放在寄存器1中,是机器类型ID;第三个参数放在寄存器2中,是启动参数标记列表(TaggedList)在RAM中的起始基地址。
其中机器ID定义在
Linux内核的引导只需要完成必要的硬件初始化操作,设置运行环境后,读取Linux内核到预先规划好的位置;设置好TaggedList后,将TaggedList,MachID,内存参数,文件系统位置等信息传递给Linux内核后就能完成Linux内核的引导,如图4所示。
本文以三星S3C6410为处理器的Mini6410开发板为硬件平台,以U-Boot为基础,实现了在一个嵌入式设备上使用一个Boot Loader 引导多个操作系统的目的。本文将系统启动的代码实现为一个独立的函数,有利于降低项目代码的耦合性,方便后续嵌入式系统的开发。