第 3 章 嵌入式系统的 BOOTLOADER
3.3 基于 S3C2410 开发板的 B OOT L OADER 实现
3.3.2 U-Boot 分析与移植
3.3.2.3 U-Boot 的移植过程
main_loop (); /*具体实现参见 common/main.c 文件*/
}
上述讲述了 U-Boot 在 Stage2 主要所做的工作,当然上述给出的只是一般情况下的,可 以根据具体的开发板相应的增加或减少实现代码。下面将介绍基于 S3C2410 开发板的 U-Boot 移植。
3.3.2.3 U-Boot 的移植过程
前面两节对 U-Boot 已经有了较为详细地介绍,本节开始讲述一个具体的 U-Boot 移植实 例,该实例的硬件环境是基于前面所述的 S3C2410 开发板。
软件环境:
l u-boot-1.1.3.tar.bz2(从http://sourceforge.net/projects/u-boot下载)
l arm-linux-gcc 3.3.6(根据第二章内容自己构建或从ftp.arm.kernel.org.uk直接下载)
准备好软、硬件环境后就开始真正的 U-Boot 移植工作了,下面将一步一步介绍移植的 具体过程。
1.修改 Makefile
首先给要建立的 S3C2410 开发板取名为 mike2410,因为 U-Boot 的移植过程需要它。修 改 Makefile 的具体操作如下:
# tar –xvjf u-boot-1.1.3.tar.bz2
# cd u-boot-1.1.3
# vi Makefile
由于 mike2410 开发板和三星的 smdk2410 开发板很相似,所以在移植 U-Boot 时大部分 源代码都以 smdk2410 为模版,然后在此基础上进行修改。此处修改 Makefile 如图 3.2 所示,
在 Makefile 中添加了如下内容:
mike2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t mike2400 NULL s3c24x0
其中,mike2400_config:unconfig 意思是为 mike2410 开发板建立一个编译项;第二行中 arm 的意思是 CPU 的架构是基于 ARM 体系的;arm920t 的意思是 CPU 的类型是 arm920t;
mike2410 的意思是开发版的型号;NULL 的意思是开发者或经销商的名称为空;s3c24x0 的意思是基于 s3c24x0 的片上系统。
图 3.2:修改 U-Boot 的 Makefile 2.建立 mike2410 开发板目录
在 board 目录下建立 mike2410 开发板子目录,目前 board 目录下已经有两百多种针对 不同开发板的子目录,由于 mike2410 开发板最接近 smdk2410 开发板,所以可以用下面的 简单方法建立 mike2410 开发板目录:
# cp –fr board/ smdk2410 board/ mike2410
# cd board/ mike2410
# mv smdk2410.c mike2410.c
同时需要修改 board/mike2410/Makefile 文件,修改如图 3.3 所示,修改的内容是 将 COBJS := smdk2410.o flash.o
改为 COBJS := mike2410.o flash.o
由 于 board/mike2410 目 录 下 的 源 代 码 文 件 为 mike2410.c, 所 以 编 译 中 间 文 件 为 mike2410.o,如果没有修改这个 Makefile 文件编译就会出错。
图 3.3 修改 mike2410 目录下的 Makefile 文件 3.建立配置头文件
在 include/configs 目录下建立 mike2410.h 头文件,建立方法如下:
# cd include/configs
# cp -fr smdk2410.h mike2410.h 4.指定交叉编译器的路径
在/etc/bashrc 文件中添加下面一行来指定交叉编译器的路径,这个路径不是绝对的,要 根据交叉编译工具链所放的位置决定。
export PATH=/opt/crosstool/gcc-3.3.6-glibc-2.3.2/arm-linux/bin:$PATH 5.测试编译
这 个步 骤的 目的 一是 为了检 查交 叉编 译器 是否正常 工作 ,二 是为 了测 试新建 的 mike2410 配置项是否能够正常编译,具体操作如下:
# cd u-boot-1.1.6
# make mike2410_config
# make CROSS_COMPILE=arm-linux-
如果编译正确,将在 u-boot-1.1.6 目录下生成 u-boot、u-boot.bin 和 u-boot.srec 三个映像 文件。其中:u-boot 是 ELF 格式二进制的 image 文件,u-boot.bin 是原始的二进制 image 文 件,u-boot.srec 是 Motorola S-Record 格式的 image 文件。到这一步说明建立好了 mike2410 的 U-Boot 编译项,但是具体的实现部分还需要修改,因为现在的实现代码还是完全和 smdk2410 开发板一样的,而不能工作在 mike2410 开发板上,接下来的步骤就是根据 mike2410 开发板的硬件配置和设计要求来进一步移植。
6.修改 start.S 文件
首先在 ldr pc, _start_armboot 一行之前添加下面内容,由于 U-Boot 中没有支持从 NAND Flash 启动,所以将程序自己复制到 DRAM 里面去需要新加代码实现,一般通过
copy_myself 函数来实现,其参考 VIVI 的 copy_myself 代码。
#ifdef CONFIG_S3C2410_NAND_BOOT bl copy_myself
@ jump to ram
ldr r1, =on_the_ram add pc, r1, #0 nop
nop
1: b 1b @ infinite loop on_the_ram:
#endif
然后在_start_armboot: .word start_armboot 一行之后添加下面内容,该部分的内容基本 上都参考 VIVI 代码实现,这段代码的主要目的是搬运 NAND Flash 数据到 DRAM 里面。
#ifdef CONFIG_S3C2410_NAND_BOOT copy_myself:
mov r10, lr
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
1: add r3, r3, #0x1 cmp r3, #0xa blt 1b
2: ldr r2, [r1, #oNFSTAT] @ wait ready tst r2, #0x1
beq 2b
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer mov fp, #0 @ no previous frame, so fp=0
@ copy vivi to RAM
ldr r0, =UBOOT_RAM_BASE
mov r1, #0x0 mov r2, #0x30000 bl nand_read_ll
tst r0, #0x0
beq ok_nand_read
#ifdef CONFIG_DEBUG_LL bad_nand_read:
ldr r0, STR_FAIL ldr r1, SerBase bl PrintWord
1: b 1b @ infinite loop
#endif
ok_nand_read:
#ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord
#endif
@ verify
mov r0, #0
ldr r1, =UBOOT_RAM_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes go_next:
ldr r3, [r0], #4 ldr r4, [r1], #4
teq r3, r4
bne notmatch subs r2, r2, #4
beq done_nand_read bne go_next
notmatch:
#ifdef CONFIG_DEBUG_LL sub r0, r0, #4
ldr r1, SerBase
bl PrintHexWord ldr r0, STR_FAIL ldr r1, SerBase
bl PrintWord
#endif
1: b 1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord
#endif
mov pc, r10
@ clear memory
@ r0: start address
@ r1: length mem_clear:
mov r2, #0 mov r3, r2
mov r4, r2 mov r5, r2 mov r6, r2 mov r7, r2 mov r8, r2 mov r9, r2
clear_loop:
stmia r0!, {r2-r9}
subs r1, r1, #(8 * 4) bne clear_loop mov pc, lr
#endif @ CONFIG_S3C2410_NAND_BOOT
接着在 start.S 文件最后添加下面几行的内容,用于定义栈地址变量。到这里 start.S 文 件已经修改完毕,接着添加 NAND Flash 读函数。
#ifdef CONFIG_S3C2410_NAND_BOOT .align 2
DW_STACK_START:
.word STACK_BASE+STACK_SIZE-4
#endif
7.添加 nand_read.c 和 nandflash.h 源文件
在 start.S 文件中调用了 nand_read_ll 函数,该函数用于 NAND Flash 读操作,在 U-Boot 中没有定义,需要新加该函数的实现,该函数的实现可以参考 VIVI 源代码,在 board/mike2410 目录下新建 nand_read.c 源文件,文件内容下:
#include <config.h>
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASE 0x4e000000
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCMD __REGb(NF_BASE + 0x4)
#define NFADDR __REGb(NF_BASE + 0x8)
#define NFDATA __REGb(NF_BASE + 0xc)
#define NFSTAT __REGb(NF_BASE + 0x10)
#define BUSY 1
inline void wait_idle(void) { int i;
while(!(NFSTAT & BUSY)) for(i=0; i<10; i++);
}
#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) {
int i, j;
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { return -1; /* invalid alignment */
}
/* chip Enable */
NFCONF &= ~0x800;
for(i=0; i<10; i++);
for(i=start_addr; i < (start_addr + size);) { /* READ0 */
NFCMD = 0;
/* Write Address */
NFADDR = i & 0xff;
NFADDR = (i >> 9) & 0xff;
NFADDR = (i >> 17) & 0xff;
NFADDR = (i >> 25) & 0xff;
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++, i++) { *buf = (NFDATA & 0xff);
buf++;
} }
/* chip Disable */
NFCONF |= 0x800; /* chip disable */
return 0;
}
新建完 nand_read.c 文件后,需要修改相同目录下的 Makefile 文件,否则编译会出错,
修改内容为:
将 COBJS := mike2410.o flash.o
改为:COBJS := mike2410.o flash.o nand_read.o
同时需要在 board/mike2410 目录下添加 nandflash.h 文件,该文件主要定义了 NAND Flash 的一些芯片配置函数,具体代码如下:
#include <s3c2410.h>
#if (CONFIG_COMMANDS & CFG_CMD_NAND) typedef enum {
NFCE_LOW, NFCE_HIGH } NFCE_STATE;
static inline void NF_Conf(u16 conf) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCONF = conf;
}
static inline void NF_Cmd(u8 cmd) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCMD = cmd;
}
static inline void NF_CmdW(u8 cmd) {
NF_Cmd(cmd);
udelay(1);
}
static inline void NF_Addr(u8 addr) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFADDR = addr;
}
static inline void NF_SetCE(NFCE_STATE s) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
switch (s) {
case NFCE_LOW:
nand->NFCONF &= ~(1<<11);
break;
case NFCE_HIGH:
nand->NFCONF |= (1<<11);
break;
} }
static inline void NF_WaitRB(void) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
while (!(nand->NFSTAT & (1<<0)));
}
static inline void NF_Write(u8 data) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFDATA = data;
}
static inline u8 NF_Read(void) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
return(nand->NFDATA);
}
static inline void NF_Init_ECC(void) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCONF |= (1<<12);
}
static inline u32 NF_Read_ECC(void) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
return(nand->NFECC);
}
#endif
8.修改 mike2410.c 文件
修改这个文件的主要目的有两个,一是初始化 CPU 相关寄存器来支持 USB 主、从设备;
二是初始化 NAND Flash 设备。添加代码如下:
#include "nandflash.h" //添加该头文件
int board_init (void) {
……
/* 根据 S3C2410 用户手册,设置相应的寄存器值来支持 USB*/
gpio->MISCCR |= (1<<3);
gpio->MISCCR &= ~((1<<12)|(1<<13));
……
}
/* 添加以下代码实现 NAND flash 的初始化*/
#if (CONFIG_COMMANDS & CFG_CMD_NAND) extern ulong nand_probe(ulong physadr);
static inline void NF_Reset(void) {
int i;
NF_SetCE(NFCE_LOW);
NF_Cmd(0xFF); /* reset command */
for(i = 0; i < 10; i++); /* tWB = 100ns. */
NF_WaitRB(); /* wait 200~500us; */
NF_SetCE(NFCE_HIGH);
}
static inline void NF_Init(void) {
#if 0 /* a little bit too optimistic */
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
#else
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
#endif
NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TW RPH1<<0));
NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TW RPH1<<0));
}
void nand_init(void) {
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
NF_Init();
#ifdef DEBUG
printf("NAND flash probing at 0x%.8lX\n", (ulong)nand);
#endif
printf ("%4lu KB\n", nand_probe((ulong)nand) >> 10);
}
#endif
9.修改头文件 mike2410.h
修改 include/configs/ mike2410.h 文件,在该文件中添加如下内容,其中定义了栈的基地 址和、栈的大小、RAM 的基地址以及定义 NAND Flash 设置参数等内容。
……
#define CONFIG_CMDLINE_TAG 1
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_INITRD_TAG 1
……
#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \ CFG_CMD_CACHE | \ CFG_CMD_ENV | \ CFG_CMD_PING | \ CFG_CMD_NAND | \ /*CFG_CMD_EEPROM |*/ \ /*CFG_CMD_I2C |*/ \ CFG_CMD_REGINFO | \ CFG_CMD_ELF)
……
/* Nandflash Boot */
#define CONFIG_S3C2410_NAND_BOOT 1
#define STACK_BASE 0x33ff8000
#define STACK_SIZE 0x8000
#define UBOOT_RAM_BASE 0x33f00000
/* NAND Flash Controller */
#define NAND_CTL_BASE 0x4E000000
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
#define CFG_MAX_NAND_DEVICE 1 /* Max number of NAND devices */
#define SECTORSIZE 512
#define ADDR_COLUMN 1
#define ADDR_PAGE 2
#define ADDR_COLUMN_PAGE 3
#define NAND_ChipID_UNKNOWN 0x00
#define NAND_MAX_FLOORS 1
#define NAND_MAX_CHIPS 1
#define NAND_WAIT_READY(nand) NF_WaitRB()
#define NAND_DISABLE_CE(nand) NF_SetCE(NFCE_HIGH)
#define NAND_ENABLE_CE(nand) NF_SetCE(NFCE_LOW)
#define WRITE_NAND_COMMAND(d, adr) NF_Cmd(d)
#define WRITE_NAND_COMMANDW(d, adr) NF_CmdW(d)
#define WRITE_NAND_ADDRESS(d, adr) NF_Addr(d)
#define WRITE_NAND(d, adr) NF_Write(d)
#define READ_NAND(adr) NF_Read()
/* the following functions are NOP's because S3C24X0 handles this in hardware */
#define NAND_CTL_CLRALE(nandptr)
#define NAND_CTL_SETALE(nandptr)
#define NAND_CTL_CLRCLE(nandptr)
#define NAND_CTL_SETCLE(nandptr)
#define CONFIG_MTD_NAND_VERIFY_WRITE 1
#define CONFIG_MTD_NAND_ECC_JFFS2 1
#endif /* CONFIG_COMMANDS & CFG_CMD_NAND */
然后修改以下各宏的值,包括内存相关的地址、启动提示字母和网络的设置。
#define CFG_MEMTEST_END 0x33F00000 /* 64 MB in DRAM */
#define CFG_LOAD_ADDR 0x30008000 /* default load address */
#define PHYS_SDRAM_1_SIZE 0x04000000 /*64 MB */
#define CFG_PROMPT "MIKE2410 # " /* Monitor Command Prompt */
#define CONFIG_IPADDR 192.168.1.10
#define CONFIG_SERVERIP 192.168.1.1
#define CFG_ENV_IS_IN_NAND 1
#define CFG_ENV_OFFSET 0x30000 10.修改 cmd_nand.c 文件
cmd_nand.c 文件在 commom 目录下,修改该文件的目的就是添加包含 nandflash.h 的头 文件,因为如果没有包含该头文件,编译时将出错如图 3.4 所示,正是因为该文件调用了 nandflash.h 头文件中的变量而没有包含该头文件引起的,包含该头文件后将编译正确。
图 3.4 没有包含 nandflash.h 引起的错误 11.重新编译
如果以上步骤都正确的话,执行下面的编译命令,将会在 U-Boot 的根目录下重新生成 u-boot.bin 可执行性文件。
# make all ARCH=arm CROSS_COMPILE = arm-linux- 12.用 Jtag 烧写 u-boot.bin 文件
用开发板上自带的 Jtage 线和开发板连接,使用三星公司提供的 SJF(Sec Jtag Flash)工具 将上面生成的 u-boot.bin 文件烧写到开发板上,烧写过程如图 3.5 所示。
图 3.5 用 SJF 烧写 U-Boot 的过程
正确烧写完 u-boot.bin 文件后,运行超级终端或 DNW 软件,DNW 是三星公司提供的 一个使用方便的串口调试软件,断开 Jtag 线,连上串口线,给开发板上电,将在屏幕中显 示如下信息,表明 U-Boot 正常启动了。
U-Boot 1.1.3 (Jan 21 2007 - 20:26:48)
U-Boot code: 33F80000 -> 33F99A28 BSS: -> 33F9DD4C RAM Configuration:
Bank #0: 30000000 64 MB Flash: 512 kB
NAND:65536 KB
*** Warning - bad CRC or NAND, using default environment In: serial
Out: serial Err: serial MIKE2410#
U-Boot 提供了许多命令,通常使用 help 或“?”来查看 U-Boot 的所有命令,查看结果 如下。
MIKE2410# help
? - alias for 'help' autoscr - run script from memory base - print or set address offset bdinfo - print Board Info structure boot - boot default, i.e., run 'bootcmd' bootd - boot default, i.e., run 'bootcmd' bootelf - Boot from an ELF image in memory bootm - boot application image from memory
bootp - boot image via network using BootP/TFTP protocol bootvx - Boot vxWorks from an ELF image
cmp - memory compare
coninfo - print console devices and information cp - memory copy
crc32 - checksum calculation dcache - enable or disable data cache echo - echo args to console erase - erase FLASH memory
flinfo - print FLASH memory information go - start application at address 'addr' help - print online help
icache - enable or disable instruction cache
iminfo - print header information for application image imls - list all images found in flash
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
nfs - boot image via network using NFS protocol nm - memory modify (constant address) ping - send ICMP ECHO_REQUEST to network host printenv- print environment variables
protect - enable or disable FLASH write protection
rarpboot- boot image via network using RARP/TFTP protocol reset - Perform RESET of the CPU
run - run commands in an environment variable saveenv - save environment variables to persistent storage setenv - set environment variables
sleep - delay execution for some time
tftpboot- boot image via network using TFTP protocol version - print monitor version
以下将介绍 U-Boot 常用命令:
boot_params = 0x30000100 //启动引导参数
3.tftp(tftpboot):利用 tftp 协议将 PC 本地上的映象文件下载到指定地址的 SDRAM 中,
执行该命令的前提是所用 PC 上已经安装了 tftp 服务。具体使用方法如下:
MIKE2410# tftp 0x30008000 zImage
TFTP from server 192.168.1.5; our IP address is 192.168.1.10 Filename 'zImage'.
Load address: 0x30008000
Loading: #################################################################
#################################################################
############################################
done
Bytes transferred = 890752 (d9780 hex)
其中,0x30008000 为指定下载到 SDRAM 的地址,zImage 为下载映象的文件名。
4.bootm:从内核的入口地址引导内核。具体使用方法如下:
MIKE2410# bootm 0x30008000
## Booting image at 30008000 ...
Starting kernel ...
Uncompressing Linux...done, booting the kernel.
5.go:直接跳转到可执行性文件的入口地址,执行可执行性文件。使用方法如下:
5.go:直接跳转到可执行性文件的入口地址,执行可执行性文件。使用方法如下: