在at91rm9200上移植u-boot
0赞1 前言:
最近写驱动写应用感觉有点乏味了,像玩点新鲜的,于是我拿手里的那块H9200E开发板玩玩移植UBOOT,参考开发板提供的uboot的源码我移植了一个ubot-1.1.1。
目的是通过移植了解掌握atmel 的at91rm9200的启动流程个uboot的启动流程和移植要点及其方法。我把在移植uboot过程中遇到的问题记录下来,希望能帮助自己在以后的移植过程中更快的找到方向,也希望能帮助初学的朋友少走些弯路。
开发板的硬件配置
SDRAM 32M
NORFLASH 4M (atmel的AT49BV322A)
NANDFLASH 64M (SAMSUNG的K9F1208U0B)
2 移植思路
我认为要想真正的理解和成功的移植uboot得先弄明白at91rm9200的启动流程和ubot的启动方法
at91rm9200有片内引导和片外引导2 种启动方式,由一根跳线控制。上电MCU检测BMS的电平,如果是高则选择片内ROM启动, 如果是低则从外部flash启动。
(1) 片外引导 执行烧在flash上的引导程序。
(2) 片内引导 at91rm9200内部本身有128k的片内rom,其固化了一个bootloader和uploader,片内引导时启动uploader,uploader开启xmodem协议,等待用户上传程序,上传的程序将载入片内SRAM,重映射,然后pc跳转到片内SRAM执行上传的用户程序。注:片内SRAM只有16k,除去3-4k片内启动程序的占用的部分数据空间,因此下载的程序大小限制在12k内。
官方at91rm9200DK u-boot Flash Programming Solutions文档提供的解决方案如下。
开发板flash上没有引导程序,于是只能用片内引导方式,载入一个12k以内的小程序到内部SRAM运行,而这个小程序初始化SDRAM后,再把u-boot下载到SDRAM运行(u-boot大于12k),pc跳到SDRAM的u-boot位置运行u-boot,u-boot启动后再用u-boot自己的flash烧写命令把自己烧到flash去,以后就可以片外flash启动了。官方文档中并不是直接烧u-boot.bin,而是烧入了boot.bin和u-boot.gz2个文件。
Loader.bin 和boot.bin我暂时用的是板子提供的修改好的,这里重点先研究研究uboot的启动和移植。不过我还是了解了一下它们的启动流程,以便更好的去移植uboot。
loader.bin, boot.bin, u-boot.bin代码执行流分析.
以上三个文件时at91rm9200启动所需要的三个bin,他们的实现代码并不难。如果是你是采用at91rm9200的评估版,应该能得到其源码。
loader.bin 执行流程,这个文件主要在片内启动从串口下载代码时会用到
loader/entry.S 初始化CPU
b main --->crt0.S
--> copydata --> clearbss --> b boot
main.c --> boot -->
/*Get internel rom service address*/
/* Init of ROM services structure */
pAT91 = AT91C_ROM_BOOT_ADDRESS;
/* Xmodem Initialization */
--> pAT91->OpenSBuffer
--> pAT91->OpenSvcXmodem
/* System Timer initialization */
---> AT91F_AIC_ConfigureIt
/* Enable ST interrupt */
AT91F_AIC_EnableIt
AT91F_DBGU_Printk("XMODEM: Download U-BOOT ");
Jump.S
// Jump to Uboot BaseAddr exec
Jump((unsigned int)AT91C_UBOOT_BASE_ADDRESS)
boot.bin执行流程 该文件会在从片内启动时被下载到板子上,以后还会被烧写到片外Flash中,以便在片外启动时用它来引导并解压u-boot.bin.gz,并跳转到u-boot来执行。
boot/entry.S
b main --> crt0.S--> copydata --> clearbss --> b boot
boot/misc.s/* unzip uboot.bin.gz */
----> decompress_image(SRC,DST,LEN) --> gunzip
//jump to ubootBaseAddr exec 这里跳转到解压u-boot.bin.gz的地址处直接开始执行u-boot
asm("mov pc,%0" : : "r" (DST));
u-boot.bin执行流程
u-boot/cpu/at91rm9200/start.S
start --->reset
---> copyex ---> cpu_init_crit
---> /* set up the stack */ --> start_armboot
u-boot/lib_arm/board.c
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
checkboard,
NULL,
};
---> start_armboot ---> call init_sequence
---> flash_init --> display_flash_config
---> nand_init ---> AT91F_DataflashInit
---> dataflash_print_info --> env_relocate
---> drv_vfd_init --> devices_init --> jumptable_init
---> console_init_r --> misc_init_r --> enable_interrupts
---> cs8900_get_enetaddr --> board_post_init -->
u-boot/common/main.c
for (;;)
{ /* shell parser */
main_loop () --> u_boot_hush_start --> readline
--> abortboot
-->printf("Hit any key to stop autoboot: %2d ", bootdelay);
}
以上是at91rm9200启动并进入u-boot的执行流分析。后面u-boot还会将uImage解压到特定的位置并开始启动内核代码。
3 修改loader boot
3.1 修改Loader.bin的源码
1. 修改include/main.h
#define AT91C_UBOOT_BASE_ADDRESS 0x21f00000
此开发板SDRAM为32M地址空间为0x20000000—0x22000000 uboot被解压缩后要被拷贝到RAM的高端地址,防治 与其他地址冲突
2. 修改init.c中对SDRAM的初始化
AT91C_BASE_SDRC->SDRC_MR=0x02
根据具体的SDRAM芯片修改: MR[4]=1(表示16位)
MR[4]=0 (表示32位)
3.2 修改Boot.bin的源码
1. 在main.c中添加两个外部函数的定义
Extern int deampress_image(void *src,void *dst,unsigned int len); 解压缩函数,用来解压缩UBOOT
Extern void Jump() 跳转函数
2. 在main.c中修改宏定义
#define SRC 0x10010000 uboot在flash中的基地址
#define DST 0x21f00000 解压缩UBOOT后载入到SDRAM中的地址
#define LEN 0x20000 uboot的大小(最大大小)
3 修改main.c中打印语句
AT91F_DBGU_Pringk() ………
AT91F_DBGU_Pringk() ………
AT91F_DBGU_Pringk() ………
……
…
修改成自己希望的开机文字图样
4 动手移植uboot
4.1 从网上下载官方的ubot-1.1.1
http://sourceforge.net/project/showfiles.php?group_id=65938&package_id=63695&release_id=233851
4.2 在/board 下建立myboard文件夹,从/board/at91rm9200dk/..下拷贝文件。
At91rm9200dk.c flash.c config.mk makefile u-boot.lds
4.2.1 将at91rm9200dk.c改名为myboard.c,修改其部分代码。由于这个开发板支持nandflash所以需要修改myboard.c下 关于nandflash初始化的部分,nand_init().
具体修改如下:
将 *AT91C_PIOC_ASR = AT91C_PC0_BFCK | AT91C_PC1_BFRDY_SMOE |
AT91C_PC3_BFBAA_SMWE;
改为 *AT91C_PIOC_ASR = AT91C_PC1_BFRDY_SMOE |
AT91C_PC3_BFBAA_SMWE; //AT91C_PC0_BFCK
将 *AT91C_PIOC_PDR = AT91C_PC0_BFCK | AT91C_PC1_BFRDY_SMOE |
AT91C_PC3_BFBAA_SMWE;
改为 *AT91C_PIOC_PDR = AT91C_PC1_BFRDY_SMOE |
AT91C_PC3_BFBAA_SMWE;
增加两句
*AT91C_PIOC_PER|= AT91C_PC0_BFCK;
*AT91C_PIOC_OER = AT91C_PC0_BFCK;
注:AT91C_PC0_BFCK 是Brust Flash控制器的时钟信号线 。
AT91C_PC1_BFRDY_SMOE 是Brust Flash 控制器的读信号。
AT91C_PC3_BFBAA_SMWE 是Brust Flash 控制器的脉冲前沿地址。
AT91C_PIOC_ASR是外设A寄存器。
AT91C_PIOC_PDR 是GPIO 禁用寄存器。
AT91C_PIOC_PER是GPIO 使能寄存器。
AT91C_PIOC_OER是GPIO 使能输出寄存器。
上述修改的目的是,使能BFCK信号线的GPIO输出。
在/*Configure PC2 as input (signal READY of the SmartMedia)*/后增加
*AT91C_PMC_PCER|=(unsigned int)(1<<4);此句。目的:开启外设时钟。
4.2.2 修改flash.c
根据不同的flash芯片采用不同的flash程序接口,本开发板选用的是atmel的AT49BV322A。由于芯片不同所以需要根据datasheet对flash.c进行修改。
① 修改扇区大小和数目的定义
OrgDef OrgAT49BV16x4A[] =
{
{ 8, 8*1024 }, /* 8 * 8kBytes sectors */
{ 31, 64*1024 } /* 31 * 64kBytes sectors */
};
改为
OrgDef OrgAT49BV16x4A[]=
{
{ 8, 8*1024 }, /* 8 * 64kBytes sectors */
{ 63, 64*1024 } /* 63 *8kBytes sectors */
};
本芯片为71个扇区。
② 根据datasheet修改关于地址和读写命令的宏定义。
#define FLASH_BANK_SIZE 0x200000 /* 2 MB */
改为 #define FLASH_BANK_SIZE 0x400000 /* 4 MB */
/* AT49BV1614A Codes */
#define FLASH_CODE1 0xAA
#define FLASH_CODE2 0x55
#define ID_IN_CODE 0x90
#define ID_OUT_CODE 0xF0
#define CMD_READ_ARRAY 0x00F0
#define CMD_UNLOCK1 0x00AA
#define CMD_UNLOCK2 0x0055
#define CMD_ERASE_SETUP 0x0080
#define CMD_ERASE_CONFIRM 0x0030
#define CMD_PROGRAM 0x00A0
#define CMD_UNLOCK_BYPASS 0x0020
#define MEM_FLASH_ADDR1 (*(volatile u16 *)(CFG_FLASH_BASE + (0x00005555<<1)))
#define MEM_FLASH_ADDR2 (*(volatile u16 *)(CFG_FLASH_BASE + (0x00002AAA<<1)))
#define IDENT_FLASH_ADDR1(*(volatile u16 *)(CFG_FLASH_BASE + (0x0000555<<1)))
#define IDENT_FLASH_ADDR2(*(volatile u16 *)(CFG_FLASH_BASE + (0x0000AAA<<1)))
修改为:
/* MBM29LV320BE Codes */
#define FLASH_CODE1 0x00AA00aa
#define FLASH_CODE2 0x00550055
#define ID_IN_CODE 0x00900090
#define ID_OUT_CODE 0x00F000f0
#define CMD_READ_ARRAY 0x00F000F0
#define CMD_UNLOCK1 0x00AA
#define CMD_UNLOCK2 0x0055
#define CMD_ERASE_SETUP 0x0080
#define CMD_ERASE_CONFIRM0x0030
#define CMD_PROGRAM 0x00A0
#define CMD_UNLOCK_BYPASS0x00200020
#define MEM_FLASH_ADDR1 (*(volatile u16 *)(CFG_FLASH_BASE + (0x0000555<<1)))
#define MEM_FLASH_ADDR2 (*(volatile u16 *)(CFG_FLASH_BASE + (0x00002AA<<1)))
#define IDENT_FLASH_ADDR1(*(volatile u32 *)(CFG_FLASH_BASE + (0x0000555<<2)))
#define IDENT_FLASH_ADDR2(*(volatile u32 *)(CFG_FLASH_BASE + (0x0000AAA<<2)))
③ 去掉芯片自动识别,防治出以外错误。
注视掉if ((flash_info[i].flash_id & FLASH_TYPEMASK)……到else 144行-152行。
4.2.3 修改config.mk
将 TEXT_BASE = 0x21f80000 修改为 TEXT_BASE = 0x21f00000
这个地址是要根一级boot中定义保持一致。
Boot中的main.c
#define DST 0x21f00000 //UBOOT被解压缩后载入到SDRAM中的位置。
4.2.4 修改makefile
将 OBJS := at91rm920dk.oflash.o
修改为 OBJS := myboard.o flash.o 自己命名的开发板的名字
4.3 修改include下面的头文件
⑴ 在/include/configs/下建立myboard.h ,可以复制at91rm9200dk.h改名。
具体注意修改如下:
#define CONFIG_MYBOARD1 //目标板
#define CONFIG_NR_DRAM_BANKS 1 //SDRAM bank数
#define PHYS_SDRAM 0x20000000 //基地址
#define PHYS_SDRAM_SIZE 0x2000000 //大小 32M
#define PHYS_FLASH_1 0x10000000 //基地址
#define PHYS_FLASH_SIZE 0x400000 //大小4M
#define CFG_FLASH_BASE PHYS_FLASH_1 //别名
#define CFG_MAX_FLASH_BANKS 1 //总bank数
#define CFG_MAX_FLASH_SECT 71 //扇区总数
根据ubot-1.1.1的/include/configs/at91rm9200dk.h修改的比较少,大部分是修改容量和扇区总数及其基地址。
⑵ 修改/include/cmd_confdefs.h
修改 #define CFG_CMD_NONSTD (………
…)
这个宏定义是决定ubot支持的功能,如果需要支持就从括号中删除此行。利于源码支持 CFG_CMD_LOADB
CFG_CMD_MEMORY 等。我们这里的修改是删除 “CFG_CMD_NAND | \ ” 这行,是uboot支持nandflash。
4.4 修改根目录下的MAKEALL 和Makefile
⑴ 修改MAKEALL:
在 LIST_ARM9=" \
at91rm9200dk integratorcp integratorap\
omap1510inn omap1610h2omap1610inn \
smdk2400 smdk2410 trab \
VCMA9 versatile myboard \" 中加入对目标板的定义。
⑵ 修改Makefile
修改当前交叉编译器的绝对路径:
ifeq ($(ARCH),arm)
CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-
添加目标板配置选项
#########################################################################
## AT91RM9200 Systems
#########################################################################
at91rm9200dk_config : unconfig
@./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk
myboard_config : unconfig
@./mkconfig $(@:_config=) arm at91rm9200 myboard
5 编译,调试
至此,uboot的修改基本完成,让我们编译下看看吧。
在纯linux环境下编译(不要在VM里linux和windows的共享目录里编译)
进入uboot的根目录,键入如下命令
make clean (清除过程文件)
make myboard_config (生成目标板的配置文件)
make all (开始编译)
我等,我等,我等等等~!
啊~!出问题了
从错误中看是提示未定义。
好吧,那就找错误。这时候就体现出Source Insight的优势了,全工程搜索,快捷的查找任意的函数或者宏的定义位置。
最后在/include/common.h中添加
#include <asm/arch-at91rm9200/AT91RM9200.h>包含与硬件相关的寄存器的定义,这个跟uboot版本有关系,比如uboot-1.0.0的include/common.h里就有这句话。
好了再编译一次吧……
Make clean
Make all
VM虚拟机编译还真是不快,不过这次没出问题,成功了。
编译结束后在根目录下会生成u-boot.bin 大概104k
然后将其压缩为gz格式的
gzip –c u-boot.bin > u-boot.gz
因为一级boot中的解压缩函数是解压缩的gz格式然后拷贝到RAM的高端地址中去的。