ARM9嵌入式Linux开发-U-Boot移植

1 为什么需要对U-Boot做移植

BootLoader依赖于具体的嵌入式板级设备的配置。每种不同的CPU体系结构都有不同的BootLoader。除了依赖于CPU的体系结构外,BootLoader 还依赖于具体的嵌入式板级设备的配置,比如板卡的硬件地址分配,外设芯片的类型等。这也就是说,对于两块不同的开发板而言,即使它们是基于同一种CPU而构建的,但如果他们的硬件资源或配置不一致的话,要想在一块开发板上运行的BootLoader程序也能在另一块板子上运行,还是需要作修改。

2 移植关键技术

2.1 U-Boot 移植参考板

这是进行U-Boot 移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM 的情况,以尽可能相一致为原则,先找出一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。对U-Boot 移植新手,建议依照循序渐进的原则,目标板文件名暂时先用移植参考板的名称,在逐步熟悉U-Boot 移植基础上,再考虑给目标板重新命名。在实际移植过程中,可用Linux 命令查找移植参考板的特定代码,如 grep –r 2410 ./ 可确定出在U-Boot 中与smdk2410 板有关的代码,依此对照目标板实际进行屏蔽或修改。同时应不局限于移植参考板中的代码,要广泛借鉴U-Boot 中已有的代码更好地实现一些具体的功能。还要验证一下这个参考开发板的U-Boot,至少能够配置编译通过。

2.2 U-Boot烧写地址和CPU寄存器参数设置

不同目标板,对U-Boot在FLASH中存放地址要求不尽相同。事实上,这是由处理器中断复位向量来决定的,与主板硬件相关 。也就是说,U-Boot烧写具体位置是由硬件决定的,而不是程序设计来选择的。     

根据CPU处理器系列、类型不同,寄存器名称与作用有一定差别。必须根据目标板的实际,进行合理配置。一个较为可行和有效的方法,就是借鉴参考移植板的配置,再根据目标板实际进行合理修改。这是一个较费时间和考验耐力的过程,需要仔细对照处理器各寄存器定义、参考设置、目标板实际作出选择并不断测试。    

2.3 串口调试

能从串口输出信息,即使是乱码,也可以说U-Boot 移植取得了实质性突破。 依据调试经历,串口是否有输出,除了与串口驱动相关外,还与FLASH 相关的寄存器设置有关。因为U-Boot 是从FLASH 中被引导启动的,如果 FLASH 设置不正确,U-Boot 代码读取和执行就会出现一些问题。因此,还需要就FLASH 的相关寄存器设置进行一些参数调试。同时,要注意串口收发芯片相关引脚工作波形。依据调试情况,如果串口无输出,可能就是该芯片损坏或工作不正常或串口线的连接不够牢固。如果出现乱码,有一种可能就是波特率等参数设置有问题。

3 U-Boot移植步骤

U-Boot1.1.6并不支持所使用的微处理器S3C2440,但是对于同一系列的S3C2410却有很完善的支持,本文的U-Boot移植工作是在微处理器S3C2410和对应的SMDK2410开发板的基础上展开。     

3.1 开发板硬件资源配置

本文所使用的开发板的主要硬件配置如下表:

ARM9嵌入式Linux开发-U-Boot移植_第1张图片

3.2 建立交叉编译环境

我们在虚拟机VMWare上安装了Ubuntu11.10,并安装了交叉编译工具链arm-linux-gcc 3.4.1。

3.3 编译测试

1.下载源码 http://ftp.denx.de/pub/u-boot/

2.解压源码包

    root@czu:~# tar jxvf /root/桌面/u-boot-1.1.6.tar.bz2,解压后在/root 目录下生成u-boot-1.1.6 目录

