9200M开发板实验文档
U-boot基于H9200M开发板的移植: 我们的移植属于板级上的移植,也就是说我们所用的处理器已被uboot支持,要让处理器能运行uboot就属于板级移植。板级移植需要在 uboot源码/board/中建立一个相应目标板的目录,再在其中建立相应的flash.c、at91rm9200dk.c、at45.c和链接描述文 档u-boot.lds和Makefile、config.mk就可以了。板级移植还包括环境变量设置等内容。
具体步骤:
由于我们的开发板与uboot现已支持的AT91RM9200DK十分相似,因此上述的文件不需要全部自己重新编写,只要copy过来稍作修改便可正常运行,所以:
1、 在board目录中建立H9200M目录 ;
2、 把board/at91rm9200dk/* copy到我们刚才所建的目录中;
3、 由于H9200M的NORFLASH与AT91RM9200DK板的有所不同,我们的是富士通公司产的FLASH芯片,4MB容量,因此需要修改H9200M中的flash.c文件,实际就是修改FLASH的驱动。
A、 在原有的数据结构数组下添加下面一块富士通FLASH的数据结构
查29LV320BE的手册得知,该芯片有8个8KB和63个64KB段
OrgDef OrgMBM29LV320BE[] =
{
{ 8, 8*1024 }, /* 8 * 8 kBytes sectors */
{ 63, 64*1024 }, /* 63 * 64 kBytes sectors */
};
B、添加一宏定义
# define FLASH_READ_RESET 0xF0 //定义RESET命令的宏
C、在flash_identification FLASH芯片识别函数中
29LV320BE手册建议进入AUTOSELECT模式时,最后先RESET芯片,确保芯片在读模式
1)、添加
/* Read Reset */
MEM_FLASH_ADDR1 = FLASH_CODE1;
MEM_FLASH_ADDR2 = FLASH_CODE2;
MEM_FLASH_ADDR1 = FLASH_READ_RESET;
由于芯片的操作是实际使用的芯片密切相关的,所以要配置相应芯片的信息
2)、把供应商改为富士通
/* Vendor type */
info->flash_id = FUJ_MANUFACT & FLASH_VENDMASK;
printf ("FUJITSU and AMD: ");
3)、由于我们已清楚H9200M所使用的FLASH型号,因此可把原来flash.c中自动判断是什么型号的芯片那段程序去掉,直接改为
/* AMD Flash */
info->flash_id |= AMD_ID_LV320B & FLASH_TYPEMASK; // flash_id = 0x000422F9
printf ("MBM29LV320B (32Mit, bottom boot sect)/n");
D、在flash_print_info FLASH芯片信息显示函数中
1)、把信息改为富士通
case (FUJ_MANUFACT & FLASH_VENDMASK):
printf ("FUJITSU and AMD: ");
break;
2)、同样我们不需判断芯片型号因此把判断的语句去掉,改为
switch (info->flash_id & FLASH_TYPEMASK) {
case (AMD_ID_LV320B & FLASH_TYPEMASK):
printf ("MBM29LV320BE (32Mit, bottom boot sect)/n");
break;
default:
printf ("Unknown Chip Type/n");
return;
}
E、在flash_erase FLASH擦除函数中
1)、把生产商改为富士通,要不然无法对芯片进行擦除
if ((info->flash_id & FLASH_VENDMASK) != (FUJ_MANUFACT & FLASH_VENDMASK)) {
return ERR_UNKNOWN_FLASH_VENDOR;
}
4、在uboot根目录下的Makefile中,在at91rm9200dk_config一行下添加上
h9200m_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t h9200m NULL at91rm9200
其中
arm: CPU的架构(ARCH)
arm920t: CPU的类型(CPU),其对应于cpu/arm920t子目录。
h9200m: 开发板的型号(BOARD),对应于board/h9200m目录。
NULL: 开发者/或经销商(vender)。
at91rm9200: 片上系统(SOC),对应于cpu/arm920t/at91rm9200子目录。
5、把board/h9200m/at91rm9200dk.c中
/* arch number of AT91RM9200DK-Board */
gd->bd->bi_arch_number = MACH_TYPE_AT91RM9200;
改为gd->bd->bi_arch_number = MACH_TYPE_AT91RM9200DK; 目的是与linux内核中对应的
目标板的硬件编号相对应
6、最后
把include/configs/at91rm9200dk.h copy一份并改名为h9200m.h修改其中的内容:
# define PLLAR_VAL 0x20263E04 改为 0x2026BE04 我们
设置cpu的工作频率为180MHz
# define SDRAM1 0x20000080 改为 0x20000020 我们
不需要sdram的burst读模式
# else
# define CFG_ENV_ADDR (PHYS_FLASH_1 + 0xe000) 把ENV定义
在FLASHROM最高端 (PHYS_FLASH_1 + 0x3F0000)
# define CFG_ENV_SIZE 0x2000 /*0x8000
*/ 把0x2000改为0x10000由于FLASH高端是每64K为一个sector
# define PHYS_FLASH_SIZE 0x200000 /* 2 megs main flash
*/ 修改flash rom的大小为0x400000 /*
4 megs main flash */ 我们板上的FLASH容量是4MB
在# define CONFIG_BAUDRATE 115200下面添加以下的一些环境变量
# define CONFIG_ETHADDR 00:11:43:43:93:5F
# define CONFIG_IPADDR 192.168.0.139
# define CONFIG_SERVERIP 192.168.0.123
# define CONFIG_BOOTARGS "root=/dev/ram rw
initrd=0x21400000,6000000 ramdisk_size=15360 console=ttyS0,115200 mem=32M"
# define CONFIG_BOOTCOMMAND "run ramdisk;run image;run boot"
# define CONFIG_EXTRA_ENV_SETTINGS /
"image=cp.b 10020000 21000000 13ffff/0" /
"ramdisk=cp.b 10160000 21400000 28ffff/0" /
"boot=bootm/0" /
""
好了,到此为止,我们的uboot就配好了,为了编译出ARM可以识别的指令,我们需要搭建一个交叉编译环境,所谓交叉编译,就是说在一台机器上运 行编译器,但该编译器生产的代码是运行在另外一种架构的机器上的,具体来说就是我们以X86的pc机作为开发主机,用搭建好的交叉编译环境,编译出ARM 可以识别的指令的程序(pc机X86架构,我们的H9200开发板是ARM架构)。由于我们不仅仅只是编译uboot和linux内核,还要编译其他一些 应用软件,而那些应用软件需要其他一些常用的库文件支持,如:libssl库,编译与ssl有关的应用软件时便需要这库文件支持,所以为了后期开发工作的 方便,我们用uboot官方提供的专为ARM架构所用的开发工具包,Embedded Linux Development Kit ,当然我们也可以自己编译一个交叉编译平台,具体请参考 XXXXXXXX文档。下载完开发工具包后,执行./install [-d <dir>] [<cpu_family1>],其中[-d <dir>]为指定要安装的目录, [<cpu_family1>]为所要安装的CPU架构,我们应该选择ARM,输入命令./install [-d <dir>] arm 平台搭建好后,我们还要修改一下linux的环境变量,好让编译时能找到交叉编译时用到的工具,输入export PATH=$PATH: [-d <dir>](开发工具包的安装路径)/usr/bin命令。进入uboot的源码目录,由于uboot在编译时,默认是选择源码目录为代码 输出目录的,因此可定义一个环境变量BUILD_DIR,让它指向想要的uboot代码输出目录,我们假设要输出到/opt/uboot, 因此执行命令,mkdir –p /opt/uboot && export BUILD_DIR=/opt/uboot ,然后便可以开始编译了,首先make distclean ,然后 make h9200m_config ,最后make all 等编译完毕后,在 /opt/uboot目录下便有u-boot.bin、u-boot、u-boot.srec文件,其中u-boot.bin是要烧写进板内 NORFLASH的二进制文件。
把uboot.bin烧写进板内的NORFLASH,让目标板能运行uboot 1、配置终端,好让我们能看到目标板的输出和控制目标板。 输入minicom -s 将光标移到Serial port setup并且回车 在Serial port setup 选项中输入每个选项所列的首字母进行相应选项的修改 Serial Device 设置为/dev/ttyS0 Bps/Par/Bits 设置为115200 8N1 Hardware Flow Control 设置为 No Software Flow Control 设置为 No 最后按ESC返回主菜单 选择Save setup as dfl 保存为默认设置。
2、启动片内ROM的程序,下载Uboot到SDRAM运行 把程序烧写进NORFLASH,有几种方法,用专用的烧写器,但不现实;用JTAG调试器,成本太高;用一个运行在目标板上的烧写程序,这是一个好方法, 但我们该到哪去找这个烧写程序呢?uboot里就有,用我们已编译好的uboot.bin行吗?可以!但是有个前提条件,就是在配置 include/configs/h9200m.h时要定义多一个宏定义: # define CONFIG_SKIP_LOWLEVEL_INIT 1,目的是跳过uboot运行时,对cpu底层的初始化工作,但这样一来问题又出现了,为了要让目标板正常运行起来,一定要进行cpu底层的初始化工作, 我们能定义的那个,跳过对cpu底层的初始化工作的宏,是因为Atmel公司的AT91RM9200的特殊性,该芯片内就集成了Atmel为 at91rm9200的初始化程序,因此可以跳过uboot的对cpu底层的初始化程序,但在我们的H9200M开发板上,Atmel公司的初始化程序最 终目的只是用于使用Xmodem协议下载程序进cpu片内RAM,而片内RAM只有区区的16K,根本容纳不下我们的uboot.bin,怎么办?可以用 厂家提供的loader.bin来把我们的uboot.bin加载到SDRAM中运行,但即使我们的uboot.bin能正常运行也不能烧写进 NORFLASH,因为没有了对cpu底层的初始化工作啊!除非我们做了两个uboot.bin,一个含有底层初始化程序,一个没有,用没有底层初始化程 序的那个,烧写有底层初始化程序的那个,这是调试uboot的好方法,在SDRAM频繁的读写不会有什么损失,但FLASH却不同了,FLASH有擦写寿 命限制,擦多了,FLASH便报废了。(以上描述可能有点绕,慢慢理顺一下吧 ^_^)
但在此我们使用让厂家提供的uboot(偷一懒,毕竟少编译了一个uboot,呵呵!)。在配置好终端后,将H9200M的跳线J100 的2-3短接(选择片内ROM启动),然后复位系统,打开minicom终端,在终端会出现“CCCCCCCC…..”,此时AT91RM9200片内 ROM的程序已开始运行,等待你的下一步操作,按ALT+a,s键,使用Xmodem协议,发送loader.bin文件,然后终端会出现下载Uboot 的提示,下载完毕后会继续出现“CCCCCCCCC………” ,继续使用Xmodem协议,发送uboot.bin文件,此时uboot.bin被发送到系统的SDRAM中,发送完毕后uboot开始运行,显示 >U-Boot的提示符。
3、擦除Flash 在对Flash进行烧写之前,需要将其擦除: Uboot>protect off all ;去掉Flash的扇区写保护 Uboot>erase all ;擦除Flash的所有扇区
4、 烧写uboot.bin到Flash 在Uboot提示符下键入命令: Uboot>tftp 20000000 ;将文件发送到系统的SDRAM中 发送完毕后,会看到实际上传的字节数,记住十六进制表示的那个,键入以下命令: Uboot>cp.b 20000000 10000000 xxxx ;将发送到SDRAM中的数据写入Flash xxxx为刚才的字节数
OK,我们的uboot已经烧写进NORFLASH了,把板的电源拔掉,把跳线J100的1-2短接(选择片外储存器启动),我们的uboot应该可以正常启动了。
接下来便是在开发主机上(PC机)完成内核的编译: 首先当然是下载内核啦,把内核的tar包解压后,打上相应的补丁,然后执行 make at91rm9200dk_defconfig make menuconfig进行内核配置,内核的配置应按照实际的需要进行选择,其中为了调试的方便我们让内核支持NFS文件系统、NFSROOT和BOOTP或DHCP。 配置完后,保存退出,执行make,等内核编译成功后,把内核源码下的vmlinuz文件copy到uboot输出的目录,即$BUILD_DIR处,然后执行 arm-linux-objcopy -O binary vmlinuz linux.bin 把vmlinuz由ELF格式,转换为二进制格式的linux.bin gzip -9 linux.bin 压缩linux内核节省空间,生成linux.bin.gz文件 ./tools/mkimage -A arm -O linux -T kernel -C gzip -a 20008000 -e 20008000 / -n 'Linux-Kernel' -d linux.bin.gz uImage 执行mkimage命令生成uboot能自动加载的镜像文件uImage 我们的linux内核便完成了,把uImage copy到tftpboot目录下(tftpboot是TFTP服务的根目录)
接下来便是制作根文件系统: 由于我们先前安装的Embedded Linux Development Kit 工具包内已集成了一个专为ARM用的根文件系统,所以只需稍作修改即可 1、 打开PC机上的TFTP服务,TFTP服务由xinetd服务控制;
2、 打开PC机上的DHCP服务,其中
option root-path "/xxx/xxx"; <--此处为NFS的输出目录
: filename "/tftpboot/uImage";
打开此服务时得小心,确保在与THIZ内网断开得情况下进行,要不然MIS部找你算帐,可不关我的事啊^_^!!!!
3、 打开PC机上的NFS服务把开发工具目录下的arm目录作为NFS的输出目录;
4、 把PC机上/dev下的设备文件copy到开发工具目录下的arm目录中的dev目录里
cp –aP /dev /* xxx/arm/dev/
5、在xxx/arm/etc目录中执行
touch mtab
把inittab中的默认运行级别改为1,由于板上的资源少,所以什么服务都不要开
cat fstab << EOF
none /proc proc defaults 0 0
none /proc/bus/usb usbfs defaults 0 0
EOF
好了,至此根文件系统也完成了,那就联合调试吧
打开minicom,把H9200M板上跳线J100的1-2短接后,插上电源,看到uboot倒数时按任意键进入命令交换模式,首先得修改某些环境变量 printenv会看到uboot的默认环境变量,大部分都很熟悉,都是我们在include/configs/h9200m.h中定义的 setenv bootargs root=/dev/nfs ip=192.168.0.139 console=/dev/ttyS0,115200 mem=32M rw setenv bootcmd bootp 21000000/;run boot saveenv
RESET目标板,会看到uboot倒数完后便开始从PC机上下载uImage,然后解压linux内核,最后linux内核就运行起来 了,内核运行到挂载根文件系统时,便会发现要挂载的是NFS根文件系统(通过uboot的bootargs变量中的root=/dev/nfs告诉内核 的),于是发出广播,DHCP响应后便传给内核有关的信息,于是内核便知道NFS根文件系统在哪,从而挂载它,一切正常后,我们熟悉的linux命令行就 出现了,OK,大功告成,我们基于H9200M板的Linux操作系统的移植便完毕了。
Linux操作系统是运行起来了,但它什么也没干,岂不太便宜它了,不行,得找些活让它干,做什么好呢?就要它编译一些它自己要运行的应 用软件吧!编译一个Bittorrent的服务端和客户端,Bittorrent服务端需要Python来解析,所以得先编译Python,解压 Python后,执行 ./configure --prefix=/usr –share-library && make && make install经过漫长的等待后,Python便编译且安装完成,执行bittorrent的服务,可以上传文件,BT服务器一切正常;再编译 ctorrent客户端,执行./configure && make ,执行ctorrent文件,能正常下载文件,BT客户端也一切正常。
所有实验都完成了,我们还可以做一个小型的根文件系统,并进一步裁减linux内核,最后把内核和根文件系统都烧写进FLASH,做到这 一步时,你会发现等待uboot烧写大(说大其实也不大,就两三兆的东西)文件是一件多么痛苦的事情啊!!(深刻体会啊!!),你若是一个完美的追求者, 可尝试修改uboot源码下board/h9200m/flash.c的 int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)多字烧写函数,原来的函数是不断地调用write_word (info, wp, data)单字写函数,直到全部烧写完毕为止,但是通过查阅29LV320BE芯片手册,该芯片有快速写功能(Fast Program),快速写模式流程如下:
修改的程序如下:
int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
{
ulong wp, data, result;
int rc;
int cflag, iflag;
int chip1;
/* 检查需要写的目标地址是否字对齐,即地址的bit0必须为0*/
if (addr & 1) {
printf ("unaligned destination not supported/n");
return ERR_ALIGN;
};
/* 同理,检查源地址是否字对齐 */
if ((int) src & 1) {
printf ("unaligned source not supported/n");
return ERR_ALIGN;
};
/*Check if Flash is (sufficiently) erased*/
result = *(volatile u16 *)addr; // return 0xFFFF
data = *(volatile u16 *)src;
if ((result & data) != data)
return ERR_NOT_ERASED;
wp = addr; // destination address
rc = ERR_OK; // initial rc = ERR_OK
if (cnt == 1)
goto
OneByte;
/*
* Disable interrupts which might cause a timeout
* here. Remember that our exception vectors are
* at address 0 in the flash, and we don't want a
* (ticker) exception to happen while the flash
* chip is in programming mode.
*/
cflag = icache_status();
icache_disable();
iflag = disable_interrupts();
/* Set to Fast Mode */
MEM_FLASH_ADDR1 = 0xAA;
MEM_FLASH_ADDR2 = 0x55;
MEM_FLASH_ADDR1 = 0x20;
/* Fast Program Mode */
while (cnt >= 2)
{
data = *((volatile u16 *) src); // get data from source
MEM_FLASH_ADDR1 = 0xA0; // command
*(volatile u16 *)wp = (volatile u16)data; // program
/* arm simple, non interrupt dependent timer */
reset_timer_masked ();
/*wait until flash is ready */
chip1 = 0;
do {
result = *(volatile u16 *)wp;
/* check timeout */
if (get_timer_masked () > CFG_FLASH_ERASE_TOUT) {
chip1 = ERR | TMO;
goto
Quit;
}
if (!chip1 && ((result & 0x80) == (data & 0x80))) // if word finished quit while
chip1 = READY;
} while (!chip1);
/* verify data */
if (*(volatile u16 *)wp != data)
{
chip1 = ERR;
goto
Quit;
}
/* adjust src dst cnt */
src += 2;
wp += 2;
cnt -= 2;
}
Quit:
MEM_FLASH_ADDR1 = 0x90; // reset fast mode
MEM_FLASH_ADDR1 = 0xF0;
if (chip1 == ERR)
rc = ERR_PROG_ERROR;
if (iflag)
enable_interrupts ();
if (cflag)
icache_enable ();
OneByte:
if (cnt == 0) // return if all finished return rc;
else
{
data = (*((volatile u8 *) src)) | (*((volatile u8 *) (wp + 1)) << 8);
if ((rc = write_word (info, wp, data)) != 0) {
return (rc);
}
src += 1;
wp += 1;
cnt -= 1;
}
return rc;
}
试验一下,烧写速度确实快了很多。