BOOT LOADER(引导装载器),是用于初始化目标板硬件,给嵌入式操作系统提供板上硬件资源信息,并进一步装载、引导嵌入式操作系统运行的固件。在嵌入式系统开发过程中,很多情况都会涉及底层BOOT LOADER的移植问题,即使在有些已有BOOT LOADER的参考开发板上也存在这种可能。概括来说,如下情况会考虑进行BOOT LOADER的移植工作:
A. 在自主设计的目标板上,用于引导嵌入式操作系统及其应用;
B. 在厂家未提供BOOT LOADER源码的参考板上,遇有如下情形之一:
a. 在实际应用中需要添加或修改一些功能;
b. 为了给自行设计主板移植BOOT LOADER提供参考,先在参考板上进行移植以积累经验;
另外,从嵌入式系统实际开发角度讲,嵌入式操作系统的引导、配置甚至应用程序的运行状况都和BOOT LOADER有一定的关联,可以说,掌握BOOT LOADER移植是顺利进行嵌入式系统开发的重要利器。
与常见的嵌入式操作系统板级支持包BSP相比,BOOT LOADER与底层硬件更为相关,即每个不同配置的目标板基本都有不同的BOOT LOADER。因为BOOT LOADER往往更依据量体裁衣、定身制作的原则,以满足要求的最小化代码存放在启动ROM或FLASH中。
诚然,自行编写BOOT LOADER未尝不可,但从可利用的资源和实际项目开发考虑,采用移植已有的BOOT LOADER源码来解决这一问题更符合大多数项目开发的要求。
1 U-Boot简介
U- Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、 x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11 月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。
选择U-Boot的理由:
① 开放源码;
② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;
③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;
④ 较高的可靠性和稳定性;
④ 较高的可靠性和稳定性;
⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;
⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;
⑦ 较为丰富的开发调试文档与强大的网络技术支持;
2 U-Boot主要目录结构
- board 目标板相关文件,主要包含SDRAM、FLASH驱动;
- common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测;
- cpu 与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件;
- driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好)
- doc U-Boot的说明文档;
- examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c;
- include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件;
- lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件;
- net 与网络功能相关的文件目录,如bootp,nfs,tftp;
- post 上电自检文件目录。尚有待于进一步完善;
- rtc RTC驱动程序;
- tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具;
3 U-Boot支持的主要功能
U-Boot可支持的主要功能列表
系统引导 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统
支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;
基本辅助功能 强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲;
支持目标板环境参数多种存储方式,如FLASH、NVRAM、EEPROM;
CRC32校验,可校验FLASH中内核、RAMDISK镜像文件是否完好;
设备驱动 串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持;
上电自检功能 SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号;
特殊功能 XIP内核引导;
4 U-Boot移植过程
① 获得发布的最新版本U-Boot源码,与Linux内核源码类似,也是 bzip2的压缩格式。可从U-Boot的官方网站http://sourceforge.net/projects/U-Boot上获得;
② 阅读相关文档,主要是U-Boot源码根目录下的README文档和U-Boot官方网站的DULG(The DENX U-Boot and Linux Guide)文档http://www.denx.de/twiki/bin/view/DULG/Manual。尤其是DULG文档,从如何安装建立交叉开发环境和解决U-Boot移植中常见问题都一一给出详尽的说明;
③ 订阅U-Boot用户邮件列表http://lists.sourceforge.net/lists/listinfo/u-boot-users。在移植U-Boot过程中遇有问题,在参考相关文档和搜索U-Boot-User邮件档案库http://sourceforge.net/mailarchive/forum.php?forum_id=12898仍不能解决的情况下,第一时间提交所遇到的这些问题,众多热心的U-Boot开发人员会乐于迅速排查问题,而且很有可能,W.D本人会直接参与指导;
④ 在建立的开发环境下进行移植工作。绝大多数的开发环境是交叉开发环境。在这方面,DENX 和MontaVista均提供了完整的开发工具集;
⑤ 在目标板与开发主机间接入硬件调试器。这是进行U-Boot移植应当具备且非常关键的调试工具。因为在整个U-Boot的移植工作中,尤其是初始阶段,硬件调试器是我们了解目标板真实运行状态的唯一途径。在这方面,W.D本人和众多嵌入式开发人员倾向于使用BDI2000。一方面,其价格不如ICE调试器昂贵,同时其可靠性高,功能强大,完全能胜任移植和调试U-Boot。另外,网上也有不少关于BDI2000调试方面的参考文档。
⑥ 如果在参考开发板上移植U-Boot,可能需要移除目标板上已有的BOOT LOADER。可以根据板上BOOT LOADER的说明文档,先着手解决在移除当前BOOT LOADER的情况下,如何进行恢复。以便今后在需要场合能重新装入原先的BOOT LOADER。
5 U-Boot移植方法
当前,对于U-Boot的移植方法,大致分为两种。一种是先用BDI2000创建目标板初始运行环境,将U-Boot镜像文件u-boot.bin下载到目标板RAM中的指定位置,然后,用BDI2000进行跟踪调试。其好处是不用将U-Boot镜像文件烧写到FLASH中去。但弊端在于对移植开发人员的移植调试技能要求较高,BDI2000的配置文件较为复杂。另外一种方法是用BDI2000先将 U-Boot镜像文件烧写到FLASH中去,然后利用GDB和BDI2000进行调试。这种方法所用BDI2000的配置文件较为简单,调试过程与U- Boot移植后运行过程相吻合,即U-Boot先从FLASH中运行,再重载至RAM中相应位置,并从那里正式投入运行。唯一感到有些麻烦的就是需要不断烧写FLASH。但考虑到FLASH常规擦写次数基本为10万次左右,作为移植U-Boot,不会占用太多的次数,应该不会为FLASH烧写有什么担忧。同时,W. D本人也极力推荐使用后一种方法。笔者建议,除非U-Boot移植资深人士或有强有力的技术支持,建议采用第二种移植方法。
6 U-Boot移植主要修改的文件
从移植U-Boot最小要求-U-Boot能正常启动的角度出发,主要考虑修改如下文件:
① <目标板>.h头文件,如include/configs/RPXlite.h。可以是U-Boot源码中已有的目标板头文件,也可以是新命名的配置头文件;大多数的寄存器参数都是在这一文件中设置完成的;
② <目标板>.c文件,如board/RPXlite/RPXlite.c。它是SDRAM的驱动程序,主要完成SDRAM的UPM表设置,上电初始化。
③ FLASH的驱动程序,如board/RPXlite/flash.c,或common/cfi_flash.c。可在参考已有FLASH驱动的基础上,结合目标板FLASH数据手册,进行适当修改;
④ 串口驱动,如修改cpu/mpc8xx/serial.c串口收发器芯片使能部分。
7 U-Boot移植要点
① BDI2000的配置文件。如果采用第二种移植方法,即先烧入FLASH的方法,配置项只需很少几个,就可以进行U-Boot的烧写与调试了。对PPC 8xx系列的主板,可参考DULG文档中TQM8xx的配置文件进行相应的修改。下面,笔者以美国Embedded Planet公司的RPXlite DW板为例,给出在嵌入式Linux交叉开发环境下的BDI2000参考配置文件以作参考:
; bdiGDB configuration file for RPXlite DW or LITE_DW
; --------------------------------------------
[INIT]
; init core register
WSPR 149 0x2002000F ;DER : set debug enable register
; WSPR 149 0x2006000F ;DER : enable SYSIE for BDI flash program
WSPR 638 0xFA200000 ;IMMR : internal memory at 0xFA200000
WM32 0xFA200004 0xFFFFFF89 ;SYPCR
[TARGET]
CPUCLOCK 40000000 ;the CPU clock rate after processing the init list
BDIMODE AGENT ;the BDI working mode (LOADONLY | AGENT)
BREAKMODE HARD ;SOFT or HARD, HARD uses PPC hardware breakpoints
[HOST]
IP 173.60.120.5
FILE uImage.litedw
FORMAT BIN
LOAD MANUAL ;load code MANUAL or AUTO after reset
DEBUGPORT 2001
START 0x0100
[FLASH]
CHIPTYPE AM29BX8 ;;Flash type (AM29F | AM29BX8 | AM29BX16 | I28BX8 | I28BX16)
CHIPSIZE 0x400000 ;;The size of one flash chip in bytes
BUSWIDTH 32 ;The width of the flash memory bus in bits (8 | 16 | 32)
WORKSPACE 0xFA202000 ; RAM buffer for fast flash programming
FILE u-boot.bin ;The file to program
FORMAT BIN 0x00000000
ERASE 0x00000000 BLOCK
ERASE 0x00008000 BLOCK
ERASE 0x00010000 BLOCK
ERASE 0x00018000 BLOCK
[REGS]
DMM1 0xFA200000
FILE reg823.def
② U-Boot移植参考板。这是进行U-Boot移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM的情况,以尽可能相一致为原则,先找出一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。如RPXlite DW板可选择U-Boot源码中RPXlite板作为U-Boot移植参考板。对U-Boot移植新手,建议依照循序渐进的原则,目标板文件名暂时先用移植参考板的名称,在逐步熟悉U-Boot移植基础上,再考虑给目标板重新命名。在实际移植过程中,可用Linux命令查找移植参考板的特定代码,如 grep –r RPXlite ./ 可确定出在U-Boot中与RPXlite板有关的代码,依此对照目标板实际进行屏蔽或修改。同时应不局限于移植参考板中的代码,要广泛借鉴U-Boot 中已有的代码更好地实现一些具体的功能。
③ U-Boot烧写地址。不同目标板,对U-Boot在FLASH中存放地址要求不尽相同。事实上,这是由处理器中断复位向量来决定的,与主板硬件相关,对 MPC8xx主板来讲,就是由硬件配置字(HRCW)决定的。也就是说,U-Boot烧写具体位置是由硬件决定的,而不是程序设计来选择的。程序中相应U -Boot起始地址必须与硬件所确定的硬件复位向量相吻合;如RPXlite DW板的中断复位向量设置为0x00000100。因此, U-Boot 的BIN镜像文件必须烧写到FLASH的起始位置。事实上,大多数的PPC系列的处理器中断复位向量是0x00000100和0xfff00100。这也是一般所说的高位启动和低位启动的BOOT LOADER所在位置。可通过修改U-Boot源码<目标板>.h头文件中CFG_MONITOR_BASE 和board/<目标板>/config.mk中的TEXT_BASE的设置来与硬件配置相对应。
④ CPU寄存器参数设置。根据处理器系列、类型不同,寄存器名称与作用有一定差别。必须根据目标板的实际,进行合理配置。一个较为可行和有效的方法,就是借鉴参考移植板的配置,再根据目标板实际,进行合理修改。这是一个较费功夫和考验耐力的过程,需要仔细对照处理器各寄存器定义、参考设置、目标板实际作出选择并不断测试。MPC8xx处理器较为关键的寄存器设置为SIUMCR、PLPRCR、SCCR、BRx、ORx。
⑤ 串口调试。能从串口输出信息,即使是乱码,也可以说U-Boot移植取得了实质性突破。依据笔者调试经历,串口是否有输出,除了与串口驱动相关外,还与 FLASH相关的寄存器设置有关。因为U-Boot是从FLASH中被引导启动的,如果FLASH设置不正确,U-Boot代码读取和执行就会出现一些问题。因此,还需要就FLASH的相关寄存器设置进行一些参数调试。同时,要注意串口收发芯片相关引脚工作波形。依据笔者调试情况,如果串口无输出或出现乱码,一种可能就是该芯片损坏或工作不正常。
⑥ 与启动 FLASH相关的寄存器BR0、OR0的参数设置。应根据目标板FLASH的数据手册与BR0和OR0的相关位含义进行合理设置。这不仅关系到FLASH能否正常工作,而且与串口调试有直接的关联。
⑦ 关于CPLD电路。目标板上是否有CPLD电路丝毫不会影响U-Boot的移植与嵌入式操作系统的正常运行。事实上,CPLD电路是一个集中将板上电路的一些逻辑关系可编程设置的一种实现方法。其本身所起的作用就是实现一些目标板所需的脉冲信号和电路逻辑,其功能完全可以用一些逻辑电路与CPU口线来实现。
⑧ SDRAM的驱动。串口能输出以后,U-Boot移植是否顺利基本取决于SDRAM的驱动是否正确。与串口调试相比,这部分工作更为核心,难度更大。 MPC8xx目标板SDRAM驱动涉及三部分。一是相关寄存器的设置;二是UPM表;三是SDRAM上电初始化过程。任何一部分有问题,都会影响U- Boot、嵌入式操作系统甚至应用程序的稳定、可靠运行。所以说,SDRAM的驱动不仅关系到U-Boot本身能否正常运行,而且还与后续部分相关,是相当关键的部分。
⑨ 补充功能的添加。在获得一个能工作的U-Boot后,就可以根据目标板和实际开发需要,添加一些其它功能支持。如以太网、LCD、NVRAM等。与串口和 SDRAM调试相比,在已有基础之上,这些功能添加还是较为容易的。大多只是在参考现有源码的基础上,进行一些修改和配置。
另外,如果在自主设计的主板上移植U-Boot,那么除了考虑上述软件因素以外,还需要排查目标板硬件可能存在的问题。如原理设计、PCB布线、元件好坏。在移植过程中,敏锐判断出故障态是硬件还是软件问题,往往是关系到项目进度甚至移植成败的关键,相应难度会增加许多。
完成一个目标板的移植工作后,可考虑将移植结果以补丁的形式发送到U-Boot用户邮件列表,尤其是一些参考板的移植结果。这是使用GPL代码并遵循GPL 条款的体现。可在阅读README相关补丁说明的基础上,添加适当的注释,将自己列入光荣榜(CREDITS)。如果愿意承担所移植板的后续更新工作,可以考虑加入维护人员(MAINTAINERS)开发队伍行列。
诚然,在实际的U-Boot移植中无法避免地会遇到一些难以预料的问题,甚至出现倒退,尤其U-Boot移植新手,更会平添诸多难度。但笔者相信,在逐步熟悉U-Boot移植方法、过程中,随着自身经验的不断积累,加之有众多热衷于开放源码人士的鼎立相助,坚冰终会消融



armlinux的bootloader启动代码分析

Author : balancesli
mail : balancesli@thizlinux.com.cn

前阶段做了一次基于at91rm9200引导部分的技术分析,主要采用了u-boot,这里只面向使用at91rm9200板子的
的朋友做个简单的推敲,希望起到抛砖引玉的作用.

关键词 :
u-boot: 一个开源的面向多个目标平台(ppc, mips, arm, x86)的bootloader.
at91rm9200 : Atmel 公司生产的基于arm9核的Soc处理器.

以下先给出at91rm9200引导流程图

Boot program Flow Diagram

Device Setup
|
|
Boot SPI DataFlash Boot --> Download from DataFlash --> run
|
|
TWI EEPROM Boot --> Download from EEPROM --> run
|
|
Parallel Boot --> Download from 8-bit Device -->
|
| Xmodem protocol
| |---DBGU Serial Download ---------------------> run
|____|
| DFU protocol
|-----USB download -----------------------> run

在这里我主要介绍通过片内引导和片外引导, 片内引导主要采用串口下载并引导u-boot,并完成程序被烧写到Flash上,
然后就可以通过跳线的方式从片外引导执行已经烧写到片外Flash上的引导程序(bootloader).

这里要提及的是at91rm9200内部本身有128k的片内rom,其固化了一个bootloader和uploader, 用来支持程序的
下载和引导,而且其内部固化的程序提供了很多内部服务接口(Internel Service)供我们来使用,例如Xmodem,Tempo
DataFlash, CRC, Sine服务接口,这样我们就可以利用它所提供的Service interface API完成程序的下载。
这里主要介绍Xmodem接口服务。

