时间:2022-09-16 03:59:35
摘 要:BootLoader移植是嵌入式系统开发中的一个重要环节,而Uboot是一个支持多处理器多操作系统较为通用的BootLoader。分析了Uboot的启动过程,并对s3c2440A如何支持驱动代码在外部Nand Flash上的执行,以及在s3c2440A平台上如何实现Nand Flash启动模式下的Uboot移植进行了介绍。具体操作和编译在fedora9操作系统和armlinuxgcc4.3.2交叉编译下完成。
关键词:嵌入式系统;BootLoader;Nand Flash;Uboot;s3c2440A
中图分类号:TP301 文献标识码:A 文章编号:16727800(2013)003003304
0 引言
BootLoader 是指在操作系统内核运行之前运行的一段程序,其功能在于完成硬件设备的初始化、建立内存空间的映射图、将系统的软硬环境带到一个合适的状态,为最终的内核调试做好准备。Uboot是在ppcboot以及ARMboot的基础上发展而来的较为通用的BootLoader,不仅仅支持Linux嵌入式操作系统的引导,还支持VxWorks、QNX等嵌入式操作系统,Uboot除了支持PowerPC系列处理器,还能支持ARM、x86、XScale、MIPS等诸多常用的处理器。
目前,Uboot的移植解决方案主要面向的是微处理器中的Nor flash。但Nor flash 存储器的价格比较昂贵且存储容量小,而SDRAM和Nand flash存储器的价格相对来说比较合适且容量大,如果能在Nand Flash中实现Uboot的启动并在SDRAM中执行主程序,则不仅能够有效地降低系统的成本,也能给实际应用带来极大的方便。1 Uboot启动过程分析
BootLoader的启动通常有两个阶段,分为阶段1(stage1)和阶段2(stage2),Uboot也不例外。在Uboot中依赖于CPU体系结构的代码(如CPU初始化代码等)通常都放在阶段1中且用汇编语言实现,而为了系统有更好的移植性和可读性,在阶段2中通常用C代码来实现。关于CPU的初始化文件一般放在第一阶段,其对应的文件目录为cpu/arm920t/start.s,主要实现一些寄存器的配置以及初始化、内存和栈空间的配置。阶段2在lib_arm/board.c中,其基本由C语言实现,start_armboot是整个启动代码中C语言的主函数,在其中通过调用各个硬件设备的初始化函数来完成本阶段要使用到的硬件设备的初始化,除此之外还涉及到内存的映射、代码的搬运、设置内核启动参数等功能。具体实现流程如图1所示。
2 Uboot在Nand Flash中的启动
Nand flash 的地址是非线性的,也就是说不能直接在芯片的内部执行程序。但为了满足这一要求, s3c2440A配备了一个内部SDRAM缓冲器,叫做‘Steppingstone’,当系统启动时,Nand Flash 存储器的前4k byte数据将被自动载入到steppingstone中,并且装载到steppingstone中的代码会被执行。执行过程如图2所示。
由上图可知,Uboot下载到Nand Flash中当系统加电时,Nand flash存储空间的前4kb的数据也就是Uboot的第一阶段会自动加载到SRAM中并执行,运行到第二阶段时则把Nand Flash中的Uboot全部拷贝到SDRAM中继续执行。
3 Uboot的移植及实现
Uboot一直都没有支持S3C2440,所以以SBC2410为蓝本进行修改和移植。2410和2440基本上没有什么大的差异,主要区别是2440的主频变得更高,在接口方面增加了摄像头接口和AC97音频接口;在寄存器方面,除了新增模块的寄存器外,Nand Flash 的控制寄存器也有了较大的变化。下面对移植的过程进行介绍。
3.1 开发环境
宿主机的软件开发环境:Fedroa9.0、armlinuxgcc4.4.3.tar。其安装路径为/usr/local/arm/4.3.2、 uboot200911。
目标开发板硬件环境:S3C2440A、SDRAM为K4S561632NLC75、Nand Flash为256M的K9F2G08U0B。
3.2 建立属于自己的board平台
参照board/sbc2410x目录,在源码目录board下建立自己的开发平台mine2440,步骤如下:
(1)这里将板子取名为mine2440,建立board/mine2440目录,拷贝board/sbc2410x下的文件到board/mine2440目录,将sbc2410x.c更名为mine2440.c.
mkdirp mine2440 //进入board目录
cparf sbc2410x/* /board/mine2440
mv sbc2410x.c mine2440.c //进入mine2440目录
(2)修改mine2440 目录下的Makefile文件在Linux中使用gedit命令,将COBJS := sbc2410x.o flash.o 改为COBJS := mine2440.o flash.o
gedit board/mine2440/Makefile
(3)修改Uboot根目录下的Makefile文件。查找到smdk2410_config的地方:
mine2440_config := unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t mine2440 NULL s3c24x0,其各个参数的定义如表1所示。
(4)测试编译环境如下:
make
Make成功则说明编译环境和开发板的创建添加都没有问题。下面针对代码执行流程对创建的开发板做出一些相应的修改。
3.3 在Uboot中添加对S3C2440一些寄存器的支持
由于2410和2440的寄存器及地址大部分是一致的,所以这里就直接在 2410的基础上再加上对2440 的支持即可,即在定义了CONFIG_S3C2410的地方加上CONFIG_S3C2440,再根据两者的不同添加功能设置。
3.3.1 系统时钟配置
2440的最高主频能达到503MHz,但正常情况下让其运行在400MHz,而2410 只有200MHz,所以需要对系统时钟部分做出更改。主要包括主时钟频率即MPLL与USB时钟频率UPLL,下面分别给出具体的计算式:
MPLL= ( 2*m*Fin ) / ( p*2^s ) / * 主时钟频率
UPLL= ( m*Fin ) / ( p*2^s ) / * USB 控制频率
其中 m、p、s的值可以自己设置,用来控制各个模块的时钟输出频率。在2440系统中使用12MHz的晶振也就是公式里的Fin。下面分别给m、p、s赋值具体实现如下:
#define S3C2440_MPLL_400 MHz ( ( 0x5c
< < 12 ) | ( 0x01 < < 4 ) | ( 0x01 ) )
#define S3C2440_UPLL_48 MHz ( ( 0x38
< < 12 ) | ( 0x02 < < 4 ) | ( 0x02 ) )
#define S3C2440_CLKDIV 0x05 / * FCLK : HCLK : PCLK =1 : 4 : 8 , UCLK = UPLL
3.3.2 串口配置
2440带有3个串口,在本系统中使用uart0来与上位机建立通信。串口时钟用pclk时钟,即1/2hclk 时钟,1/8fclk时钟。可以通过对寄存器ULCON0与UFCON0的配置,来对uart0的工作模式进行设置。串口通信中波特率的设置很重要,在此选择115200作为调制速率,具体实现可以通过给寄存器UBRDIV0赋值来实现,具体的设置值可以通过以下计算公式进行计算:
UBRDIVn=(int)(UARTclock/(buadrate*16))-1。其中,UART clock为pclk时钟,buard rate为选择的波特率值。
3.4 实现Nand Flash 启动
(1)在Uboot中添加对mine2440开发板Nand Flash的支持。目前,Uboot中还没有对2440上Nand Flash的支持,也就是说想要Uboot从Nand Flash上启动就得自己去实现。首先在include/configs/mine2440.h头文件中定义NF要用到的宏和寄存器,主要包括如下内容:
#define CONFIG_S3C2440_NAND_BOOT //支持从Nand Flash中启动
#define CONFIG_UBOOT_SIZE 0x60000
//定义存放 uboot 代码段的大小
#define NAND_CTL_BASE 0x4E000000
//Nand Flash 配置寄存器基地址,查 2440 手册可得知
#define STACK_BASE 0x33F00000 // 定义堆栈的地址
#define STACK_SIZE 0x8000 //堆栈的长度大小
#define oNFCONF 0x00 // 相对Nand配置寄存器基地址的偏移量,还是配置寄存器的基地址
#define oNFCONT 0x04
// 相对 Nand配置寄存器基地址的偏移量,可得到控制寄存器
的基地址(0x4E000004)
#define oNFADDR 0x0c
// 相对 Nand配置寄存器基地址的偏移量,可得到地址寄存器
的基地址(0x4E00000c)
#define oNFDATA 0x10
// 相对Nand配置寄存器基地址的偏移量,可得到数据寄存器的基地址(0x4E000010)
#define oNFCMD 0x08
// 相对Nand配置寄存器基地址的偏移量,可得到指令寄存器
的基地址(0x4E000008)
#define oNFSTAT 0x20
//相对Nand配置寄存器基地址的偏移量,可得到状态寄存器的基地址(0x4E000020)
#define oNFECC 0x2c
//相对Nand配置寄存器基地址的偏移量,可得到 ECC 寄存器的基地址(0x4E00002c)
(2)修改cpu/arm920t/start.S文件,因为uboot的入口程序是/cpu/arm920t/start.S,故需在该程序中添加Nand Flash的复位程序,以及实现从Nand Flash中把uboot搬移到RAM中的功能程序。uboot默认是从Nor Flash启动的,所以首先屏蔽掉uboot中的从Nor Flash启动部分。在添加2440中uboot从Nand Flash启动的部分,代码如下:
#ifdef CONFIG_S3C2440_NAND_BOOT
//copy U-Boot to RAM
ldr r0, =TEXT_BASE
…… //这里初始化nandflash控制器,
代码省略
ldr sp, DW_STACK_START
//设置堆栈指针,为调用C语言函数作准备,在其后有定义
mov fp, #0 //初始化帧指针寄存器
//传递给C 代码的第一个参数:u-boot 在RAM中的起始地址
mov r1, #0x0
// 传递给C 代码的第二个参数:Nand Flash 的起始地址
mov r2, # CONFIG_UBOOT_SIZE
// 传递给 C 代码的第三个参数:u-boot 的长度大小(384k)
bl nand_read_ll
// 此处调用 C 代码中读Nand的函数,将整个uboot代码读到ram
………….. //这里验证所读取的代码,代码省略
ok_nand_read:
// 检查搬移后的数据,如果前 4k 完全相同,表示搬移成功
notmatch:
loop3: b loop3 //当不匹配时在此循环
#endif
在_ start_armboot: .word start_armboot
下面加上 DW_STACK_START 的定义
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
(3)在上面启动代码中添加对Nand Flash的支持中调用了nand_read_ll函数,它是跳入新增的C语言文件nand_read.c,将其新建在board/samsung/mine2440/目录下,这个文件参考的vivi代码在其基础进行了修改并添加了对本开发板所用的Nand Flash的支持。因为S3C2440和S3C2410之间的差别就是:S3C2410的Nand Flash 控制器只支持512B+16B 的Nand Flash,而S3C2440还支持2KB+64B的大容量Nand Flash。所以在Nand Flash 控制器上寄存器和控制流程方面的差别很明显。
首先根据用户手册在nand_read.c中定义nandflash控制器和各个寄存器的指针目标:
#define NF_BASE 0x4e000000
#if defined (CONFIG_S3C2440)
…… //根据数据手定义其偏移地址具体定义在此省略
#define NFSTAT_BUSY 1
#endif
再在nand_read.c中加入nand_read_ll函数的实现。这里给出详细的设计和伪程序,具体代码:
int nand_read_ll (unsigned char *buf, unsigned long start_addr, int size)
{
/*参数说明*/
/* buf:指向ram中的目标地址*/
/* start_addr:所读内容在nandflash中的起始地址*/
/* size:复制的总字节数*/
/*主要操作步骤*/
/*1:验证nandflash地址对齐*/
/*2:打开nandflash芯片使能信号*/
/*3:从nandflash首页开始(start_addr),逐页读出size个字节数并放置于ram的指定位置(buf)处*/
/*4:关闭nandflash芯片使能信号*/
……
return 0;
}
最后添加我们使用的nand flash的ID识别程序代码,具体实现如下:
if (nand_id == 0xecda )
{ /* Samsung K9F2G08U0B 我们使用的型号大小为256MB*/
nand.page_size = 2048;
nand.block_size = 128 * 1024;
nand.bad_block_offset= nand.page_size;
}
在完成上面的工作后需要修改board/my2410/Makefile,文件找到:OBJS := my2410.o flash.o修改为COBJS:=mini2440.o flash.o nand_read.o
(4)最后还有一个重要的地方需要修改,在cpu/arm920t/uboot.lds中,这个uboot启动连接脚本文件决定了uboot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把添加的用于nandboot的子函数放到4K之后, 否则无法启动。如下:
添加cpu/arm920t/start.o (.text)
board/samsung/mini2440/lowlevel_init.o (.text)
board/samsung/mini2440/nand_read.o (.text)
在完成以上工作时再次通过make mine2440_config 和make命令将Uboot编译成uboot.bin文件并将其下载到开发板中,通过串口可以看到如下信息,如图3所示。
由调试信息可知,Uboot在Nand Flash中的启动已经实现,并且Uboot成功地识别了Nand Flash的大小。
4 结语
Uboot移植是一个非常复杂的任务,Uboot对Nand Flash闪存的支持不仅体现在Nand Flash启动Uboot上,还体现在Uboot对Nand Flash进行常用操作的Nand Flash命令上。本文在Uboot完全移植中只是完成了一小部分,后续仍有许多工作有待完成。
参考文献:
\[1\] 刘淼.嵌入式系统接口设计与Linux驱动程序开发\[M\].北京:北京航空航天大学出版社,2006.
[2] 黄卫军,苗放,郗诚.Uboot在S3C2410系统上的移植与应用\[J\].铁路计算机应用,2008(8).
\[3\] 芦伟,潘炼.uboot在s3c2440上的移植\[J\].微型机与应用,2010(4).