3.建立自己的目标板

  •       进入 u-boot-1.1.6 目录。
    • 将board/smdk2410 目录复制为czu2440目录 cp -rf board/smdk2410/ board/czu2440     
  • 修改 smdk2410.c 为czu2440.c         
    • mv board/czu2440/smdk2410.c board/czu2440/czu2440.c         
    • 修改Makefile 文件中28 行的COBJS 改为:         COBJS := czu2440.o flash.o     
    • 建立目标板配置文件:进入include/configs 目录下, 将smdk2410.h 复制为czu2440.h  cp include/configs/smdk2410.h include/configs/czu2440.h
  • 修改顶层(u-boot-1.1.6 目录)Makefile 文件1881 行,增加:         
    • czu2440_config : unconfig         
    • @$(MKCONFIG) $(@:_config=) arm arm920t czu2440 NULL s3c24x0         
    • 各项的意思如下:             
      • arm: CPU 的架构(ARCH)             
      • arm920t: CPU 的类型(CPU),其对应于 cpu/arm920t 子目录。             
      • czu2440: 开发板的型号(BOARD),对应于 board/dong2440 目录。             
      • NULL: 开发者/或经销商(vender)。(此处没加 vender,为 NULL。)             
      • s3c24x0: 片上系统(SOC)。
  • 配置交叉编译器:修改顶层(u-boot-1.1.6 目录)Makefile 文件128 行,修改:         
    • CROSS_COMPILE = arm-linux-         
    • 使用arm-linux-gcc –v命令查看交叉编译器的版本是否是3.4.1。

4.编译测试

  • 进入 u-boot-1.1.6 目录     
  • #make mrproper //(或#make distclean 修改顶层Makefile 等相关文件必须执行此步骤)     
  • #make czu2440_config     
  • #make all     
  • 如果没有错误,则会生成u-boot.bin 文件。至此,自己的目标板已经建立,下面要做的是修改一些配置,增加一些驱动。

3.4 具体的移植操作

3.4.1概述

U-Boot移植操作,实际上就是根据嵌入式系统硬件资源,对相关的文件进行修改。     

移植U-Boot到新的嵌入式系统板上包括两个层面的移植,第一层面是针对CPU的移植,第二层面是针对BOARD的移植,同时需要移植相关的头文件。与BOARD相关的文件位于board/smdk2410目录下,复制smdk2410目录并修改文件lowlevel_init.S,flash.c,smdk2410.c。lowlevel_init.S文件是完成系统板外部存储空间的配置;smdk2410.c是通用I/O口的初始化,flash.c是FLASH芯片的驱动程序。与CPU相关的文件位于cpu/arm920t目录及其子目录s3c24x0下,文件start.S主要完成底层硬件初始化和代码搬运任务。头文件也分为CPU和BOARD两个层次,与BOARD相关的头文件主要是include/configs/smdk2410.h和include/flash.h,二者完成系统板参数的基本配置。与CPU相关的头文件是include/s3c2410.h和s3c24x0.h,二者主要完成CPU内部寄存器和中断向量表设置。     

除以上需要修改的文件外,公共代码(common目录下的文件)、网络传输代码(net目录)、驱动程序(drivers目录)3部分根据不同的移植要求修改.一般情况下,对U-Boot的功能进行扩充或添加不支持的设备时,需要修改相应的程序。

3.4.2增加对S3C2440 的支持

加入S3C2440 相关代码,使得u-boot可以在s3c2440上正常工作:

1、修改SDRAM 配置 进入 board/czu2440目录修改lowlevel_init.S 文件

2、时钟设置,S3c2440 的时钟计算公式和s3c2410 不一样

  • 修改 cpu/arm920t/start.S 文件,屏蔽S3C2410的时钟设置代码
  • 在 board/czu2440 目录下建议一个名为boot_init.c 的文件,编写colck_init 函数初始化时钟
  • 修改 board/czu2440/czu2440.c 文件中的board_init 函数
  • 修改 board/czu2440/目录下的Makefile,以增加对 boot_init.o 的连接
  • 由于分频系数的设置方法也不一样,
    • 修改cpu/arm920t/s3c24x0/speed.c
    • 修改get_PPLCLK 函数
    • 修改get_HCLK、get_PCLK
  • 在include/s3c24x0.h中重新定义S3C24X0_CLOCK_POWER结构体 S3C24X0_REG32 CAMDIVN; /* for s3c2440*/

3.4.3配置Nor Flash

本开发板中的Nor Flash型号为SST39VF1601,而配置文件include/configs/czu2440.h中默认型号为AM29LV400。因为本开发板Nor Flash为2MB,和AM29LV800 很相似,且U-Boot也指出,所以对Nor Flash配置修改一下就可以了。     

本例中Nor Flash的操作函数在board/czu2440/flash.c中实现,它支持AM29LV400 和AM29LV800。

#if 0
#define CONFIG_AMD_LV400	1	/* uncomment this if you have a LV400 flash */
#endif
#define CONFIG_AMD_LV800	1	/* uncomment this if you have a LV800 flash */
--------------------------------------------------------------------------------------------------------------------------------------------
#define PHYS_FLASH_SIZE		0x00200000 /* 2MB */
--------------------------------------------------------------------------------------------------------------------------------------------
#define CFG_ENV_ADDR		(CFG_FLASH_BASE + 0x1F0000) /* addr of environment */
--------------------------------------------------------------------------------------------------------------------------------------------
#define CFG_ENV_SIZE		0x20000	/* Total Size of Environment Sector */

3.4.4增加Nand Flash读写驱动

U-Boot1.1.6对Nand Flash的支持有新旧两套产品,新代码在drivers/nand目录下,旧代码在drivers/nand_legacy目录下。新代码来自Linux内核2.6.12,更加智能,可以自动识别更多型号的Nand Flash,之所以还保留旧代码是因为NETTA和NETTA_ISDN开发板使用JFFS文件系统,它们还依赖旧代码。选择使用哪一套代码由相应的宏决定。本文使用新代码,需要增加CFG_CMD_NAND宏。

Nand Flash的初始化函数是lib_arm/board.c中的start_armboot函数调用的nand_init函数。该函数在/drivers/nand/nand.c中实现,它调用相同文件中的nand_init_chip函数;nand_init_chip函数首先调用board_nand_init函数来初始化Nand Flash设别,最后才是统一的识别过程。board_nand_init函数和平台/开发板相关,需要自己编写。     

讲解完基本原理后,接下来讲解移植过程。

修改配置文件 include/configs/czu2440.h, 修对 Flash 的配置和增加 NAND

//#define	CFG_ENV_IS_IN_FLASH	1
#define CFG_ENV_IS_IN_NAND		1
#define CFG_ENV_OFFSET		0x80000
#define CFG_ENV_SIZE64		0xc000	/* Total Size of Environment Sector */
#define CFG_ENV_SIZE		0x20000	/* Total Size of Environment Sector */

/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#define CFG_NAND_BASE		0   //基地址 这个在board_nand_init中会重新指定
#define CFG_MAX_NAND_DEVICE	1  //设备数目
#define NAND_MAX_CHIPS		1  //每个设备由一个芯片组成

修改配置文件 include/configs/czu2440.h,增加 NAND 命令

#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \
CFG_CMD_CACHE | \
CFG_CMD_NAND | \
/*CFG_CMD_EEPROM |*/ \
/*CFG_CMD_I2C |*/ \
/*CFG_CMD_USB |*/ \
CFG_CMD_REGINFO | \
CFG_CMD_DATE | \
CFG_CMD_ELF)

增加 :cpu/arm920t/s3c24x0/nand_flash.c文件,实现board_nand_init函数。

同时修改cpu/arm920t/s3c24x0目录下的 Makefile

修改该目录下的 Makefile:29 行

COBJS = i2c.o interrupts.o serial.o speed.o \

usb_ohci.o nand_flash.o

修改 include目录下的s3c24x0.h和s3c2410.h文件

在 include/s3c24x0.h 中定义 S3C2440_NAND结构体:168 行

/* NAND FLASH */
typedef struct {
	S3C24X0_REG32 NFCONF;
	S3C24X0_REG32 NFCONT;
	S3C24X0_REG32 NFCMD;
	S3C24X0_REG32 NFADDR;
	S3C24X0_REG32 NFDATA;
	S3C24X0_REG32 NFMECCD0;
	S3C24X0_REG32 NFMECCD1;
	S3C24X0_REG32 NFSECCD;
	S3C24X0_REG32 NFSTAT;
	S3C24X0_REG32 NFESTAT0;
	S3C24X0_REG32 NFESTAT1;
	S3C24X0_REG32 NFMECC0;
	S3C24X0_REG32 NFMECC1;
	S3C24X0_REG32 NFSECC;
	S3C24X0_REG32 NFSBLK;
	S3C24X0_REG32 NFEBLK;
} /*__attribute__((__packed__))*/ S3C2440_NAND;

同时在 include/s3c2410.h中仿照S3C2410_GetBase_NAND函数添加S3C2440_GetBase_NAND函数:

static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)
{
	return (S3C2440_NAND * const)S3C2410_NAND_BASE;
}

3.4.5支持网卡CS8900

U-Boot1.1.6已经支持CS8900网卡,我们设置一些参数就可以使用了,修改include/config/czu2440.h文件:

#define CONFIG_ETHADDR	08:00:3e:26:0a:5b 
#define CONFIG_NETMASK  255.255.255.0
#define CONFIG_IPADDR	192.168.1.5
#define CONFIG_SERVERIP	192.168.1.1

3.4.6支持NAND Flash 启动

  • 修改cpu/arm920t/start.S ,修改代码搬移程序。U-Boot1.1.6仅支持从Nor Flash启动,我们增加CopyCode2Ram函数将U-Boot从Nand Flash复制到SDRAM中。
  • 修改 board/czu2440/boot_init.c 增加CopyCode2Ram 函数及其支持子函数

3.4.7引导Linux 内核

  • 修改include/configs/czu2440.h,添加U-Boot给Linux 传递参数所需要的宏定义和环境变量配置
  • 修改 include/asm-arm/mach_types.h,更改 mach_type 参数
  • 用 mkzImage 给 zImage 加头信息

3.4.8支持Yaffs 文件系统

在实际生产中,可以通过烧片器等工具将内核、文件系统的镜像文件烧入存储器中,BootLoader不需要具备烧写功能。但为了开发方便,通常要增加烧写 内核和文件系统镜像的功能。     

U-Boot增加了Nand Flash功能后就可以通过“nand write...”、“nand write.jeffs2..”等命令烧写内核,烧写cramfs、jeffs2文件系统镜像系统。     

但在Nand Flash上,yaffs文件系统的性能更加。下面我们来增加“nand write.yaffs..”命令烧写yaffs文件系统镜像。

“nand write.yaffs..”中“nand”是具体的命令,“write.yaffs..”是参数。

U_BOOT_CMD(nand, 5, 1, do_nand,
	"nand    - NAND sub-system\n",
	"info                  - show available NAND devices\n"
	"nand device [dev]     - show or set current device\n"
	"nand read[.jffs2]     - addr off|partition size\n"
	"nand write[.jffs2]    - addr off|partiton size - read/write `size' bytes starting\n"
	"    at offset `off' to/from memory address `addr'\n"
	"nand erase [clean] [off size] - erase `size' bytes from\n"
	"    offset `off' (entire device if not specified)\n"
	"nand bad - show bad blocks\n"
	"nand dump[.oob] off - dump page\n"
	"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
	"nand markbad off - mark bad block at offset (UNSAFE)\n"
	"nand biterr off - make a bit error at offset (UNSAFE)\n"
	"nand lock [tight] [status] - bring nand to lock state or display locked pages\n"
	"nand unlock [offset] [size] - unlock section\n");

在 commom/cmd_nand.c 中增加"nand write.yaffs" 的使用说明

U_BOOT_CMD(nand, 5, 1, do_nand,
	"nand    - NAND sub-system\n",
	"info                  - show available NAND devices\n"
	"nand device [dev]     - show or set current device\n"
	"nand read[.jffs2]     - addr off|partition size\n"
	"nand write[.jffs2]    - addr off|partiton size - read/write `size' bytes starting\n"
	"    at offset `off' to/from memory address `addr'\n"
	 "nand read.yaffs addr off size - read the `size' byte yaffs image starting\n"
    	"    at offset `off' to memory address `addr'\n"
    	"nand write.yaffs addr off size - write the `size' byte yaffs image starting\n"
    	"    at offset `off' from memory address `addr'\n"
	"nand erase [clean] [off size] - erase `size' bytes from\n"
	"    offset `off' (entire device if not specified)\n"
	"nand bad - show bad blocks\n"
	"nand dump[.oob] off - dump page\n"
	"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
	"nand markbad off - mark bad block at offset (UNSAFE)\n"
	"nand biterr off - make a bit error at offset (UNSAFE)\n"
	"nand lock [tight] [status] - bring nand to lock state or display locked pages\n"
	"nand unlock [offset] [size] - unlock section\n");

Nand Flash每一页大小为(2048+64)B(当然还有其他格式),其中的2048B是一般存储数据的区域,64B称为OOB(Out Of Band)区。通常在OOB区存放着坏块标记、前面2048B的ECC校验码等。         

camfs、jffs2文件系统镜像没有OOB取的内容,如果将其烧写到Nor Flash时,则是简单的“平铺”,如果烧写到Nand Flash中,则Nand Flash的驱动程序首先会根据OOB的标记掠过坏块,然后将一页数据,也就是2048B写入后,还要计算刚写入的2048B数据的ECC值,并写入OOB区,如此循环。camfs、jffs2文件系统镜像的大小通常是512的整数倍。     