at91rm9200内部固化的代码在设计上采用了面向对象的设计方法,如下:


typedef struct _AT91S_Service
{
char data;
char (*MainMethod)();
char (*ChildMethod)();
}AT91S_Service, *AT91PS_Service;

char AT91F_MainMethod()
{

}
char AT91F_ChildMethod()
{

}

/*init the Service */
AT91PS_Service AT91F_OpenDevice(AT91PS_Service pService)
{
pService->data = 0;
pService->MainMethod = AT91F_MainMethod;
pService->ChildMethod = AT91F_ChildMethod;
}

//使用方法如下
AT91S_Service service;
AT91PS_Service pService = AT91F_OpenDevice(&service);
pService->AT91F_MainMethmod();
.....

通过如上代码片断可以看出它采用了类似面向对象的设计方法。
其实如果各位朋友接触过的话或者看过这本书的话,应该很容易便接受它。
下面以Xmodem服务为例子介绍:

at91rm9200内部提供的服务包含了几个服务对象, 这些对象在片内启动xmodem协议Host端和Targe端通讯时会用到.

typedef struct _AT91S_RomBoot
{
const unsigned int version;
// Peripheral descriptors
const AT91S_MEMCDesc MEMC_DESC;
const AT91S_STDesc SYSTIMER_DESC;
const AT91S_Pio2Desc PIOA_DESC;
const AT91S_Pio2Desc PIOB_DESC;
const AT91S_USART2Desc DBGU_DESC;
const AT91S_USART2Desc USART0_DESC;
const AT91S_USART2Desc USART1_DESC;
const AT91S_USART2Desc USART2_DESC;
const AT91S_USART2Desc USART3_DESC;
const AT91S_TWIDesc TWI_DESC;
const AT91S_SPIDesc SPI_DESC;

// Objects entry
const AT91PF_OpenPipe OpenPipe;
const AT91PF_OpenSBuffer OpenSBuffer;
const AT91PF_OpenSvcUdp OpenSvcUdp;
const AT91PF_OpenSvcXmodem OpenSvcXmodem;
const AT91PF_OpenCtlTempo OpenCtlTempo;
const AT91PF_OpenDfuDesc OpenDfuDesc;
const AT91PF_OpenUsbDesc OpenUsbDesc;
const AT91PF_OpenSvcDataFlash OpenSvcDataFlash;
const AT91PF_SVC_CRC16 CRC16;
const AT91PF_SVC_CRCCCITT CRCCCITT;
const AT91PF_SVC_CRCHDLC CRCHDLC;
const AT91PF_SVC_CRC32 CRC32;
// Array
const AT91PS_SVC_CRC_BIT_REV Bit_Reverse_Array;
const AT91PS_SINE_TAB SineTab;
const AT91PF_Sinus Sine;
} AT91S_RomBoot;

