基于cortex-A8的Bootloader设计

发布时间:2015-5-7 14:29    发布者:designapp
关键词: cortext-A8 , Bootloader设计 , s5pv210

            随着整个微电子行业日新月异的发展,ARM处理器已经带给了人们越来越多的惊喜。目前它凭借着自身高性能,低成本,低功耗等特点,已经广泛应用于消费电子、数字家电、及工业生产等领域中。ARM公司推出的新一代cortex系列产品为开发人员提供了更多不同层面的选择。cortex-A8为cortex系列中的“A”系,属于ARMV7架构,主要面向搭载操作系统的,高性能的应用领域。Bootloader是系统上电后执行的第一段代码,其功能类似于电脑的BIOS。简单来说,Bootloader主要的工作有两点:其一是初始化底层硬件资源,为操作系统启动提供必要的环境;其二是从存储设备中读取操作系统镜像并启动。但为了方便后期开发,我们往往要为Bootloader添加其他的功能。如支持串口打印调试信息,支持nfs网络下载,支持根文件系统烧写等。本文以u-boot-2013.01为源码包,设计和实现了一个基于s5pv210平台的,功能齐全且高效稳定的Bootloader。
1 硬件平台
1.1 s5pv210简介
    s5pv210是三星公司推出的一款基于cortex-A8架构的,高性能的应用处理器。该处理器支持ARM V7指令集,具有32位内部总线结构,主频最高可达1GHz。另外该处理器还支持挂接LPDDR1、LPDDR2和DDR2类型的RAM,Flash方面可选择Nand Flash,Nor Flash等。不仅如此,该处理器还提供了包括串口、LCD、IIC、SPI、USBHDMI等丰富的外部接口资源。目前s5pv210以其高效的性能和卓越的图形处理能力已经广泛的应用于智能手机和平板电脑之中。
1.2 s5pv210的启动方式
    s5pv210支持多种启动方式,下图为s5pv210启动的流程图:
                     


图1  s5pv210的启动流程