yaffs文件系统镜像文件本身就包含了OOB区的数据,这里面有坏块标记,ECC校验码和其他yaffs相关的信息。所以烧写时,不需要再计算ECC值,首先检查是否是坏块,是则跳过,然后写入2048B的数据,最后写入64B的OOB数据,如此循环。yaffs文件系统镜像文件的大小是(2048+64)的整数。要注意的是:烧写yaffs文件系统镜像时,分区上的第一个可用的(不是坏块)块要跳过。

在 nand 命令的处理函数 do_nand 中增加对"nand yaffs"的支持(左边的代码)。

}else if (  s != NULL && !strcmp(s, ".yaffs")){
            if (read) {
                /* read */
                nand_read_options_t opts;
                memset(&opts, 0, sizeof(opts));
                opts.buffer = (u_char*) addr;
                opts.length = size;
                opts.offset = off;
                opts.readoob = 1;
                opts.quiet      = quiet;
                ret = nand_read_opts(nand, &opts);
            } else {
                /* write */
                nand_write_options_t opts;
                memset(&opts, 0, sizeof(opts));
                opts.buffer = (u_char*) addr;
                opts.length = size;
                opts.offset = off;
                /* opts.forceyaffs = 1; */
                opts.noecc = 1;//不计算ECC
                opts.writeoob = 1;//烧入文件中的OOB
                opts.blockalign = 1;
                opts.quiet      = quiet;
                opts.skipfirstblk = 1;//跳过第一个可用的逻辑块
                ret = nand_write_opts(nand, &opts);
            }
}

上述代码中,opts.skipfirstblk 是新增加的项,表示烧写时跳过第一个可用的逻辑块, 这是由yaffs文件系统的特性决定的。下面给opts.skipfirstblk 新增加项重新定义 nand_write_options_t结构,并在下面调用的 nand_write_opts 函数中对他进行处理。在 include/nand.h 中进行如下修改,增加 skipfirstblk 成员:

struct nand_write_options {
	u_char *buffer;	/* memory block containing image to write */
	ulong length;	/* number of bytes to write */
	ulong offset;	/* start address in NAND */
	int quiet;		/* don't display progress messages */
	int autoplace;	/* if true use auto oob layout */
	int forcejffs2;	/* force jffs2 oob layout */
	int forceyaffs;	/* force yaffs oob layout */
	int noecc;		/* write without ecc */
	int writeoob;	/* image contains oob data */
	int pad;		/* pad to page size */
	int blockalign;	/* 1|2|4 set multiple of eraseblocks to align to */
	int skipfirstblk;
};

在 drivers/nand/nand_util.c修改nand_write_opts 函数,增加对skipfirstblk 成员的支持。

int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
{
	int imglen = 0;
	int pagelen;
	int baderaseblock;
	int blockstart = -1;
	loff_t offs;
	int readlen;	
	int oobinfochanged = 0;
	int percent_complete = -1;
	struct nand_oobinfo old_oobinfo;
	ulong mtdoffset = opts->offset;
	ulong erasesize_blockalign;
	u_char *buffer = opts->buffer;
	size_t written;
	int result;
	int skipfirstblk = opts->skipfirstblk;
	if (opts->pad && opts->writeoob) {
		printf("Can't pad when oob data is present.\n");
		return -1;
	}

/* skip the first good block when wirte yaffs image */
if (skipfirstblk) {
	mtdoffset += erasesize_blockalign;
	skipfirstblk = 0;
	continue;
}
		readlen = meminfo->oobblock;
		if (opts->pad && (imglen < readlen)) {
			readlen = imglen;
			memset(data_buf + readlen, 0xff,
			       meminfo->oobblock - readlen);
		}

进行上面移植后,u-boot 已经支持 yaffs 文件系统映象的烧写,由于前面设"opts.noecc=1"不使用 ECC 校验码,烧写时会提示很多提示信息。修改 drivers/nand/nand_base.c 文件中的 nand_write_page 函数, 将其注释掉

case NAND_ECC_NONE:
		//printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
		this->write_buf(mtd, this->data_poi, mtd->oobblock);
		break;

U-Boot详细移植过程:https://download.csdn.net/download/ce123/12452602

你可能感兴趣的:(ARM9嵌入式Linux开发)