//AT91S_Pipe
typedef struct _AT91S_Pipe
{
// A pipe is linked with a peripheral and a buffer
AT91PS_SvcComm pSvcComm;
AT91PS_Buffer pBuffer;

// Callback functions with their arguments
void (*WriteCallback) (AT91S_PipeStatus, void *);
void (*ReadCallback) (AT91S_PipeStatus, void *);
void *pPrivateReadData;
void *pPrivateWriteData;

// Pipe methods
AT91S_PipeStatus (*Write) (
struct _AT91S_Pipe *pPipe,
char const * pData,
unsigned int size,
void (*callback) (AT91S_PipeStatus, void *),
void *privateData
);

AT91S_PipeStatus (*Read) (
struct _AT91S_Pipe *pPipe,
char *pData,
unsigned int size,
void (*callback) (AT91S_PipeStatus, void *),
void *privateData
);

AT91S_PipeStatus (*AbortWrite)(struct _AT91S_Pipe *pPipe);
AT91S_PipeStatus (*AbortRead)(struct _AT91S_Pipe *pPipe);
AT91S_PipeStatus (*AbortRead)(struct _AT91S_Pipe *pPipe);
AT91S_PipeStatus (*Reset)(struct _AT91S_Pipe *pPipe);
char (*IsWritten)(struct _AT91S_Pipe *pPipe, char const *pVoid);
char (*IsReceived) (struct _AT91S_Pipe *pPipe, char const *pVoid);
} AT91S_Pipe;

