Coder
分类: 嵌入式
2010-02-07 21:52:32
一. 简介
应用程序 |
文件系统 |
操作系统内核 |
BootLoader |
简单的说bootloader是一段程序,它的作用就是加载操作系统,BootLoader(引导加载程序)是系统加电后运行的第一段软件代码。通过这段代码实现硬件的初始化,建立内存空间的映射图,为操作系统内核准备好硬件环境并引导内核的启动。如上图所示的那样在设备的启动过程中bootloader位于最底层,首先被运行来引导操作系统运行,很容易可以看出bootloader是底层程序,所以它的实现严重地依赖于硬件,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的BootLoader几乎是不可能的。尽管如此,一些功能强大、支持硬件环境较多的BootLoader也被广大的使用者和爱好者所支持,从而形成了一些被广泛认可的、较为通用的的bootloader实现。简单的介绍几种:
1.U-BOOT
uboot是一个庞大的开源软件项目。他支持一些系列的体系架构和处理器,包含常见的外设的驱动,是一个功能非常强大的板极支持包。其代码可以从ftp://ftp.denx.de/pub/u-boot/下载。U-BOOT是由 PPCBOOT发展起来的,是PowerPC、ARM9、Xscale、X86等系统通用的Boot方案,从官方版本
2.vivi
vivi是韩国mizi 公司开发的bootloader, 适用于ARM9系列处理器。 Vivi有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这是vivi的默认模式。如果修改或更新需要进入下载模式,在下载模式下,vivi为用户提供一个命令行接口通过接口可以使用vivi提供的一些命令,来实现flash的烧写、管理、操作mtd分区信息、启动系统等功能。
其它还有一些bootloader实现如下表所示:
名称 |
说明 |
支持的架构 |
LILO |
Linux的磁盘引导加载程序 |
x86 |
GRUB |
LILO的GNU版本 |
x86 |
Loadlin |
从DOS引导Linux |
x86 |
RedBoot |
以eCos为基础的引导程序 |
x86 、ARM、PowerPC、MIPS等 |
ROLO |
从ROM引导Linux,且不需要BIOS |
x86 |
Etherboot |
从以太网卡启动Linux系统的固件 |
x86 |
LinuxBIOS |
以Linux为基础的BIOS的替代品 |
x86 |
blob |
来自LART计划的引导程序 |
ARM |
由于u-boot的通用性好,功能全面,适合初学者学习和使用,我们选用u-boot作为基准代码,在此基础上进行修改,完成移植工作。
二.移植准备
1.目标板:
这是进行U-Boot移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM的情况,以尽可能相一致为原则,先找出 一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。我们使用友善之臂的老板本mini2440来作实验
一些重要参数如下:
CPU处理器
– Samsung S
SDRAM内存:两片现代的HY57V561620FTP-H
– 板载64MB SDRAM
– 32bit数据总线
– SDRAM时钟频率高达100MHz
Flash存储器
– 板载64MB Nand Flash,掉电非易失:Samsung的K
– 板载2MB Nor Flash:SST39VF1601
网卡
– DM9000EP
2.源文件:
选择一标准的u-boot代码,当然要选最新版本的了:u-boot-2009.11.1
3.烧写工具:
u-boot的烧写使用JTAG线进行下载,用J-Flash ARM软件进行烧写,使用SecureCRT终端进行串口调试。用串口线相连。
4.知识储备:
u-boot的目录结构:
目录 |
说明 |
board |
和一些已有开发板有关的文件,比如makefile和U-Boot.ldS等都和具体开发板的硬件和地址分配有关。 |
common |
与体系结构无关的文件,实现各种命令的C文件 |
cpu |
CPU相关文件,其中的子目录都是以U-Boot所支持的CPU为名,比如子目录arm926ejS、mips等,每个特定的子目录都包括cpu.c和interrupt.c,start.s。其中cpu.c初始化CPU、设置指令cache和数据cache等。Interruput.c设置系统的各种中断和异常;start.s是U-Boot自动执行时的第一个文件,它主要是设置系统堆栈和工作方式,为进入C程序奠定基础。 |
disk |
Disk驱动的分区处理代码 |
doc |
文档 |
drivers |
通用设备驱动程序,例如各种网卡、支持CFI的Flash、串口、USB等 |
fs |
支持文件系统的文件,目前支持cramfs、fat、fdos、jffs2和registerfs |
include |
头文件,还有对各种硬件平台支持的汇编文件,系统配置文件等 |
net |
与网络有关的代码,BOOTP协议、TFTP协议、RARP和NFS等 |
lib_arm |
与ARM体系结构相关的代码 |
tools |
创建S-Record格式文件和U-Boot images的工具 |
u-boot代码:
由于代码比较庞大,只简单分析启动部分。网络和很多书中有非常详细的分析,如果想详细了解查阅相关资料,或着提出讨论,还可以阅读源代码自身和它附带的一些文档。
U-Boot启动过程可以分成两个阶段(stage)
下面是u-boot启动过程的流程图其中左右两部分分别是启动过程的两个阶段
第一阶段(stage 1)是依赖于CPU体系结构的代码(如设备初始化代码等),一般用汇编语言来实现。主要进行以下方面的设置:设置ARM进入SVC模式、禁止IRQ和FIQ、关闭看门狗、屏蔽所有中断。设置时钟(FCLK,HCLK,PCLK)的分频比(实际上设置时钟,也即设置MPLLCON等也可以在第二阶段的board/$(board)/$(boadr).c文件的board_init函数中进行)、清空I/D cache、清空TLB、禁止MMU和cache、配置内存控制器、为搬运代码做准备、搬移uboot映像到RAM中(使用copy_loop实现)、分配堆栈、清空bss段(使用clbss_l实现)。
第二阶段(stage 2)通常用C语言来实现。
start_armboot():
一系列初始化(cpu, 板卡,中断,串口,控制台等),开启I/D cache。初始化FLASH,根据系统配置执行其他初始化操作。打印LOG,使能中断,获取环境变量,初始化网卡。最后进入main_loop()函数。在main_loop函数中会检查bootdelay和bootcmd环境变量,如果bootcmd已经设置过,则在等待bootdelay个毫秒后会自动执行bootcmd。如果等待过程中被用户中断(ctl+c)或者bootcmd没有设置,则会等待用户输入命令。
关键点一: U-Boot移植参考板
这是进行U-Boot移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM的情况,以尽可能相一致为原则,先找出 一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。对U-Boot移植新手,建议依照循序渐进的原则,目标板文件名暂时先用移 植参考板的名称,在逐步熟悉U-Boot移植基础上,再考虑给目标板重新命名。在实际移植过程中,可用Linux命令查找移植参考板的特定代码,如 grep –r 2410 ./ 可确定出在U-Boot中与smdk2410板有关的代码,依此对照目标板实际进行屏蔽或修改。同时应不局限于移植参考板中的代码,要广泛借鉴U-Boot 中已有的代码更好地实现一些具体的功能。
关键点二: U-Boot烧写地址和CPU寄存器参数设置
不同目标板,对U-Boot在FLASH中存放地址要求不尽相同。事实上,这是由处理器中断复位向量来决定的,与主板硬件相关 。也就是说,U-Boot烧写具体位置是由硬件决定的,而不是程序设计来选择的。
根据CPU处理器系列、类型不同,寄存器名称与作用有一定差别。必须根据目标板的实际,进行合理配置。一个较为可行和有效的方法,就是借鉴参考移植板的配置,再根据目标板实际,进行合理修改。这是一个较费时间和考验耐力的过程,需要仔细对照处理器各寄存器定义、参考设置、目标板实际作出选择并不断测试。
关键点三:串口调试。
能从串口输出信息,即使是乱码,也可以说U-Boot移植取得了实质性突破。 依据笔者调试经历,串口是否有输出,除了与串口驱动相关外,还与FLASH相关的寄存器设置有关。因为U-Boot是从FLASH中被引导启动的,如果 FLASH设置不正确,U-Boot代码读取和执行就会出现一些问题。因此,还需要就FLASH的相关寄存器设置进行一些参数调试。同时,要注意串口收发芯片相关引脚工作波形。依据笔者调试情况,如果串口无输出,有一种可能就是该芯片损坏或工作不正常。 如果出现乱码,有一种可能就是波特率、系统时钟等参数设置有问题。
三.修改源代码:
移 植U-Boot应该算是一个说难不是很难,说简单也不是特别简单的事吧。为了便于我们更好的对U-Boot进行研究,也为了回头出问题的时候,调试工作能 变得简单些而不至于千头万绪完全摸不着北,我们采用分成多个步骤来进行移植。首先先弄出一个能在板子上跑起来,能运行基本命令的U-Boot,然后再对驱 动等进行移植、添加命令来实现更复杂的功能。
我们刚开始学做u-boot移植,综合别人的方法,总结归纳如下:
1. 先不去考虑nor/nand flash启动,先让u-boot在SDARM中成功运行。
2. 把那个既经典又简单又very useful的调试方法用上,那就是在程序中需要的地方加上led灯指示。(有时串口没信息,一头雾水,连自己的程序是否在运行都不知道,这事在程序开头加led的代码,最合适不过了)
3. u-boot能在SDRAM中运行后,先考虑nor flash中运行(如果有nor flash的话),因为U-Boot是支持NorFlash的,它已经有了U-Boot的重定位的代码了,故支持nor flash比支持nand flash改动少,这样也就会较容易一些。SMDK2410里也是支持nor flash的,那里用的是AMD公司的。
4. 能在flash 中启动后,增加nand支持,看是否能检测到nand,并在u-boot中用些nand的命令验证驱动是否有问题。
5. 增加代码,让u-boot从nand启动。
6. u-boot可从nand启动后,增加代码,让代码能自动识别跳线的设置,从相应的flash启动。
以上6步完成之后,关于启动方面的就完成了,如果有需要,再增加这个u-boot的其它功能,比如:网络支持、USB支持等,这些部分如果自已的开发板和SMDK2410不同的话,也是要做移植的。
第一步、先让U-Boot在我们的板子上跑起来
1. 添加新开发板信息
(1)顶层Makefile:为了能让u-boot在编译之前根据此规则来获得具体的配置文件和编译规则。
在smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410
samsung s
之后,添加以下内容:
mini2440_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t
mini2440 samsung s
添加交叉编译器路径(根据个人情况,如果交叉编译的工具名称前缀正好为arm-linux-,且交叉工具链所在目录也在环境变量PATH中,则无需改变,仍为原来的值):
ifeq ($(ARCH),arm)
CROSS_COMPILE=arm-linux-
Endif
关于这些更改的意思可以参考Makefile的语法,顶层目录的Makefile文件和顶层目录的mkconfig脚本文件。
(2)建立新开发板目录:可以把类似的开发板进行修改
cd board/samsung/
cp -r smdk2410/
mini2440/
cd mini2440/
mv smdk2410.c mini2440.c
(3)修改mini2440目录下的Makefile
将原来的
COBJS
:= smdk2410.o flash.o
修改为
COBJS
:= mini2440.o flash.o
其实也就是使得Makefile的内容能和源代码的文件名相匹配。
(4)获得配置文件
cp include/configs/smdk2410.h include/configs/mini2440.h
2、修改cpu/arm920t/start.S文件
这个文件是U-Boot执行的第一个文件。主要需要注意的几个点为:
(1)、S
(2)、要在这个文件中完成整个时钟部分的初始化,而不是仅仅设置一个CLKDIVN寄存器。
代码为:
#if defined(CONFIG_S
/*
turn off the watchdog */
# if defined(CONFIG_S
#
define pWTCON 0x15300000
#
define INTMSK 0x14400008 /* Interupt-Controller base addresses */
#
define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define
pWTCON 0x53000000
#
define INTMSK 0x
#
define INTSUBMSK 0x
/* register of the clock and power management
*/
#
define LOCKTIME 0x
# define
MPLLCON 0x
#
define UPLLCON 0x
#
defien CLKCON 0x
#
define CLKDIVN 0x
#
define CAMDIVN 0x
#
define MDIV_405 (0x
#
define PDIV_405 (0x02
<< 4)
#
define SDIV_405 (0x01
<< 0)
#
define MDIV_96 (0x38
<< 12)
#
define PDIV_96 (0x02
<< 4)
#
define SDIV_96 (0x01
<< 0)
# endif
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the
INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* Initialize the clock & power
management */
ldr r0, =LOCKTIME
mov r1, #0xffffffff
str r1, [r0]
ldr r0, =CAMDIVN
mov r1, #0
str r1, [r0]
/*
FCLK:HCLK:PCLK = 1:4:8 */
/*
default FCLK is 405 MHz ! */
ldr r0, =CLKDIVN
mov r1, #0xd
str r1, [r0]
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xC0000000
mcr p15,0,r0,c1,c0,0
ldr r0,=UPLLCON
mov r1,#MDIV_96
add r1,r1,#PDIV_96
add r1,r1,#SDIV_96
str r1,[r0]
nop
nop
nop
nop
nop
nop
nop
nop
ldr r0,=MPLLCON
mov r1,#MDIV_405
add r1,r1,#PDIV_405
add r1,r1,#SDIV_405
str r1,[r0]
#endif /*
CONFIG_S
注:关于时钟部分的设置,手册中说的已经相当明白了。如果CLKDIVN设置的HDIVN不为,那么需要将CPU设置为异步总线模式,也就是紧跟在设置CLKDIVN的代码后面的3行代码。如果要同时设置MPLL和UPLL,则需注意它们的先后顺序,MPLL和UPLL的设定是有前后顺序的,手册中说明了,若UPLLCON和MPLLCON要同时设置则必须先设置UPLLCON,而后设置MPLLCON,同时两者之间要至少插入7个NOP。关于PLLCON的值,手册中也根据输入时钟,有一组建议值,按照手册建议的值设置即可。
3、修改lowlevel_init.S文件
这个文件主要用来初始化内存控制器
#define B1_BWSCON
(DW32) 修改为 #define B1_BWSCON
(DW16)
#define B4_BWSCON
(DW16) 修改为 #define B4_BWSCON
(DW16 + WAIT + UBLB)
#define REFCNT
1113
/* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
修改为
#define REFCNT 0x
4、修改board/samsung/mini2440/mini2440.c
追随着U-Boot的执行流,进入到lib_arm/board.c文件的start_armboot函数中,这个函数在初始化几个全局数据结构之后,它便会开始执行一组初始化函数。首先执行的便是board/samsung/mini2440/mini2440.c中的board_init函数,这个函数主要完成初始化时钟和初始化GPIO的功能,同时也设置机器代码和内核参数存放的地址。(初始化时钟的部分在这里再做一次,似乎有点重复作业了)。
S
【1】MPLL, 用于产生FCLK, HCLK, PCLK三种频率,这三种频率分别有不同的用途:
FCLK是为CPU提供的时钟信号。
HCLK是为AHB总线提供的时钟信号, Advanced High-performance Bus,主要用于高速外设,比如内存控制器,中断控制器,LCD控制器,DMA等。
从S
PCLK是为APB总线提供的时钟信号,Advanced Peripherals Bus,主要用于低速外设,比如看门狗,UART控制器,IIS,I
【2】UPLL,专门用于驱动USB host/Device等设备。并且驱动USB host/Device的频率必须为48MHz。
定义MPLL/UPLL/CLKDIV,参考S
我们的板子设置CPU频率为405MHZ,板子的晶振输入为12MHZ。分频比设置为1:4:8。因为S
struct s
S
S
S
S
S
S
#if defined(CONFIG_S
S
#endif
};
......(S
根据手册说明:
增加宏定义:
#define S
#define S
#define S
修改board_init函数如下:
int board_init (void)
{
S
S
/* FCLK:HCLK:PCLK = 1:4:8 */
clk_power->CLKDIVN = S
/* change to asynchronous bus mod */
__asm__( "mrc p15, 0, r1, c1, c0, 0\n" /* read ctrl register */
"orr r1, r1, #0xc0000000\n" /* Asynchronous */
"mcr p15, 0, r1, c1, c0, 0\n" /* write ctrl register */
:::"r1"
);
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
/* configure MPLL */
clk_power->MPLLCON = S
/* some delay between MPLL and UPLL */
delay (4000);
/* configure UPLL */
clk_power->UPLLCON = S
/* some delay between MPLL and UPLL */
delay (8000);
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00044555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA;
gpio->GPGUP = 0x0000FFFF;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
gpio->GPJCON=0x02AAAAAA;
gpio->GPJUP=00001FFF;
/* arch number of MINI2440-Board */
gd->bd->bi_arch_number = MACH_TYPE_MINI2440;
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
dcache_enable();
return 0;
}
5、修改cpu/arm920t/s
关于串口的初始化和设置,需要获得系统工作频率的正确信息,故接下来要让获得系统工作频率的一组函数能够正常工作。
【1】在#define MPLL 0
#define UPLL 1上面增加
DECLARE_GLOBAL_DATA_PTR;
(为了使用全局的数据结构gd)。
【2】S
return
(CONFIG_SYS_CLK_FREQ * m) / (p << s);
更改为
if (pllreg == MPLL)
return
(CONFIG_SYS_CLK_FREQ * m * 2) / (p << s);
else
return
(CONFIG_SYS_CLK_FREQ * m) / (p << s);
【3】增加宏定义
/* for s
#define S
#define S
#define S
#define S
#define S
#define S
#define S
#define S
#define S
#define S
#define S
#define S
【4】get_HCLK get_PCLK函数修改
ulong get_HCLK(void)
{
S
unsigned long clkdiv;
unsigned long camdiv;
int hdiv = 1;
clkdiv = clk_power->CLKDIVN;
camdiv = clk_power->CAMDIVN;
/* work out clock scalings */
switch (clkdiv & S
case S
hdiv = 1;
break;
case S
hdiv = 2;
break;
case S
hdiv = (camdiv & S
break;
case S
hdiv = (camdiv & S
break;
}
return get_FCLK() / hdiv;
}
/* return PCLK frequency */
ulong get_PCLK(void)
{
S
unsigned long clkdiv;
return get_HCLK()
/ ((clkdiv & S
}
6、以下文件主要加入CONFIG_S
inlcude/s
drivers/srial/srial_s
cpu/arm920t/s
cpu/arm920t/s
cpu/arm920t/s
cpu/arm920t/s
cpu/arm920t/s
drivers/rtc/s
7、头文件修改 include/configs/mini2440.h
增加宏定义
#define CONFIG_S
#define CONFIG_MINI2440 1
#define CONFIG_SKIP_LOWLEVEL_INIT 1
#define CONFIG_SKIP_RELOCATE_UBOOT 1
同时注掉
#define CONFIG_S
8、修改文件board/Samsung/mini2440/config.mk
改变TEXT_BASE的值,比如更改为0x
做完这一切,执行:
Mkdir ../u-boot-build
make O=../u-boot-build mini2440_config
make O=../u-boot-build
进行编译(在另外一个目录../u-boot-build中执行构建过程,而不污染源代码目录),将二进制文件下载到内存位置为0x
go
(前提是,有一个已经移植好的U-Boot可供使用)
应该可以看到串口的输出的,说明前面所做的是成功的。