s5pv210的启动过程由BL0,BL1和BL2(BL为Bootloader的简称)三部分代码实现,其中BL0在出厂时已经被固化到64KB的iROM中。s5pv210上电后首先执行BL0,该段代码主要负责一些简单的初始化工作,如关看门狗,初始化ICache等。然后BL0会根据硬件设置判断为何种方式启动,并将BL1从启动设备(OneNand,Nand Flash,USB,UART,SD card等)拷贝到Internal SRAM的BL1区中。最后BL0会比较一个校验值,如果相等则跳转到BL1中继续执行,否则转入其他启动方式。该校验值存在BL1的头部中,其大小为16字节。
开发人员一般会单独编写一个工具对BL1编译出的二进制文件添加头部。其校验值(checksum)的计算方法如下:
...
a = Buf + SPL_HEADER_SIZE;   
for(i = 0, checksum = 0; i


       
2 软件平台  
2.1 U-Boot介绍
U-Boot(全称Universal Boot Loader)是Bootloader的一种,其他常见的Bootloader还有RedBoot,vivi等。与之相比,U-Boot具有代码开源,可靠性和扩展性较高等优点。另外,对于类似于Nand Flash、Nor Flash、网卡、LCD等硬件资源的,U-Boot已经抽象出与硬件平台无关的代码作为设备驱动源码提供给开发者。对于与硬件平台相关的代码,U-Boot将其定义成宏,并保留到配置文件中。开发者往往只需要修改这些宏的值就能成功使用这些硬件资源,这让我们的移植工作变得十分简单。
2.2 U-Boot中几个比较重要的源文件
U-Boot的源码包包含有上千个源文件,但经过分析之后,可以找出如下几个比较重要的源文件(括号中为该源文件所在源码包的位置)。
(1)start.S(arch\arm\cpu\armv7\start.S)
    通过分析U-Boot的链接文件可以发现,start.S是U-Boot上电后被执行的第一个源文件。该源文件包括定义了异常向量入口,相关的全局变量,禁用L2缓存,关闭MMU等,之后跳转到lowlevel_init()函数中继续执行。
(2)lowlevel_init.S(board\samsung\smdkv210\lowlevel_init.S)
     该源文件同样用汇编代码编写,其中只定义了一个函数lowlevel_init()。该函数实现对平台硬件资源的一系列初始化过程,包括关看门狗,初始化系统时钟,内存和串口。
(3)mem_setup.S(board\samsung\smdkv210)
    该源文件包含对内存进行初始化的汇编源码。
(4)board.c(arch\arm\lib\board.c)
    该源文件是用C编写的,主要实现了U-Boot第二阶段启动过程。包括初始化环境变量,串口控制台,Flash和打印调试信息等,最后调用main_loop()函数。
(5)smdkv210.h(include\configs\Smdkv210.h)
s5pv210平台的配置文件,该源文件定义了一些与CPU或者外设相关的参数,这些参数都是用宏来定义的。
2.3 U-Boot启动的一般流程
2.3.1 第一阶段初始化
U-Boot的启动过程分为两个阶段,第一个阶段主要由汇编代码实现,负责对CPU及底层硬件资源的初始化。第二阶段用C实现,负责使能Flash,网卡和引导操作系统等。其第一阶段流程如下图所示:


图2  U-Boot第一阶段启动流程

U-Boot上电后首先会设置cpu为管理模式,禁用L1缓存,关闭MMU和清除caches。之后会调用底层初始化函数lowlevel_init()。该函数实现如下:
.globl lowlevel_init
lowlevel_init:
        push   {lr}
#if defined(CONFIG_SPL_BUILD)
        /* 初始化时钟 */
        bl  system_clock_init
        /* 初始化内存 */
        bl  mem_ctrl_asm_init
        /* 初始化串口 */
        bl  uart_asm_init
#endif
        pop    {pc}
上述代码中system_clock_init(), mem_ctrl_asm_init(),uart_asm_init()这三个函数需要开发者结合具体硬件环境进行修改和实现。
初始化完成之后,U-Boot会调用一个拷贝函数将BL2拷贝到内存地址为0x3FF00000处,然后跳转到该位置执行BL2。在U-Boot中,BL1和BL2是基于相同的一些源文件编译生成的。开发者在编写代码时需要使用预编译宏CONFIG_SPL_BUILD来实现BL1和BL2不同的功能。其拷贝函数实现如下:
void copy_code_2_sdram_and_run(void)
{
        unsigned long ch;
        void (*u_boot)(void);  
        ch = *(volatile unsigned int *)(0xD0037488);  /* 根据该地址的值判断传输通道 */
/* copy_bl2()函数不需要开发者去实现,s5pv210在出厂时已经固化在了0xD0037F98地址处 */
        copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem) (*(unsigned int *) (0xD0037F98));
        unsigned int ret;
        if (ch == 0xEB000000) { /* CONFIG_SYS_TEXT_BASE = 0x3FF00000 */
                ret = copy_bl2(0, 49, 1024,(unsigned int *)CONFIG_SYS_TEXT_BASE, 0);
        } else if (ch == 0xEB200000) {
                ret = copy_bl2(2, 49, 1024,(unsigned int *)CONFIG_SYS_TEXT_BASE, 0);
        } else {
                return;
}
u_boot = (void *)CONFIG_SYS_TEXT_BASE;
    (*u_boot)(); /* 跳转到该地址执行 */
}
值得注意的是以上代码中,copy_bl2()函数不需要开发者去实现,s5pv210在出厂时已经将该函数固化在了0xD0037F98地址处。其函数原型如下:
u32 (*copy_sd_mmc_to_mem)(u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);
/*
参数介绍:
channel:通道数2或0,该值通过读取0xD0037488地址上的值判断。
start_block:从第几个扇区开始拷贝,一个扇区为512byte。
block_size:拷贝多少个扇区,这里拷贝512K
trg:目的地址:0x3FF00000, 即离内存顶部1M的位置
init:是否需要初始化sd卡,写0即可。
*/





       
2.3.2 第二阶段初始化
U-Boot进入第二阶段后会首先声明一个gd_t结构体类型的指针指向内存地址(0x40000000 - GD_SIZE)处。0x40000000为内存结束地址,GD_SIZE为结构体gd_t的大小。这样相当于在内存最顶端分配了一段空间用于存放一个临时结构体gd_t。该结构体在global_data.h中被定义,U-Boot用它来存储所有的全局变量。之后U-Boot会调用board_init_f()和board_init_r()两个函数进一步对底板进行初始化。
(1)board_init_f()
进入board_init_f()之后,U-Boot首先设置之前分配的临时结构体,然后开始划分内存空间,其内存分配示意图如下:


图3  U-Boot内存分配状态

从内存分配状态图中我们可以看到,gd指针指向的临时结构体存放在内存的最顶部。BL2代码存放在内存地址0x3ff00000处,即距离内存顶部1M空间的位置。接下来依次分配malloc空间,bd_t结构体空间和gd_t结构体空间,并且重新设置栈。最后将临时结构体拷贝到id指针所指向的位置。board_init_f()实现过程大致如下:
unsigned int board_init_f(ulong bootflag)
{
memset((void *)gd, 0, sizeof(gd_t));
...
设置gd结构体;
...
addr = CONFIG_SYS_TEXT_BASE;  /* CONFIG_SYS_TEXT_BASE = 0x3ff00000000 */
addr_sp = addr - TOTAL_MALLOC_LEN;
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
...
memcpy(id, (void *)gd, sizeof(gd_t));
base_sp = addr_sp;
return (unsigned int)id;
}
(2)board_init_r()
board_init_r()负责对其他硬件资源进行初始化。如网卡、Flash、MMC、中断等。最后调用main_loop(),等待用户输入命令。




       
3 设计实现
3.1 支持Nand Flash读写
Nand Flash是嵌入式系统中重要的存储设备,其储存对象包括Bootloader本身,操作系统内核,环境变量,根文件系统等,所以使能Nand Flash读写是U-Boot移植过程中必须完成的一个步骤。U-Boot中Nand Flash初始化函数调用关系为:
board_init_r()->nand_init()->nand_init_chip()->board_nand_init()。
board_nand_init()完成两件事:(1)对s5pv210关于Nand Flash控制器的相关寄存器进行设置。(2)对nand_chip结构体进行设置。我们需要设置的成员项有IO_ADDR_R,IO_ADDR_W,这两个成员都指向地址0xB0E00010,即 Nand Flash控制器的数据寄存器的地址。另外还需要实现以下三个成员函数:
① void (*select_chip)(struct mtd_info *mtd, int chip);
该函数实现Nand Flash设备选中或取消选中。
② void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
该函数实现对Nand Flash发送命令或者地址。
③ int (*dev_ready)(struct mtd_info *mtd);
该函数实现检测Nand Flash设备状态。
最后将成员ecc.mode设置为NAND_ECC_SOFT,即ECC软件校验。
配置文件中相应的宏定义如下所示:
#define CONFIG_NAND_S5PV210   /* 告诉Makefile编译Nand模块 */
#define CONFIG_SYS_MAX_NAND_DEVICE    1   /* 指定设备数量 */
#define CONFIG_SYS_NAND_BASE     0xB0E00000    /* Nand Flash 控制器的基址 */
3.2 支持网卡
支持nfs或tftp网络下载会极大的方便我们从Linux服务器上下载文件或镜像到硬件平台上。所以使能网卡在U-Boot移植过程中也显得非常重要。以网卡dm9000为例,U-Boot已经抽象出一套完整的关于dm9000的驱动代码(其源码路径为drivers\net\dm9000x.c)。用户只需要根据具体的硬件电路配置相应的宏即可。U-Boot中dm9000网卡初始化函数的调用关系为:
board_init_r()->eth_initialize()->board_eth_init()->dm9000_initialize()。
配置文件中相应的宏定义如下所示:
#define CONFIG_DRIVER_DM9000     /* DM9000网卡模块加入编译 */
#define CONFIG_DM9000_BASE       (0x88001000)  /* 基地址 */
#define DM9000_IO                (CONFIG_DM9000_BASE)  /* IO口地址 */
#define DM9000_DATA              (CONFIG_DM9000_BASE + 0x300C)  /* 数据口地址 */
3.3.支持环境变量的保存和修改
为了方便用户配置,U-Boot将一部分变量,如串口波特率、ip地址、内核参数、启动命令等存在Flash或SD卡上,这部分数据称为环境变量。每次上电启动时,U-Boot会检查Flash或SD卡上是否存放有环境变量。如果有则将其读取出来并使用,如果没有就使用默认的环境变量。默认的环境变量定义在env_default.h中。用户也可以随时修改或保存环境变量到Flash或SD卡中。
对于环境变量的移植也非常简单。以Nand Flash为例,开发人员在smdkv210.h源文件中只需要添加如下的宏定义即可:
#define CONFIG_ENV_IS_IN_NAND          /* 告诉Makefile环境变量保存在Nand Flash中 */
#define CONFIG_ENV_OFFSET           0x80000  /* 环境变量保存的Nand Flash中的偏移地址 */
#define CONFIG_ENV_SIZE             0x20000   /* 环境变量的大小 */
#define CONFIG_ENV_OVERWRITE       /* 规定环境变量和覆盖 */




       
4. 测试结果
4.1. 测试U-Boot启动
测试平台的软硬件环境:
◆ 硬件平台为友善之臂的smart210开发板;
◆ Linux服务器版本为ubuntu9.0;
◆ U-Boot版本为2013-01;
◆ GCC交叉编译工具链版本为4.3.2。
首先将修改后的源码包放到装有交叉编译工具链的Linux服务器上,输入命令make即可以编译生成两个二进制文件。一个是smdkv210-spl.bin,即BL1代码,存放在spl目录下。另一个是u-boot.bin,即BL2代码,存放在顶层目录下。然后插入SD卡,执行write2sd.sh脚本就可以把BL1和BL2分别烧写到SD卡的第1扇区和第49扇区。write2sd.sh脚本实现如下:
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=spl/smdkv210-spl.bin of=/dev/sdc seek=1
sudo dd iflag=dsync oflag=dsync if=u-boot.bin of=/dev/sdc seek=49
烧写完成之后将SD卡插入到smart210开发板的SD卡槽中,连接好串口和网线,上电启动。之后可以通过串口工具(本次测试使用的是SecureCRT)看到输出信息如图四所示,表示该U-Boot已经成功运行在开发板上。


图四  测试U-Boot启动

  
4.2 测试Nand Flash和网卡。
在等待命令状态下输入“nfs 20000000 192.168.1.123:/work/nfs_root/uImage”,该命令表示从ip为192.168.1.123的Linux服务器上,通过nfs下载该服务器上“/work/nfs_root”目录下的uImage,并存放在内存地址为0x20000000处。uImage为适配于s5pv210开发板的Linux内核镜像。等待一段时间后看到“Bytes transferred = 2127008 (2074a0 hex)”字样,表示下载成功。
下载成功之后输入命令“nand erase.part kernel”,该命令表示擦除Nand Flash上的kernel分区。接着输入命令“nand write 20000000 kernel”,该命令表示拷贝内存地址为0x20000000的内容,烧写到Nand Flash的kernel分区中。内存地址0x20000000存放的内容正是之前我们从Linux服务器上下载下来的uImage。kernel保存在环境变量mtdparts中,kernel规定了该分区的起始地址和大小。执行完这两条命令后看到“4194304 bytes written: OK”字样,表示系统内核已经成功烧写到Nand Flash中,证明了Nand Flash和网卡均可以正常使用。其实验结果如图五所示:
           


图五  测试Nand Flash和网卡

4.3 测试引导内核启动
烧写完内核之后执行boot命令就可以启动该内核。boot命令实际上是执行环境变量bootcmd中的一条语句,其内容为“nand read 20000000 kernel; bootm 20000000”,该语句表示从Nand Flash的kernel分区中读出内核并存放在内存地址为0x20000000处,然后跳转在该地址执行内核镜像。启动过程中可以看到串口打印出如图六所示的信息,证明了该U-Boot已经成功支持引导Linux操作系统。


图六   测试引导内核启动

结语
本文从硬件,软件两个方面分析了s5pv210的特性及启动方式,然后通过u-boot-2013.01源码包详细阐述了U-Boot启动过程中的两个阶段,最后结合smart210开发板成功设计了一个基于SD卡启动的,多功能的Bootloader。测试结果表明该Bootloader支持Nand Flash 读写,nfs网络下载,环境变量保存和修改等功能,为后期开发带来了极大的便利。



本文地址:https://www.eechina.com/thread-148983-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表