//AT91S_Buff
typedef struct _AT91S_SBuffer
{
AT91S_Buffer parent;
char *pRdBuffer;
char const *pWrBuffer;
unsigned int szRdBuffer;
unsigned int szWrBuffer;
unsigned int stRdBuffer;
unsigned int stWrBuffer;
} AT91S_SBuffer;

// AT91S_SvcTempo
typedef struct _AT91S_SvcTempo
{

// Methods:
AT91S_TempoStatus (*Start) (
struct _AT91S_SvcTempo *pSvc,
unsigned int timeout,
unsigned int reload,
void (*callback) (AT91S_TempoStatus, void *),
void *pData);
AT91S_TempoStatus (*Stop) (struct _AT91S_SvcTempo *pSvc);

struct _AT91S_SvcTempo *pPreviousTempo;
struct _AT91S_SvcTempo *pNextTempo;

// Data
unsigned int TickTempo; //* timeout value
unsigned int ReloadTempo;//* Reload value for periodic execution
void (*TempoCallback)(AT91S_TempoStatus, void *);
void *pPrivateData;
AT91E_SvcTempo flag;
} AT91S_SvcTempo;

// AT91S_CtrlTempo
typedef struct _AT91S_CtlTempo
{
// Members:

// Start and stop for Timer hardware
AT91S_TempoStatus (*CtlTempoStart) (void *pTimer);
AT91S_TempoStatus (*CtlTempoStop) (void *pTimer);

// Start and stop for Tempo service
AT91S_TempoStatus (*SvcTempoStart) (
struct _AT91S_SvcTempo *pSvc,
unsigned int timeout,
unsigned int reload,
void (*callback) (AT91S_TempoStatus, void *),
void *pData);
AT91S_TempoStatus (*SvcTempoStop) (struct _AT91S_SvcTempo *pSvc);
AT91S_TempoStatus (*CtlTempoSetTime)(struct _AT91S_CtlTempo *pCtrl, unsigned int NewTime);
AT91S_TempoStatus (*CtlTempoGetTime)(struct _AT91S_CtlTempo *pCtrl);
AT91S_TempoStatus (*CtlTempoIsStart)(struct _AT91S_CtlTempo *pCtrl);
AT91S_TempoStatus (*CtlTempoCreate) (struct _AT91S_CtlTempo *pCtrl,struct _AT91S_SvcTempo *pTempo);
AT91S_TempoStatus (*CtlTempoRemove) (struct _AT91S_CtlTempo *pCtrl,struct _AT91S_SvcTempo *pTempo);
AT91S_TempoStatus (*CtlTempoTick) (struct _AT91S_CtlTempo *pCtrl);

// Data:

void *pPrivateData; // Pointer to devived class
void const *pTimer; // hardware
AT91PS_SvcTempo pFirstTempo;
AT91PS_SvcTempo pNewTempo;
} AT91S_CtlTempo;



//以下代码是上面几个对象的使用范例,通过这样就可以完成Host端和Targe端之间的xmodem通讯,并可以下载代码了。

AT91S_RomBoot const *pAT91;
AT91S_SBuffer sXmBuffer;
AT91S_SvcXmodem svcXmodem;
AT91S_Pipe xmodemPipe;
AT91S_CtlTempo ctlTempo;

AT91PS_Buffer pXmBuffer;
AT91PS_SvcComm pSvcXmodem;
unsigned int SizeDownloaded;

/* Init of ROM services structure */
pAT91 = AT91C_ROM_BOOT_ADDRESS;//这里取得内部ROM服务的入口地址

/* Tempo Initialization */
pAT91->OpenCtlTempo(&ctlTempo, (void *) &(pAT91->SYSTIMER_DESC));
ctlTempo.CtlTempoStart((void *) &(pAT91->SYSTIMER_DESC));

/* Xmodem Initialization */
pXmBuffer = pAT91->OpenSBuffer(&sXmBuffer);
pSvcXmodem = pAT91->OpenSvcXmodem(&svcXmodem, (AT91PS_USART)AT91C_BASE_DBGU, &ctlTempo);
pAT91->OpenPipe(&xmodemPipe, pSvcXmodem, pXmBuffer);
xmodemPipe.Read(&xmodemPipe, (char *)AT91C_UBOOT_BASE_ADDRESS, AT91C_UBOOT_MAXSIZE,
AT91F_XmodemProtocol, 0);
while(XmodemComplete !=1);



//上面部分主要针对at91rm9200片内启动时我们可以使用的片内接口服务介绍,玩H9200的朋友可以参考一下便知道缘由。

下面主要介绍at91rm9200片外启动时所使用的bootloader-->u-boot.

一. bootloader
BootLoader(引导装载程序)是嵌入式系统软件开发的非常重要的环节,它把操作系统和硬件平台衔接在一起,
是跟硬件体系密切相关的。



1.1 典型的嵌入式系统软件部分Image memory layout : bootloader , bootloader param, kernel, rootfs.

1.2 引导模式 : 1. bootstrap或download
2. autoboot
1.3 u-boot简介 :
u-boot是由Wolfgang Denk开发,它支持(mips, ppc, arm, x86)等目标体系,
可以在http://sourceforge.net 上下载获得源码,

1.4 u-boot源代码目录结构

board:开发板相关的源码,不同的板子对应一个子目录,内部放着主板相关代码。

at91rm9200dk/at91rm9200.c, config.mk, Makefile, flash.c ,u-boot.lds等都和具体开发板的硬件和地址分配有关。

common:与体系结构无关的代码文件,实现了u-boot所有命令,
其中内置了一个shell脚本解释器(hush.c, a prototype Bourne shell grammar parser), busybox中也使用了它.

cpu:与cpu相关代码文件,其中的所有子目录都是以u-boot所支持的cpu命名.

at91rm9200/at45.c, at91rm9200_ether.c, cpu.c, interrupts.c serial.c, start.S, config.mk, Makefile等.
其中cpu.c负责初始化CPU、设置指令Cache和数据Cache等;

interrupt.c负责设置系统的各种中断和异常,比如快速中断、开关中断、时钟中断、软件中断、
预取中止和未定义指令等;

start.S负责u-boot启动时执行的第一个文件,它主要是设置系统堆栈和工作方式,为跳转到C程序入口点.

disk:设备分区处理代码。

doc:u-boot相关文档。


drivers:u-boot所支持的设备驱动代码, 网卡、支持CFI的Flash、串口和USB总线等。

fs: u-boot所支持支持文件系统访问存取代码, 如jffs2.

include:u-boot head文件,主要是与各种硬件平台相关的头文件,
如include/asm-arm/arch-at91rm9200/, include/asm-arm/proc-armv

net:与网络有关的代码,BOOTP协议、TFTP协议、RARP协议代码实现.

lib_arm:与arm体系相关的代码。(这里我们主要面向的是ARM体系,所以该目录是我们主要研究对象)

tools:编译后会生成mkimage工具,用来对生成的raw bin文件加入u-boot特定的image_header.

1.5 u-boot的功能介绍

 u-boot支持SCC/FEC以太网、OOTP/TFTP引导、IP和MAC的功能.
读写Flash、DOC、IDE、IIC、EEROM、RTC
支持串行口kermit和S-record下载代码, 并直接从串口下载并执行。

在我们生成的内核镜像时,要做如下处理.
1. arm-linux-objcopy -O binary -R.note -R.comment -S vmlinux linux.bin
2. gzip -9 linux.bin
3. mkimage -A arm -O linux -T kernel -C gzip -a 0xc0008000 -e 0xc0008000 -n
"Linux-2.4.19-rmk7” -d linux.bin.gz uImage

即在Linux内核镜像vmLinux前添加了一个特殊的头,这个头在include/image.h中定义,
typedef struct image_header
{
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;

当u-boot引导时会对这个文件头进行CRC校验,如果正确,才会跳到内核执行.

如果u-boot启动以后会出现
u-boot>
敲入help, 会出现大量的命令提示,Monitor command
go - start application at address 'addr'
run - run commands in an environment variable
bootm - boot application image from memory
bootp - boot image via network using BootP/TFTP protocol
tftpboot- boot image via network using TFTP protocol
and env variables "ipaddr" and "serverip"
(and eventually "gatewayip")
rarpboot- boot image via network using RARP/TFTP protocol
diskboot- boot from IDE devicebootd - boot default, i.e., run 'bootcmd'
loads - load S-Record file over serial line
loadb - load binary file over serial line (kermit mode)
md - memory display
mm - memory modify (auto-incrementing)
nm - memory modify (constant address)
mw - memory write (fill)
cp - memory copy
cmp - memory compare
crc32 - checksum calculation
imd - i2c memory display
imm - i2c memory modify (auto-incrementing)
inm - i2c memory modify (constant address)
imw - i2c memory write (fill)
icrc32 - i2c checksum calculation
iprobe - probe to discover valid I2C chip addresses
iloop - infinite loop on address range
isdram - print SDRAM configuration information
sspi - SPI utility commands
base - print or set address offset
printenv- print environment variables
setenv - set environment variables
saveenv - save environment variables to persistent storage
protect - enable or disable FLASH write protection
erase - erase FLASH memory
flinfo - print FLASH memory information
bdinfo - print Board Info structure
iminfo - print header information for application image
coninfo - print console devices and informations
ide - IDE sub-system
loop - infinite loop on address range
mtest - simple RAM test
icache - enable or disable instruction cache
dcache - enable or disable data cache
reset - Perform RESET of the CPU
echo - echo args to console
version - print monitor version
help - print online help
? - alias for 'help'

u-boot支持大量的命令可用, 这里就不作介绍,大家有兴趣可以看看u-boot 的README文档
3.3 对u-boot-1.0.0的修改和移植

1.6 关于u-boot的移植如下,由于u-boot的软件设计体系非常清晰,它的移植工作并不复杂,
相信各位的代码阅读功力不错的话,参照如下就可以完成。

If the system board that you have is not listed, then you will need
to port U-Boot to your hardware platform. To do this, follow these
steps:

1. Add a new configuration option for your board to the toplevel
"Makefile" and to the "MAKEALL" script, using the existing
entries as examples. Note that here and at many other places
boards and other names are listed in alphabetical sort order. Please
keep this order.

2. Create a new directory to hold your board specific code. Add any
files you need. In your board directory, you will need at least
the "Makefile", a ".c", "flash.c" and "u-boot.lds".

3. Create a new configuration file "include/configs/.h" for
your board

4. If you're porting U-Boot to a new CPU, then also create a new
directory to hold your CPU specific code. Add any files you need.

5. Run "make _config" with your new name.

6. Type "make", and you should get a working "u-boot.srec" file

7. Debug and solve any problems that might arise.
[Of course, this last step is much harder than it sounds.]

为了使u-boot-1.0.0支持新的开发板,一种简便的做法是在u-boot已经支持的开发板中参考选择一种较接近板的进行修改,
幸运的是在u-boot-1.0.0中已经有了at91rm9200的支持。

1.7 与at91rm9200相关的u-boot代码

在include/configs/at91rm9200dk.h 它包括开发板的CPU、系统时钟、RAM、Flash系统及其它相关的配置信息。
在include/asm-arm/AT91RM9200.h, 该文件描述了H9200寄存器的结构及若干宏定义。
具体内容要参考相关处理器手册。
在cpu/at91rm9200/目录下别为cpu.c、interrupts.c和serial.c等文件.
在board/at91rm9200dk/目录下分别为flash.c、at91rm9200dk.c, config.mk, Makefile,u-boot.lds

flash.c : u-boot读、写和删除Flash设备的源代码文件。由于不同开发板中Flash存储器的种类各不相同,
所以,修改flash.c时需参考相应的Flash芯片手册。它包括如下几个函数:
unsigned long flash_init (void ),Flash初始化;
void flash_print_info (flash_info_t *info),打印Flash信息;
int flash_erase (flash_info_t *info, int s_first, int s_last),Flash擦除;
volatile static int write_dword (flash_info_t *info, ulong dest, ulong data),Flash写入;
int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt),从内存复制数据。

