1. 来由:
在我搞完 ucos 后 ( 本来我是想学 uclinux 的,不过在对系统一无所知的情况下,还是先学一下 ucos 比较实际 ^_^ ,从中也可以对系统有个清晰的概念 ) ,我意识到要搞系统的话一个功能强大的 bootloader 是必不可少的,而我的板上自带的是 armboot (其实我个人觉得应用在 ARM 上的话 armboot 已经足够了,毕竟 u-boot 也是从 armboot 中发展过来的,纯属个人意见),如果我的板上自带光盘有已经移植好的 armboot 源代码供我参考的话,我想我也不会花这么多时间去搞 u-boot ,可恨的是我的光盘上只有 armboot.bin 这个二进制文件,而没有源文件,没办法,我下定主意自己搞一个,考虑了一番后我选择了 u-boot ,毕竟参考资料相对比较多,再我学完 u-boot 后再回头看 armboot ,简直是一个炉里出的饼,这是后话。
2. 准备:
说是容易,做起来却挺难。因为编译 u-boot 要在 linux 环境下,而不能在我们平时所熟悉的 ads 下那么直观。首先要建立好交叉编译环境,这个交叉编译环境可以自己来做,不过完全没必要,而且难度也挺大,一般是下载人家编译好的工具。我刚开始在这里就郁闷了很久,现在会了以后觉得原来就是这么简单,在些我把方法说清楚,希望不会再有人为这个问题郁闷了 ^_^ :
1) 在网上下载一个 u-boot 源代码,我用的是 1.1.2 版本的,最新的应该是 1.1.4 的吧,其实差不多,那就像我那样下载一个 1.1.2 版本的吧。把源文件解压,这个应该不用说了吧,学过 linux 的人应该会,不会的话我想你继续做下去也困难,那就先装个 linux 用下吧(我用的是 RedHat 的,哦对了,编译程序是需要 gcc 编译器的,所以安装方式一定要选择工作站哦 ^_^ )。好了,解压后你发现在 u-boot.1.1.2 目录下有 Makefile 这个文件吧?让我们看看它里面的内容,最简单的方法就是 vi Makefile 了。我们要看的是它选择的是哪一个交叉编译器。可以看到这一项:
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
看到吧,也就是说这里所用的交叉编译器是 arm-linux-gcc 了,( u-boot 默认是用这个的,也有用 arm-elf-gcc 的,网上有个工具包 arm-elf-tools-20030314.sh, 我试过用它来编译,没有问题,顺便提一下, arm-elf-gcc 是用来编译 uClinux 内核的工具来的)那你就下载一个 arm-linux- 的交叉编译器吧,我是在网上下载 cross-2.95.3.tar.bz2 这个文件,然后解压得到 2.95.3 版本的交叉编译工具的,具体设置如下(参考网上资料):
2 )在宿主机上建立 arm-linux-gcc 交叉编译环境
在 RedHat Linux 系统下以 root 用户登录,
将 cross-2.95.3.tar.bz2 文件复制到 / 目录下,
安装:
# tar jxvf cross-2.95.3.tar.bz2
这个命令会在你的 /usr/local/arm/2.95.3 目录下安装 arm-linux-gcc 交叉编译程序,
然后在 PATH 变量中添加一项: /usr/local/arm/2.95.3/bin.
[root@localhost root]# export ATH=/usr/local/arm/2.95.3/binPATH
或
把 PATH=/usr/local/arm/2.95.3/binPATH 添加到 /ETC/bash_profile 文件中
或者
在 /etc/bashrc 文件中添加一项 :
export PATH=/usr/local/arm/2.95.3/binPATH
测试:
把终端关闭,重新打开后执行如下命令:
# arm-linux-gcc –v
好了,建立好交叉编译环境后可以试着编译 u-boot 了
这里提几个注意点:
1. 不可用 winRAR 解压 u-boot-1.1.2.tar.bz2 或 u-boot-1.1.2.tar.gz 这种文件(就个可能新手会犯,一般熟悉 linux 命令的人应该都不会这样做吧,在此还是要提一下)
2. 可能下载的文件有一些中间文件会阻碍编译的运行,所以在编译前最好来个彻底清除,在 u-boot.1.1.2 目录下运行命令: make distclean (其实这个命令在 Makefile 文件下就有)我当时为这个问题郁闷了很久,希望你们不会像我这样 ^_^ ) ;
3 ,有些人为了方便想在 cygwin 下编译,但是经常在网上看到在这个虚拟平台下编译有很多的问题,要配置的东西也多,而且好像我用过那个 vi 没有 linux 环境下的好用,所以最好还是不要用这个软件了吧,如果你真的离不开 windows 的话可以像我这样装个虚拟机,在虚拟机下再装 linux 的系统,具体参考这个网站): http://fedora.linuxsir.org/doc/vmware/
3 )好了,现在开始测试你的交叉编译器搞好没有。在u-boot.1.1.2目录下执行如下命令:
1) Make distclean ( 再次强调)
2) Make B2_config( 随便再个现成的试试^_^)
3) Make ( 没错的话应该会生成u-boot.bin文件,发生错误的话也不怕,只要细心看一下哪里错就行了,gcc碰到错误后会退出编译,所以可以一个个错误来改,一般的问题都是没找到编译器(可能你没装或者装的不对,例如人家用的是arm-linux-而你装的是arm-elf-,如果你装了的话看看你的环境变量设好了没有,前面有讲,如果不关编译器的事的话那就再看看,一般是文件的后缀不对,有些文件后缀是大写的,例如start.S但是如果你的是start.s小写的话那当然找不到(解决方法很,把它改成大写就行了)。细心看吧,不用怕,它都有注明路径,很容易可以找到的))
如果以上步骤都无误的话那么恭喜你,你的交叉编译环境可以用来编译你的u-boot源代码了,可以开始以下阶段。
3. 移植 :
说时迟那时快,现在开始移植工作(以下是我一步步重新做一遍,力求说得详细点,感谢我吧^_^)
我以B2板子的程序做为模板来做.
#cd u-boot-1.1.2
#cd board
#cp -R dave myboard ( 这是我取的板子名字,可以换上你的,但是后面的也要跟着来换哦 ^_^)
#cd myboard
#mv B2 myboards3c44b0 ( 自己取个板子名 )
#cd myboards3c44b0
# mv B2.c myboards3c44b0.c
修改 myboards3c44b0 里面的 Makefile, 把 B2 改成 myboards3c44b0 ,编译时如果报的其它类似找不到 B2 的错误也是把相应的 B2 改成 myboards3c44b0 来处理。
1 )其中的 myboards3c44b0.c 文件是板的初始化代码,看一下就知道,根据你的板上自带的 44binit.s 来修改吧。 memsetup.S 文件主要是存储器的初始化设置,其实也是 44binit.s 里面的一部分。
2 )其实 board 这里要修改的不多,先跳过 flash 部分吧,我们来看一下 cpu 设置部分吧。
u-boot-1.1.2 里面已经加入了对 s3c44b0 的支持,让我们来看一下 cpu/s3c44b0 里面的部分吧。
看到 start.S 这个文件了吗?要修改的其实并不多,按照你的板设置一下中断跳转矢量就行了,或者上网查一下吧,应该很容易看明的。其中 cpu.c 这个文件简直不用修改,再来看一下 serial.c 这个文件吧,改一下波特率的设置就行,就是你用多少 M 的 CPU 频率的话对应的波特率参数设置问题,其实 B2 已经做得不错的了,很多子程序都不用自己写的了 ^_^ ,在我的板上是这样设置的 , 参考一下吧 :
#if CONFIG_S3C44B0_CLOCK_SPEED==66 ,把所有的这些 66 改成 60 ,原因,我的实验板上用的频率是 60 ( 44B0 最高频率为 64M )。然后其它分频系数,寄存器初始化设置,可以参考一下 44blib.c ,在这里我给出我的设置出来吧,可以参考一下。
case 115200:
#if CONFIG_S3C44B0_CLOCK_SPEED==60
divisor = 32;
UFCON0 = 0x0;
ULCON0 = 0x03;
UCON0 = 0x245;
UBRDIV0 = divisor;
我在这方面算术不好,所以都是参考人家的。
3 )好,再加上个头文件:
cd u-boot-1.1.2/include/configs
cp B2.h myboards3c44b0.h
这个头文件其实要改的地方还挺多的,我等下再说怎样修改。先回到 u-boot.1.1.2 目录,
4 )在 Makefile 里面加上这部分,不会的话就模仿 B2 来写吧 ^_^
在 B2 的这部分文件
B2_config : unconfig
@./mkconfig $(@:_config=) arm s3c44b0 B2 dave
后面加上这部分:
myboards3c44b0_config : unconfig
@./mkconfig $(@:_config=) arm s3c44b0 myboard3c44b0 myboard
切记在 @./mkconfig $(@:_config=) arm s3c44b0 myboard3c44b0 myboard 前面的是 Tab 来的,万万不能用空格代替,因为它是靠这个来识别命令的!
5 )好,现在可以在 u-boot-1.1.2 目录下执行如下命令:
Make distclean (还是用这个吧,比较彻底)
Make myboards3c44b0_config
Make
看看有没有错,有错的话按着提示来改,如果没错的话应该就能生成 u-boot.bin 这个文件,现在还不能用哦,因为这几是 B2 的一个仿制品而已(注:在我的机上到这一步可没有报错哦,你一步步按着来做的话应该也不会有什么错误吧 ^_^ )。
6 )好,现在继续修改 u-boot-1.1.2/include/configs/myboards3c44b0.h 这个文件。
#define CONFIG_INIT_CRITICAL 1 这个在 cpu/s3c44b0/start.S 里面用到,如果你的 u-boot 程序不是在 sdram 中调试而是固化到 flash 中运行的话,这个必不可少。
找到 #define CONFIG_B2 1 把 B2 改成 myboards3c44b0 吧(不然就不会编译你的板了)
找到 #define CONFIG_S3C44B0_CLOCK_SPEED 75 你的 44b0x 应该没有 75M 吧 我的是改成 60 的
找到 Size of malloc() pool 这部分设置,改成这样吧,反正我是照着人家来做的,你自己研究下吧 ^_^ 。
#define CFG_MONITOR_LEN (256 * 1024) /* Reserve 256 kB for Monitor */
#define CFG_ENV_SIZE (64*1024) /* 1024 bytes may be used for env vars*/
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024 )
#define CFG_GBL_DATA_SIZE 128 /* size in bytes reserved for initial data */
#define CFG_ENV_IS_IN_FLASH 1 这个必不可少,如果你想把你的参数保存到 flash 的话(有些板是保存到 EEPRAM 中去的,但是 s3c44b0 的话还是保存到 flash 吧)
#define CFG_ENV_ADDR (PHYS_FLASH_1+0x40000) 这个就是你的参数保存在 flash 里的起始地址了
#define CFG_ENV_OFFSET0x40000 这个我后来看它源程序发现如果你上一步没有设置它的起始地址的话就会用它来作默认地址的了
#define CONFIG_AUTO_COMPLETE
其它地方没有深究哦,有些好像不要也行,你就试试吧。
找到 Hardware drivers 部分,这应该是网络芯片设置吧,参考一下这个吧(要看芯片的):
#define CONFIG_DRIVER_RTL8019 这个就要看你的板上用的是什么网卡了(这个是台湾出的,有 10M )
#define RTL8019_BASE 0x06000000 这个是网卡相就寄存器的起始地址
以下部分我试过不要也行,你试下吧
#define RTL8019_BUS32 0
#define CONFIG_SMC_USE_16_BIT
#undef CONFIG_SHOW_ACTIVITY
#define CONFIG_NET_RETRY_COUNT 10 应该是重试的次数吧
#define CONFIG_BAUDRATE 115200 设置波特率
#define CONFIG_COMMANDS ( CONFIG_CMD_DFL | \
CFG_CMD_DATE | \
CFG_CMD_ELF | \
CFG_CMD_NET | \
CFG_CMD_EEPROM | \
CFG_CMD_I2C | \
CFG_CMD_FAT | \
CFG_CMD_JFFS2)
把 CFG_CMD_EEPROM 改成 CFG_CMD_FLASH 吧,虽然不改也是可以的,具体没考究。
以下是板上 env 参数设置,看一下吧,其实我觉得了解一下就行的了,只是一些初始设置值,以后可以用命令 setenv saveenv 来修改的。
#define CONFIG_BOOTDELAY 3 这个就是运行 bootcmd 之前的等待时间
#define CONFIG_BOOTARGS devfs=mount root=ramfs console=ttyS0,115200 引导 uClinux 的时候传递的参数,不会就先不用管它也行。
#define CONFIG_ETHADDR 00:50:c2:1e:af:fb 网卡的物理地址 MAC
#define CONFIG_NETMASK 255.255.255.0 掩码地址,设置过 ip 的人都应该知道吧
#define CONFIG_IPADDR 192.168.0.30 这是你板上网卡 8019 的 ip 地址
#define CONFIG_SERVERIP 192.168.0.10 这是你宿主机的 ip 地址,以后用 tftp 下载的时候用到,一定要跟你的宿主机一致才行。
#define CFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */
#define CFG_MAXARGS 100 /* max number of command args */
#define CFG_BARGSIZE CFG_CBSIZE/* Boot Argument Buffer Size */
#define CFG_MEMTEST_START 0x0C400000 /* memtest works on */
#define CFG_MEMTEST_END 0x0C800000 /* 4 ... 8 MB in DRAM */
#undef CFG_CLKS_IN_HZ /* everything, incl board info, in Hz */
#define CFG_LOAD_ADDR 0x0c008000 默认的下载地址
#define CFG_HZ 1000 /* 1 kHz */
#define CFG_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } 可供选择的波特率
Physical Memory Map 部分,比较重要,修改你的 sdram 和 flash 的地址和容量。
#define CONFIG_NR_DRAM_BANKS1 我们只占用了一个 Bank 用来映射 sdram
#define PHYS_SDRAM_1 0x0c000000 sdram 的起始地址
#define PHYS_SDRAM_1_SIZE 0x00800000 sdram 的容量( 8M )
#define PHYS_FLASH_1 0x00000000 flash 的起始地址
#define PHYS_FLASH_SIZE flash 的容量( 2M )
#define CFG_FLASH_BASE PHYS_FLASH_1 定义多个名字而已,其它地方会用到
FLASH organization 部分,看注释应该知道了吧,参考一下:
#define CFG_FLASH_ERASE_TOUT 4120000 /* Timeout for Flash Erase (in ms) */
#define CFG_FLASH_WRITE_TOUT 4000 /* Timeout for Flash Write (in ms) */
按着来一步步的做应该不难,这里提一下 vi 程序的用法,其实在 google 一搜就找到了,不过为了方便大家,还是说一声吧,
在命令模式下:
按 / xxx 再回车即可搜索到你所需要的内容( xxx ),再按 n 搜索下一个,按 shift+n 搜索上一个
按 : xxx 即可跟到你想要的行
按 : set nu 显示行号
其它查书都可以找到就不说了。
做到这个时候,你可以再编译一下,看有没有错,这一步我就没有帮你们做了,不过我觉得应该不会有问题吧,下载到 ram 中运行,看看效果。
7 )终于到了 flash 的设置部分了,这完全是我个人悟出来的,网上资料好像讲得不是先清楚。
首先介绍一下 flash 的识别吧,每块 flash 都有一个 id ,前部分用来说明生产厂家,后部分用来说明它的容量,类型,位数等。它们的定义是在 u-boot-1.1.2/include/flash.h 中,就以我的 ssts9vf1601 为例:
#define SST_MANUFACT 0x00BF00BF 这是生产厂家 id
#define SST_ID_xF1601 0x234B234B 这是它的型号,容量,位数等 id
#define FLASH_SST160A 0x0046 这个我还说不清楚,有个要注意的问题是像 sst160 是不能用奇地址的,我做的过程中是把 SST160A 改成 SST1601 的,懒得帮它创一个
好了,相信你已经找到你的板上所用的 flash 对应的 id 号了吧?现在就来谈谈怎么改。
先进入 u-boot-1.1.2/board/myboards3c44b0/common/flash.c
它里面已经帮 SST160A 设置好了,我的做法是把 SST 公司的 160A 都改成 1601 ,是不是很简单,当然你也可以帮 1601 再写一分,但是我是个懒人,所以我就样做了 ^_^
到这里还有个问题没提到的,就是如果你重新用 setenv saveenv 设置了参数,但是复位后会发现怎么没有保存到的(不信你试试看)。其实我是故意留到这里讲的,应该它不会报错,可以说是比较隐秘的问题,所以我故意放到这里来说,以引起你的重视。
解决方法: vi u-boot-1.1.2/board/myboard/common/flash.c
找到这个函数: write_buff
里面有这句话: #ifdef CONFIG_B2
要把 B2 改了,因为我们的板不叫 B2 ,那叫什么呢?原来是在这里定义的: u-boot-1.1.2/include/configs/myboards3c44b0.h
里面有这句话: #define CONFIG_HFRK 你不喜欢 HFRK 这个名字也行,可以改成别的名字,只不过相应地前面的 B2 也要改成这个名字而已。
4. 后记:
本来是想刚做好的时候写的,但是后来发现有些功能实现不了,所以就停住了笔,等我搞好了以后,又急着去学习 uClinux 的移植,真的很忙,现在终于抽空把它写完。虽然有些地方还没说清楚(毕竟我对它的了解还不是很深,但我相信在我以后移植系统的过程中会逐步加深对它的了解),我还是希望它能成为最详尽的 u-boot 移植新手指导,对 u-boot 移植的初学者起到实质性的作用,这样就不枉我花了这么多的心思来写这个文档。