u-boot.lds :linker scripte, 设置u-boot中各个目标文件的连接地址.

网口设备控制程序

  在drivers/目录中网口设备控制程序cs8900, bcm570x等, 还可以添加其他网卡驱动
int eth_init (bd_t *bd) : 初始化网络设备;
void eth_halt (void) : 关闭网络设备;
int eth_send (volatile void *packet,int len) : 发送数据包;
int eth_rx (void) : 接收数据包。

Makefile

  在u-boot-1.0.0/Makefile中
at91rm9200dk_config : unconfig
./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk

1.8 编译u-boot

  make at91rm9200_config
Configuring for at91rm9200 board...
make all
生成三个文件:u-boot.bin, u-boot, u-boot.srec

u-boot.bin is a raw binary image
u-boot is an image in ELF binary format
u-boot.srec is in Motorola S-Record format (objcopy -O srec -R.note -R.comment -S [inputfile] [outfile]


以上工作完成我们可以通过串口将u-boot.bin下载到主板的SDRAM中,它会自动执行, 并出现uboot>
这里我们可以通过串口把boot.bin, u-boot.bin.gz下载到主板,再用u-boot的提供的写flash功能分别
把boot.bin, u-boot.bin.gz写入到flash中,完成以上工作后,对主板跳线选择片外启动,
板子复位后会自动启动u-boot.




二.loader.bin, boot.bin, u-boot.bin代码执行流分析.

以上三个文件时at91rm9200启动所需要的三个bin,他们的实现代码并不难。
如果是你是采用at91rm9200的评估版,应该能得到其源码。

2.1 loader.bin 执行流程,这个文件主要在片内启动从串口下载代码时会用到
loader/entry.S init cpu
b main ---> crt0.S
--> copydata --> clearbss --> b boot
main.c --> boot -->
/*Get internel rom service address*/
/* Init of ROM services structure */
pAT91 = AT91C_ROM_BOOT_ADDRESS;

/* Xmodem Initialization */
--> pAT91->OpenSBuffer
--> pAT91->OpenSvcXmodem
/* System Timer initialization */
---> AT91F_AIC_ConfigureIt
/* Enable ST interrupt */
AT91F_AIC_EnableIt
AT91F_DBGU_Printk("XMODEM: Download U-BOOT ");

Jump.S
// Jump to Uboot BaseAddr exec
Jump((unsigned int)AT91C_UBOOT_BASE_ADDRESS)

2.2 boot.bin执行流程 该文件会在从片内启动时被下载到板子上,以后还会被烧写到片外Flash中,以便在片外启动时
用它来引导并解压u-boot.gz,并跳转到u-boot来执行。
boot/entry.S
b main --> crt0.S --> copydata --> clearbss --> b boot

T91F_DBGU_Printk(" ");
AT91F_DBGU_Printk("************************************** ");
AT91F_DBGU_Printk("** Welcome to at91rm9200 ** ");
AT91F_DBGU_Printk("************************************** ");

boot/misc.s /* unzip uboot.bin.gz */
----> decompress_image(SRC,DST,LEN) --> gunzip

//jump to ubootBaseAddr exec 这里跳转到解压u-boot.gz的地址处直接开始执行u-boot
asm("mov pc,%0" : : "r" (DST));

2.3 uboot.bin执行流程
u-boot/cpu/at91rm9200/start.S
start --->reset
---> copyex ---> cpu_init_crit
---> /* set up the stack */ --> start_armboot
u-boot/lib_arm/board.c

init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
checkboard,
NULL,
};

---> start_armboot ---> call init_sequence
---> flash_init --> display_flash_config
---> nand_init ---> AT91F_DataflashInit
---> dataflash_print_info --> env_relocate
---> drv_vfd_init --> devices_init --> jumptable_init
---> console_init_r --> misc_init_r --> enable_interrupts
---> cs8900_get_enetaddr --> board_post_init -->

u-boot/common/main.c
for (;;)
{ /* shell parser */
main_loop () --> u_boot_hush_start --> readline
--> abortboot
-->printf("Hit any key to stop autoboot: %2d ", bootdelay);
}

以上是at91rm9200启动并进入u-boot的执行流分析。后面u-boot还会将uImage解压到特定的位置并开始执行内核代码。

三. 综述
总之, 不同厂商的出的Soc片子在启动方式大都提供片内和片外启动两种方式,一般都是在片内固化一段小程序
方便于程序开发而已,在其DataSheet文档中有详尽的描述。若是对at92rm9200有兴趣或玩过的朋友,可以与我共同探讨相互学习。