初学嵌入式Linux,感觉需要学习的东西太多了。把学习过程中的收获和问题记录在这里,算是一份经验吧。
前面利用开发板带的现成的东西step by step让Linux 2.4.19在开发板上跑起来了,对于开发的流程也有了一定的认识。现在想对每一步进行详细深入的探讨,好好学习一下,把笔记记录下来,省得忘记了。有些内容是从看过的书中摘要过来的,大部分是自己实践后的心得。我想记得详细点,也好为后面总结打好基础。
先谈一下对于嵌入式开发流程和嵌入式开发环境拓扑结构的认识。
刚开始比较盲目,看得书也不多,现在才算是大体上有了些了解。从开发产品的角度简单的描述如下:
一、嵌入式开发流程简介
假设我们现在进行一项嵌入式开产品的开发,比如说智能电力系统终端,那么我想首先应该对完整的开发流程有一个大致的了解,才不致于在以后的工作中被动。下面对嵌入式linux开发简单的介绍一下。
1、系统的需求分析
2、硬件平台的选择和设计
3、软件开发
(3-1)建立开发环境。
(3-2)引导装载程序。
(3-3)内核裁减与编译。
(3-4)建立文件系统。
(3-5)应用程序开发。
图1
因为我想要做的是软件开发,所以对前两步就不作深入探索了。只想要对软件开发的每一步熟悉起来,让我定制的系统跑得稳定,开发的程序能很好的完成其功能。这是个很艰巨的任务,万里长征刚刚走了第一步:)
二、嵌入式开发环境拓扑结构简介
嵌入式开发环境一般由:宿主机(Linux Server)、工作站、嵌入式目标系统(target board)和将它们连在一起的网络环境。
1、linux server:嵌入式linux内核编译、应用程序编译的公共平台,有单独的一台pc机充当,安装标准的linux操作系统,比如redhat,debian等等。
2、工作站:为普通局域网计算机,以支持小组项目开发。工作站一般安装windows,需要linux服务器时,可以从工作站远程登陆到linux server。
3、target board:这是需要开发的最终产品,可以根据需要与工作站连接(通常通过串口或者usb接口),或连至局域网。
4、工作站需要安装ftp客户端(cuteftp、flashfxp等)和telnet客户端程序(secureCRT等),linux服务器应该开通ftp和telnet服务,还有ssh。
综述过程:开发人员在一台工作站进行操作,通过远程登陆的方式操作linux server,并且使用ftp在linux server和工作站进行文件传输,同时target board需要与网络连接,其串口与工作站的RS232接口连接。使用工作站上的超级终端作为嵌入式目标系统输入/输出端。
对于开发流程有了一定的了解后就有了目标,这样才能够不是太郁闷。
我的开发环境:Windows XP SP2+VMWARE+RedHat 9.0
首先规划一下,我先建立了一个用户armlinux,我的全部工作都是在这个用户根目录下完成。
$pwd
/home/armlinux
$mkdir bootloader debug images software source kernel rootfiles sysapps tmp tools program
$ls
bootloader images program software sysapps tools
debug kernel rootfiles source tmp
然后建立环境变量,直接在.bash_profile中改就可以了。
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
export TARGET=arm-linux
export PRJROOT=/home/armlinux
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=$PATH:$HOME/bin:$PREFIX/bin:/sbin:/usr/sbin:/usr/local/sbin
unset USERNAME
这样在下一次进入此用户时,环境变量就生效了。如果想立刻生效,那么可以用下面的命令:$source .bash_profile。这种方法可以使这些环境变量在进入用户armlinux后就会成效,比较方便。
三、建立交叉编译环境
这一步工作我已经很顺利的完成了。刚开始时,手动建立交叉编译工具链,很困难,出现了很多问题,幸亏网上有不少的资料可以参考。关于手动建立交叉编译工具链的过程我已经做了总结,放到blog上了。现在可以获得已经编译好的工具链,这样比较方便些,毕竟几个小时的手动建立过程太繁琐,太容易出错了。
3.1 获取交叉编译工具链
网站:http://www.arm.linux.org.uk
在linux server上以ftp方式登陆:
#ftp ftp.arm.linux.org.uk
这是系统提示输入用户名和密码,不要随便输,那样可能会连接失败。该ftp站点是允许匿名访问的,所以你可以用下面的用户名:anonymous登陆,密码无,直接回车就可以了。
进入后执行:
ftp>cd pub/armlinux/toolchain/
ftp>bin
ftp>get cross-2.95.3.tar.bz2
ftp>get cross-3.0.tar.bz2
ftp>get cross-3.2.tar.bz2
ftp>get README
ftp>bye
Linux 2.4.xx及其以下的内核源码用2.95.3的交叉编译器来编译就可以了;而2.6.xx的内核源码一般要用到cross-3.x以上的版本来编译。所以我全都下载下来了,备用。若下載速度慢,可選擇在windows下用迅雷下載,速度很快。
要想用更新的版本,则可以到网站ftp://ftp.handhelds.org/projects/toolchain下载,这里可以下在到cross-3.3.2和cross-3.4.1,默认路径是/usr/local/arm/<版本号>。
从手动编译就可以知道,根据环境变量PREFIX指定了安装目标文件夹,那么上面提供的编译好的工具链也必须安装到指定的文件夹才可以使用。可以在README中知道安装方法:
This works for both gcc-2.95.3 and gcc-3.0.
How to install:
cd /usr/local
mkdir arm
cd arm
tar Ixvf cross-.tar.bz2
Add /usr/local/arm//bin to your path to use the cross compiler.
在Linux下面要养成看README、INSTALL文件的习惯,虽然是英文,但是写得都比较具体,比较简洁,没有很困难的。
明确了方法就比较简单了。
#mkdir /usr/local/arm
#cd /usr/local/arm
#tar jxvf cross-2.95.3.tar.bz2
然后添加路径:
$cd
$ls -a
$vi .bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
export TARGET=arm-linux
export PRJROOT=/home/armlinux
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=$PATH:$HOME/bin:$PREFIX/bin:/usr/local/arm/2.95.3/bin:/sbin:/usr/sbin:/usr/local/sbin
unset USERNAME
修改好後,終端執行命令:
$. .bash_profile //讓配置文件生效
看见红色的行就是修改好的。只需要把你的路径添加到后面就可以了。
这样可以验证一下了。
验证:
$cd
$cd program
$vi hello.c
-------------------------------------------
#include
int main()
{
int i;
for(i=1;i<9;i++)
printf("Hello World %d times!\n",i);
}
--------------------------------------------
保存后退出。
$arm-linux-gcc hello.c -o hello-arm
$file hello-arm
hello-arm: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.0.0, dynamically linked (uses shared libs), not stripped
这就说明生成的hello-arm是可以工作在ARM平台上的,也证明了你的交叉编译工具链是有效并且可用的。
在下载的2.95.3的工具链中没有包含调试工具gdb和目标板的gdbserver。在这里一起搭建好。
首先从ftp://ftp.gnu.org/gnu/gdb中获得gdb套件。解压缩关于路径就不多说了。
$cd
$cd debug
$mkdir build-gdb build-gdbserver
$cd
$cd software
$../source/gdb-5.2.1/configure --target=$TARGET --prefix=$PREFIX
$make
$make install
这样就可以顺利的完成gdb-5.2.1的安装了。
要想对目标板进行交叉编译,gdb显得太大了些,所以需要gdbserver。下面建立gdbserver。
$cd
$cd debug/build-gdbserver
$chmod +x ../../source/gdb-5.2.1/gdb/gdbserver/configure
$CC=arm-linux-gcc ../../source/gdb-5.2.1/gdb/gdbserver/configure --host=$TARGET --prefix=$TARGET_PREFIX
$make
$make install
n=`echo gdbserver | sed 's,x,x,'`; \
if [ x$n = x ]; then n=gdbserver; else true; fi; \
/usr/bin/install -c gdbserver /home/armlinux/tools/arm-linux/bin/$n; \
/usr/bin/install -c -m 644 ../../software/gdb-5.2.1/gdb/gdbserver/gdbserver.1 /home/armlinux/tools/arm-linux/man/man1/$n.1
/usr/bin/install: 无法创建一般文件‘/home/armlinux/tools/arm-linux/bin/gdbserver’: 没有那个文件或目录
/usr/bin/install: 无法创建一般文件‘/home/armlinux/tools/arm-linux/man/man1/gdbserver.1’: 没有那个文件或目录
make: *** [install-only] Error 1
此处错误不难理解,主要是因为交叉编译工具放到/usr/local/arm里面了。前面的安装目录tools里面缺少几个文件夹,只需要建立就可以了。
$cd $PRJROOT/tools
$mkdir arm-linux
$cd arm-linux
$mkdir bin man
$cd man
$mkdir man1
$cd $PRJROOT/debug/build-gdbserver
$make install
这样就没有问题了,当然也可以修改makefile,但是自己还没有学好shell语言,所以采取上述方法解决了。这样首先用strip处理一下,目的是gdbserver不需要附带上调试的信息,把它们都剥离去就是了。
$arm-linux-strip $TARGET_PREFIX/bin/gdbserver/gdbserver
$ls -l $TARGET_PREFIX/bin/
总用量 24
-rwxr-xr-x 1 armlinux armlinux 23132 8月 10 11:33 gdbserver
也就是说,gdbserver经过strip处理之后还剩下23KB多一点,已经挺小了,适合目标板了。
附:
在CU论坛上看到tree工具可以查看目录树,觉得不错。也想要安装一个。下面這個站點已經沒有tree-1.5.0.tgz。
有效的下載地址:http://linus.yhspatriot.net/cs/cs/assignments/q1/introToUnix.html
$cd
$cd source
$ftp mama.indstate.edu
Connected to mama.indstate.edu (139.102.70.201).
220 ProFTPD 1.3.0 Server (ProFTPD Default Installation) [139.102.70.201]
Name (mama.indstate.edu:armlinux): anonymous
331 Anonymous login ok, send your complete email address as your password.
Password:
230 Anonymous access granted, restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd linux/tree
250 CWD command successful
ftp> ls
227 Entering Passive Mode (139,102,70,201,149,53).
150 Opening ASCII mode data connection for file list
drwxr-xr-x 2 root root 4096 Jun 4 2001 binary
drwxr-xr-x 2 root root 4096 May 19 1999 slack
-rw-r--r-- 1 root root 406 Oct 11 1996 tree-1.1.lsm
-rw-r--r-- 1 root root 13135 Oct 11 1996 tree-1.1.tgz
-rw-r--r-- 1 root root 405 Jan 6 1997 tree-1.2.lsm
-rw-r--r-- 1 root root 16362 Jan 6 1997 tree-1.2.tgz
-rw-r--r-- 1 root root 436 Feb 21 2002 tree-1.3.lsm
-rw-r--r-- 1 root root 25060 Feb 21 2002 tree-1.3.tgz
-rw-r--r-- 1 root root 432 Feb 21 2002 tree-1.4b1.lsm
-rw-r--r-- 1 root root 27536 Feb 21 2002 tree-1.4b1.tgz
-rw-r--r-- 1 root root 27891 Mar 25 2002 tree-1.4b2.tgz
-rw-r--r-- 1 root root 432 Jun 18 2003 tree-1.4b3.lsm
-rw-r--r-- 1 root root 29366 Feb 6 2003 tree-1.4b3.tgz
-rw-r--r-- 1 root root 466 Aug 20 2004 tree-1.5.0.lsm
-rw-r--r-- 1 root root 26543 Aug 16 2004 tree-1.5.0.tgz
226 Transfer complete.
ftp> get tree-1.5.0.tgz
ftp> bye
221 Goodbye.
在Windows下解壓tree-1.5.0.tgz,將解壓後的文件夾tree-1.5.0拷到/home/zhj1011/software下。
$cd /home/zhj1011/software/tree-1.5.0
$make
$make install
然后就可以了,安装路径查看makefile文件知道在/usr/local/bin,也可以用which查看。现在可以用tree命令来查看一下自己的目录结构了。
示例:
$cd
$tree -L 1 //显示一级目录,当然也可以显示二级目录了。
.
|-- bootloader
|-- debug
|-- images
|-- kernel
|-- program
|-- rootfiles
|-- software
|-- source
|-- sysapps
|-- tmp
`-- tools
这样对自己的组织比较好。等用tree熟悉了,在补充它的用法。
四、引导装载程序bootloader
因为u-boot的功能比较强大,所以选择了u-boot。这几天想先研究研究它的代码,好好分析一下启动过程。原来做过一次,不过是利用原来开发板做好的,熟悉过程是可以,但是不明白原理仍然不行。这次要学习掌握原理部分。
待续。。。。
2006-08-14
一、了解一下存储器的基本分类情况。
存储器的物理实质是一组或多组具备数据输入输出和数据存储功能的集成电路,用于充当设备缓存或保存固定的程序及数据。存储器按存储信息的功能可分为只读存储器ROM(Read Only Memory)和随机存储器RAM(Random Access Memory)。
图1 常见存储器分类
1、 ROM
ROM 中的信息一次写入后只能被读出,而不能被操作者修改或删除,一般由芯片制造商进行掩膜写入信息,价格便宜,适合于大量的应用。一般用于存放固定的程序,如监控程序、汇编程序等,以及存放各种表格。EPROM(Erasable Programmable ROM)和一般的ROM不同点在于它可以用特殊的装置擦除和重写它的内容,一般用于软件的开发过程。
特别介绍:闪存(Flash Memory)
闪速存储器(Flash Memory)又称PEROM(Programmable and Erasable Read Only Memory),是Intel 公司在80 年代末90 年代初推出的,由于它的众多优点而深受用户的青睐。Flash Memory 的两个主要特点是可以按整体/扇区擦除和按字节编程。它是完全非易失的,可以在线写入,并且可以按页连续字节写入,读出速度高。Flash 芯片划分成很多扇区,把一位从0 重置为1 不能通过对该位单独操作来实现,而必须擦除整个扇区。Flash芯片的寿命就用擦除周期来衡量。通常的寿命为每个扇区可擦除100,000 次。为了避免任意一个扇区在其他扇区之前达到这个极限,大多数Flash 芯片用户会尽量保证擦除次数在各扇区之间均匀分布,这一过程称为“磨损均衡”(wear leveling)
2、 RAM
RAM 就是我们平常所说的内存,主要用来存放各种现场的输入、输出数据,中间计算结果,以及与外部存储器交换信息和作堆栈用。它的存储单元根据具体需要可以读出,也可以写入或改写。RAM 只能用于暂时存放程序和数据,一旦关闭电源或发生断电,其中的数据就会丢失。现在的RAM 多为MOS 型半导体电路,它分为静态和动态两种。静态RAM 是靠双稳态触发器来记忆信息的;动态RAM 是靠MOS 电路中的栅极电容来记忆信息的。由于电容上的电荷会泄漏,需要定时给与补充,所以动态RAM 需要设置刷新电路。但动态RAM 比静态RAM 集成度高、功耗低,从而成本也低,适于作大容量存储器。所以主内存通常采用动态RAM,而高速缓冲存储器(Cache)则使用静态RAM。动态RAM 按制造工艺的不同,又可分为动态随机存储器(Dynamic RAM)、扩展数据输出随机存储器(Extended Data Out RAM)和同步动态随机存储器(Synchromized DynamicRAM)。DRAM 需要恒电流以保存信息,一旦断电,信息即丢失。它的刷新频率每秒钟可达几百次,但由于DRAM 使用同一电路来存取数据,所以DRAM 的存取时间有一定的时间间隔,这导致了它的存取速度并不是很快。另外,在DRAM 中,由于存储地址空间是按页排列的,所以当访问某一页面时,切换到另一页面会占用CPU 额外的时钟周期。EDO-RAM同DRAM 相似,但在把数据发送给CPU 的同时可以去访问下一个页面,故而速度要比普通DRAM 快15~30%。SDRAM同DRAM 有很大区别,它使用同一个CPU 时钟周期即可完成数据的访问和刷新,即以同一个周期、相同的速度、同步的工作,因而可以同系统总线以同频率工作,可大大提高数据传输率,其速度要比DRAM 和EDO-RAM 快很多(比EDO-RAM提高近50%)。
二、AT91RM9200开发板的存储器情况
第一级地址译码由存储控制器执行,即由具有附加功能的高级系统总线(ASB) 执行。译码将4G的地址空间分为16 个256M字节的区域。区域1 ~ 8对应EBI,和外部片选NC0 ~NCS7相联系。区域0为内部存储器地址,第二级译码提供1M字节内部存储空间。区域15为外设地址,且提供对高级外设总线(APB) 的访问。其它区域未使用,使用它们进行访问时将向发出访问请求的主机发出异常中断。注意,地址的转换都是按照字节为单位的。
1、内部存储器映射
内部ROM:AT91RM9200集成了一个128-K字节的内部ROM。任何时候,ROM均被映射到地址0x10 0000。若复位时BMS(引导模式选择引脚) 为高,则在复位后到重新映射命令执行前,可访问地址0x0。ROM容量为128KB,即对应0x20000。所以范围为0x100000-0x120000。
内部RAM:AT91RM9200集成了高速,16-K 字节的内部SRAM。复位后到重新映射命令执行前,只可访问SRAM 中0x20 0000的地址空间。重新映射后, SRAM 在地址0x0 同样有效。SRAM容量为16KB,即对应0x4000,所以范围为0x200000-0x204000。
USB 主机端口:AT91RM9200集成了一个USB主机端口开放主机控制器接口(OHCI)。ASB可直接访问该接口寄存器,且同标准内部存储器一样映射到地址0x30 0000。
图2 内部存储器映射
2、外部存储器映射
图3 外部存储器映射
嵌入式存储设备通常主要是RAM 和作为永久存储媒质的Flash。
现在所用的AT91RM9200开发板所用的SDRAM是HY57V281620HCT-H,其容量为4banks×2Mbits×16,即128Mbits=16Mbytes。SDRAM共有两片HY57V281620HCT-H,所以SDRAM容量为32MB。
现在所用的Flash芯片为Intel的28F640J3,容量为8MB,地址映射从0x10000000到0x10800000。现在将Flash分为64个扇区,每个扇区为128KB=0x20000,每个扇区分为两个擦除块,为64KB=0x10000。
-------------------------------------------------------------------
Chip Select 0――Flash(0x1000 0000-0x1FFF FFFF)
0x1000 0000(第0扇区)
boot.bin Flash
0x1001 0000(第0扇区)
u-boot.bin.gz Flash
0x1002 0000(第1扇区)
uImage Flash
.
.
.
0x1012 0000(第9扇区)
ramdisk Flash
.
.
.
0x107E 0000(第63扇区)
u-boot环境变量 Flash
-------------------------------------------------------------------
Chip Select 1――SDRAM(0x2000 0000-0x2200 0000)
0x2000 0000
SDRAM
.
.
0x2100 0000
uImage SDRAM
0x2110 0000
ramdisk SDRAM
.
.
-------------------------------------------------------------------
2006-08-17 u-boot移植
1 首先,了解一下bootloader。bootloader是系统加电后运行的第一段代码。它要完成的工作就是初始化硬件设备,建立内存空间的映射图,这样为最终调用操作系统内核做好准备。
2 bootloader的操作模式
(1)启动加载模式(bootloading)
(2)下载模式(downloading)
开发时要用(2),target board上的bootloader将通过串口或者网络等通信手段从host上下载内核映象和根文件系统映象等到ram中。
3 bootloader的启动方式:网络启动、磁盘启动、flash启动。
4 bootloader的种类
区分一下“bootloader”和“monitor”的概念。bootloader只是引导设备并且执行主程序的固件;而monitor还提供了更多的命令行接口,可以进行调试、读写内存、烧写flash、配置环境变量等。monitor在嵌入式系统开发过程中还可以提供很好的调试功能,开发完成后,就完全配置成了一个bootloader。所以,习惯上把它们统称为bootloader。我现在要使用的u- boot就是典型的monitor。
5 bootloader的启动流程
搜集了一些资料,写的比较精彩,放在这里。。。
资料一:
系统上电,检测BMS(引导模式选择引脚),选择系统的启动方式,如果BMS(引导模式选择引脚)为高电平,则系统从片内ROM启动。AT91RM9200的ROM上电后被映射到了0x0和0x100000处,在这两个地址处都可以访问到ROM。由于9200的ROM中固化了一个BOOTLOAER程序。所以PC从0X0处开始执行这个BOOTLOAER(准确的说应该是一级BOOTLOADER)。这个BOOTLOER 依次完成以下步骤:
1. PLL SETUP //PLL:鎖相環,倍頻的作用。
设置PLLB产生48M时钟频率提供给USB DEVICE。同时DEBUG USART也被初始化为48M的时钟频率。
2. 相应模式下的堆栈设置
3. 检测主时钟源(Main oscillator)
4. 中断控制器(AIC)的设置
5. C 变量的初始化
6. 跳到主函数
完成以上步骤后,我们可以认为BOOT过程结束,接下来的就是LOADER的过程,或者也可以认为是装载二级BOOTLOER。AT91RM9200按照DATAFLASH、EEPROM、连接在外部总线上的8位并行FLASH的顺序依次来找合法的BOOT程序。所谓合法的指的是在这些存储设备的开始地址处连续的存放的32个字节,也就是8条指令必须是跳转指令或者装载PC的指令,其实这样规定就是把这8条指令当作是异常向量表来处理。必须注意的是第6 条指令要包含将要装载的映像的大小。关于如何计算和写这条指令可以参考用户手册。一旦合法的映像找到之后,则BOOT程序会把找到的映像搬到SRAM中去,所以映像的大小是非常有限的,不能超过16K的大小。当BOOT程序完成了把合法的映像搬到SRAM的任务以后,接下来就进行存储器的REMAP,经过REMAP之后,SRAM从映设前的0X200000地址处被映设到了0X0地址并且程序从0X0处开始执行。而ROM这时只能在0X100000这个地址处看到了。至此9200就算完成了一种形式的启动过程。如果BOOT程序在以上所列的几种存储设备中找到合法的映像,则自动初始化DEBUG USART口和USB DEVICE口以准备从外部载入映像。对DEBUG口的初始化包括设置参数115200 8 N 1以及运行XMODEM协议。对USB DEVICE进行初始化以及运行DFU协议。现在用户可以从外部(假定为PC平台)载入你的映像了。在PC平台下,以WIN2000为例,你可以用超级终端来完成这个功能,但是还是要注意你的映像的大小不能超过13K。一旦正确从外部装载了映像,接下来的过程就是和前面一样重映设然后执行映像了。我们上面讲了BMS(引导模式选择引脚)为高电平,AT91RM9200选择从片内的ROM启动的一个过程。如果BMS(引导模式选择引脚)为低电平,则AT91RM9200会从片外的FLASH启动,这时片外的FLASH的起始地址就是0X0了,接下来的过程和片内启动的过程是一样的,只不过这时就需要自己写启动代码了,至于怎么写,大致的内容和ROM的BOOT差不多,不同的硬件设计可能有不一样的地方,但基本的都是一样的。由于片外FLASH可以设计的大,所以这里编写的 BOOTLOADER可以一步到位,也就是说不用像片内启动可能需要BOOT好几级了,目前AT91RM9200上使用较多的bootloer是u-boot,这是一个开放源代码的软件,用户可以***下载并根据自己的应用配置。
资料2
loader.bin, boot.bin, u-boot.bin代码执行流分析.
以上三个文件时at91rm9200启动所需要的三个bin,他们的实现代码并不难。
如果是你是采用at91rm9200的评估版,应该能得到其源码。
loader.bin 执行流程,这个文件主要在片内启动从串口下载代码时会用到
loader/entry.S init 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
AT91F_DBGU_Printk(" ");
AT91F_DBGU_Printk("************************************** ");
AT91F_DBGU_Printk("** Welcome to at91rm9200 ** ");
AT91F_DBGU_Printk("************************************** ");
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解压到特定的位置并开始执行内核代码。
6 u-boot-1.1.1的移植
自己先用u-boot-1.1.1,主要步骤如下:
(1)下载u-boot-1.1.1
http://sourceforge.net/projects/u-boot
(2)解压
用户:armlinux
$mkdir bootloader
$cd bootloader
$tar jxvf ../source/u-boot-1.1.1.tar.bz2
$cd u-boot-1.1.1
(3)修改
首先看一下结构
$ tree -L 1 -d
.
|-- board 平台依赖,存放电路板相关的目录文件
|-- common 通用多功能函数的实现
|-- cpu 平台依赖,存放cpu相关的目录文件
|-- disk 通用。硬盘接口程序
|-- doc 文档
|-- drivers 通用的设备驱动程序,如以太网接口驱动
|-- dtt
|-- examples 应用例子
|-- fs 通用存放文件系统的程序
|-- include 头文件和开发板配置文件,所有开发板配置文件放在其configs里
|-- lib_arm 平台依赖,存放arm架构通用文件
|-- lib_generic 通用的库函数
|-- lib_i386 平台依赖,存放x86架构通用文件
|-- lib_m68k 平台依赖
|-- lib_microblaze 平台依赖
|-- lib_mips 平台依赖
|-- lib_nios 平台依赖
|-- lib_ppc平台依赖,存放ppc架构通用文件
|-- net 存放网络的程序
|-- post 存放上电自检程序
|-- rtc rtc的驱动程序
`-- tools 工具
然后具体步骤为:
(一)在board文件夹下面建立自己的开发板的文件夹。一般的,要选取与自己的开发板硬件设置最为接近的型号。在u-boot-1.1.1中,已经支持at91rm9200,所以可以选取at91rm9200dk作为模板进行修改。设置你的开发板的名字,随意即可,我的设置为:myboard。
[armlinux@lqm u-boot-1.1.1]$ cd board
[armlinux@lqm board]$ cp -R at91rm9200dk/ myboard/
[armlinux@lqm board]$ cd myboard
[armlinux@lqm myboard]$ ls
at91rm9200dk.c config.mk flash.c Makefile u-boot.lds
(二)可以看到,这里共有5个文件。首先,要修改主文件的名字,即要把at91rm9200dk.c更改为 myboard.c。其次,要更改config.mk中TEXT_BASE的数值,与loader等一级bootloader的要一致。接下来,因为在 at91rm9200dk用的是AMD的flash,而我的开发板上用的是Intel的28F640J3,那么需要另外找Intel的flash.C,以减少工作量。在这里,推荐用source insight这个查看代码的工具。我是在win下面使用的,它可以很方便的读代码,并且查找调用函数等等的工作。在strong ARM构架里有xm250,它的flash是Intel的,修改的东西并不是很多。需要注意的是,xm250的flash位宽是32,而我的位宽是16,要根据这个进行相应的修改。最后,修改Makefile,主要是修改生成文件的名字。具体操作如下:
[armlinux@lqm myboard]$ mv at91rm9200dk.c myboard.c
[armlinux@lqm myboard]$ cat config.mk
TEXT_BASE = 0x21f80000
[armlinux@lqm myboard]$ vi config.mk
修改成:TEXT_BASE = 0x21f00000,然后保存退出。
[armlinux@lqm myboard]$ vi Makefile
include $(TOPDIR)/config.mk
LIB = lib$(BOARD).a
OBJS := myboard.o flash.o
SOBJS :=
$(LIB): $(OBJS) $(SOBJS)
$(AR) crv $@ $(OBJS) $(SOBJS)
clean:
rm -f $(SOBJS) $(OBJS)
[armlinux@lqm myboard]$ rm flash.c
[armlinux@lqm myboard]$ cp ../xm250/flash.c ./
[armlinux@lqm myboard]$ ls
config.mk flash.c Makefile myboard.c u-boot.lds
[armlinux@lqm myboard]$ vi flash.c
34 #undef FLASH_PORT_WIDTH32 /*不定义位宽32*/
35 #define FLASH_PORT_WIDTH16 /*定义位宽16*/
216 switch (value) {
217
218 case (FPW) INTEL_ID_28F128J3A:
219 info->flash_id += FLASH_28F128J3A;
220 info->sector_count = 128;
221 info->size = 0x01000000;
222 break; /* => 16 MB */
223
224 case (FPW) INTEL_ID_28F640J3A: /*就是这个芯片*/
225 info->flash_id += FLASH_28F640J3A;
226 info->sector_count = 64;
227 info->size = 0x00800000;
228 break; /* => 8 MB */
[armlinux@lqm myboard]$ cd ../..
[armlinux@lqm u-boot-1.1.1]$ vi Makefile
#########################################################################
## AT91RM9200 Systems
#########################################################################
at91rm9200dk_config : unconfig
@./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk
myboard_config : unconfig
@./mkconfig $(@:_config=) arm at91rm9200 myboard
#########################################################################
在这里,可以在命令模式下输入“/at91rm9200”快速查找 at91rm9200dk,仿照它的例子,写出自己板子的配置。注意的是,第二行开头要用TAB键,不是空格,否则报错。选项arm表示目标板架构, at91rm9200表是片上系统,myboard是你自己的开发板名字。
[armlinux@lqm u-boot-1.1.1]$ vi MAKEALL
LIST_ARM9=" \
at91rm9200dk integratorcp integratorap \
omap1510inn omap1610h2 omap1610inn \
smdk2400 smdk2410 trab \
VCMA9 versatile myboard \
"
(三)修改主要的配置文件。配置选项比较多,主要是配置cpu,波特率,flash和sdram的类型大小,环境变量的偏移量等等,容易出错。应该首先了解硬件情况,仔细对应芯片资料进行修改。
[armlinux@lqm u-boot-1.1.1]$ cd include/configs
[armlinux@lqm configs]$ cp at91rm9200dk.h myboard.h
[armlinux@lqm configs]$ vi myboard.h
//行號和實際的myboard.h的行號有所偏差,認真修改好配置就可以了。
41 #define CONFIG_MYBOARD 1 目标板
65 #define CONFIG_BOOTDELAY 5 u-boot延时等待时间
110 #define CONFIG_NR_DRAM_BANKS 1 sdram banks,我的是一个
111 #define PHYS_SDRAM 0x20000000 sdram起始地址
112 #define PHYS_SDRAM_SIZE 0x2000000 sdram容量32MB
121 #undef CONFIG_HAS_DATAFLASH 未用dataflash
128 #define PHYS_FLASH_1 0x10000000
129 #define PHYS_FLASH_2 0x00000000 定义,flash.c用到,但实际并未起作用
130 #define PHYS_FLASH_SIZE 0x800000 flash容量8MB
131 #define CFG_FLASH_BASE PHYS_FLASH_1 flash起始地址别名
132 #define CFG_MAX_FLASH_BANKS 1 flash最大banks数
133 #define CFG_MAX_FLASH_SECT 64 扇区总数
134 #define PHYS_FLASH_SECT_SIZE (128*1024) 每个扇区128KB
135 #define CFG_FLASH_ERASE_TOUT (2*CFG_HZ) /* Timeout for Flash Erase */
136 #define CFG_FLASH_WRITE_TOUT (2*CFG_HZ) /* Timeout for Flash Write */
137 #define CFG_FLASH_UNLOCK_TOUT (2*CFG_HZ)
138
139 #undef CFG_ENV_IS_IN_DATAFLASH
140
141 #ifdef CFG_ENV_IS_IN_DATAFLASH
142 #define CFG_ENV_OFFSET 0x20000
143 #define CFG_ENV_ADDR (CFG_DATAFLASH_LOGIC_ADDR_CS0 + CFG_ENV_OFFSET)
144 #define CFG_ENV_SIZE 0x2000 /* 0x8000 */
145 #else
146 #define CFG_ENV_IS_IN_FLASH 1
147 #define CFG_ENV_ADDR (PHYS_FLASH_1 + 0x7e0000) /* 0x107E0000 */
148 #define CFG_ENV_SIZE 0x20000 环境变量占了一个扇区,共128KB
149 #endif
150
151 #define CFG_SOFT_RESET 1 定义软复位,flash.c用到
152 #define CFG_LOAD_ADDR 0x21000000 /* default load address */
160 #define CFG_PROMPT "U-boot> " 提示符名字,可任意改
[armlinux@lqm configs]$ cd ../..
[armlinux@lqm u-boot-1.1.1]$ make myboard_config
Configuring for myboard board...
[armlinux@lqm u-boot-1.1.1]$ make CROSS_COMPILE=arm-linux-
然后把生成的u-boot.bin保存,并且压缩一下得到u-boot.bin.gz。将这两个文件通过ftp传至windows上,通过SecureCRT来进行传输。
//u-boot.bin在目錄: u-boot-1.1.1下。
$gzip u-boot.bin //壓縮得到u-boot.bin.gz
这里注意,知道CROSS_COMPILE路径,前面设置环境变量时说到过。压缩命令为gzip
利用交叉线将com1和开发板串口相连。首先设置为片内启动方式,上电。超级终端首先出现“CCCC”,这时利用modem协议传输loader.bin,成功后传输u-boot.bin。具体现象如下:
--------------------------------------------------------------
CCCCCCCCCCCC
正在开始 xmodem 传输。 按 Ctrl+C 取消。
正在传输 loader.bin...
100% 6 KB
-I- AT91F_LowLevelInit(): Debug channel initialized 6 KB/s 00:00:01 0 错误
loader 1.0 (Aug 8 2003 - 12:01:07)
XMODEM: Download U-BOOT
CCCCCCCCCCCCC
正在开始 xmodem 传输。 按 Ctrl+C 取消。
正在传输 u-boot.bin...
100% 85 KB 6 KB/s 00:00:14 0 错误
U-Boot downloaded successfully
U-Boot 1.1.1 (Aug 17 2006 - 14:07:56)
U-Boot code: 21F00000 -> 21F156CC BSS: -> 21F198D0
RAM Configuration:
Bank #0: 20000000 32 MB
Flash: 8 MB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
U-boot> version
U-Boot 1.1.1 (Aug 17 2006 - 14:07:56)
U-boot> printenv
bootdelay=5
baudrate=115200
stdin=serial
stdout=serial
stderr=serial
Environment tes
-------------------------------------------------------
设定环境变量,当然这些也可以在include/configs/.h里面定义。
U-boot> setenv ipaddr 192.168.1.100
U-boot> setenv serverip 192.168.1.106
U-boot> setenv ethaddr 36:B9:04:00:24:80
注意:物理地址应该合法,在tools文件夹内有一个文件gen_eth_addr.c,利用其生成可执行文件则能够获得有效的mac地址。你可以先用gcc编译,生成可执行文件,然后执行,获得合法的mac地址。
U-boot> saveenv
Saving Environment to Flash...
Un-Protected 1 sectors
Erasing Flash...
Erasing sector done
Erased 1 sectors
Writing to Flash...\done
Protected 1 sectors
U-boot> tftpboot 20000000 boot.bin
TFTP from server 192.168.1.106; our IP address is 192.168.1.100
Filename 'boot.bin'.
Load address: 0x20000000
Loading: ###
done
Bytes transferred = 10628 (2984 hex)
U-boot> protect off 1:0
Un-Protect Flash Sectors 0-0 in Bank # 1
U-boot> erase 1:0
Erase Flash Sectors 0-0 in Bank # 1
Erasing sector 0 ... done
U-boot> cp.b 20000000 10000000 2984
Copy to Flash...-done
U-boot> tftpboot 20000000 u-boot.bin.gz
TFTP from server 192.168.1.106; our IP address is 192.168.1.100
Filename 'u-boot.bin.gz'.
Load address: 0x20000000
Loading: #########
done
Bytes transferred = 43791 (ab0f hex)
U-boot> cp.b 20000000 10010000 ab0f
Copy to Flash...\done
断电重启,从片外启动。
这时遇到了一个问题,就是从flash启动时,总是提示:*** Warning - bad CRC, using default environment,具体解决方法:
现象:配置好u-boot,在RAM里正常启动如下:
--------------------------------
U-Boot 1.1.2 (Aug 17 2006 - 14:07:56)
U-Boot code: 21F00000 -> 21F156CC BSS: -> 21F198D0
RAM Configuration:
Bank #0: 20000000 32 MB
Flash: 8 MB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
U-boot>
--------------------------------
flash读写擦除均正常,当设置好环境变量,固化到flash之后,启动仍然如上,即总是提示“*** Warning - bad CRC, using default environment”。使用md查看环境变量所在63扇区,发现设置的环境变量仍然在。
问题解决:
通过分析u-boot的启动流程,调试代码,得知问题出在cpu/at91rm9200/start.S中。其中有一段代码:
--------------------
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
--------------------
它的作用是把中断向量表从flash reload到RAM,以提高速度。但是它没有进行remap。故而使得u-boot启动之后无法寻找到环境变量所在的第63扇区。更改如下:
--------------------
if 0
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
endif
--------------------
即把此段代码注释掉。
【或者是在此段前面加上remap部分,不过如果加上remap,则需要把前面的设置svc部分的代码注释掉,否则在u-boot>reset时会进入异常状态。】
此解决方案对u-boot-1.1.2也有效。
U-Boot 1.1.1 (Aug 17 2006 - 16:50:31)
U-Boot code: 21F00000 -> 21F157F4 BSS: -> 21F199F4
RAM Configuration:
Bank #0: 20000000 32 MB
Flash: 8 MB
In: serial
Out: serial
Err: serial
U-Boot>
经验证,这是u-boot-1.1.1已经能够正常启动了。
---------------------------
同样的方法,u-boot-1.1.2也正常启动了。另外,u-boot-1.1.2有几个小的补丁,等明天再做一下总结。同时看看如何制作补丁,如何打补丁。具体的调试过程没有写得很仔细,要想做好一项工作,只会写程序远远不够,更为重要的是会调试。我需要加强此方面的工作。明天把JEDI probe调试环境的建立也总结一下。
2006-08-18
今天学习了一下Linux下面patch的制作和使用,做了总结,放到blog上面。u-boot-1.1.2有几个diff补丁文件,具体没有分析,先附在这里。我的使用倒是还没有发现这几个问题,也许没有测试,不管它,以后如果出现问题在来仔细考虑吧。
开发板由王老师用,我先学习内核裁减吧。关于移植版本,不一定非得越高越好。关于内核版本标号问题,详细参考《Building Embedded Linux Systems》。现在还不开发产品,那么先多试用几个,总结总结经验。初步打算先移植一个2.4.x版本,然后移植一个2.6.x版本。
-----------------------------
Bug1:
RCS file: /home/cvs/u-boot/tools/env/fw_env.c,v
retrieving revision 1.2
diff -u -r1.2 fw_env.c
--- fw_env.c 21 Jul 2004 03:28:43 -0000 1.2
+++ fw_env.c 23 Jul 2004 05:00:25 -0000
@@ -612,8 +612,8 @@
if (!crc1_ok) {
fprintf (stderr,
"Warning: Bad CRC, using default environment\n");
- environment.data = default_environment;
- free (addr1);
+ memset(environment.data, 0, ENV_SIZE);
+ memcpy(environment.data, default_environment, sizeof(default_environment));
}
} else {
flag1 = environment.flags;
Bug2:
saveenv bad checksum
when saving environment after changing stdin,
stdout,... the checksum is not consistent.
Adding an env_crc_update() before saving environment
could solve this. i.e in common/cmd_nvedit.c in
function do_saveenv() :
int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc,char *argv[])
{
extern char * env_name_spec;
printf ("Saving Environment to %s...\n", env_name_spec);
env_crc_update();
----------------------------------------
2006-08-22
Linux内核移植
版本:Linux-2.4.27-vrs1
Target Board:ARM
U-boot:1.1.2
toolchain:cross-2.95.3
1、准备
kernel下载到网站www.kernel.org,可以通过ftp方式:$ftp ftp.kernel.org,采用匿名的方式输入anonymous即可进入下载。
patch准备:到官方网站www.arm.linux.org.uk,这里提供ftp方式$ftp ftp.arm.linux.org.uk
2、打好补丁,执行make mrproper清理一下代码树。
3、修改根目录的Makefile文件,只需修改ARCH和CROSS_COMPILE即可。
ARCH :=arm
CROSS_COMPILE=arm-linux-
在这里,我已经将2.95.3放到PATH中,所以不需要写全路径。如果采用其他版本的toolchain,可以指明路径,如:CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux-
4、$make at91rm9200dk_config
该步的作用是把根目录下面的.config重命名为.config.old,然后把arch/arm/defconfig/里的at91rm9200dk开发板的默认配置文件复制到根目录下,命名为.config。简单的说,就是要利用提供的开发板的配置文件,然后在此基础上进行修改。
下一步如果执行make oldconfig,那么就完全按照.config来配置。而现在需要根据实际应用需要重新配置,所以不执行make oldconfig。而要执行make menuconfig。这两个命令都是首先寻找默认的.config文件并且执行,但不同的是,make oldconfig只出现.config没有的新配置选项供选择。而make menuconfig则提供所有选项,图形界面默认的是.config的配置。
在这里,还是推荐在提供的开发板配置文件的基础之上进行修改,否则后面很容易出现意想不到的问题。我就是因为开始没有使用默认配置,后面的错误一个接一个,折腾了一个上午。
5、make menuconfig
在这里可以重新配置,根据你所需要的功能进行裁减。
6、make clean dep
建立依赖关系。
7、make Image 或者 make zImage。这要看你后面使用什么方式的内核映象。如果是make Image,则生成vmlinux,需要arm-linux-objcopy进行处理,生成uImage影响文件。如果是make zImage,则生成zImage,vmlinux,system.map。
zImage和uImage对应的u-boot处理的方式也不相同,分别对应着go和bootm。
8、
[armlinux@lqm linux-2.4.27]$ cp arch/arm/boot/zImage /home/armlinux/images/zImage-2.4.27-vrs1
[armlinux@lqm linux-2.4.27]$ cp vmlinux $PRJROOT/images/vmlinux-2.4.27-vrs1
[armlinux@lqm linux-2.4.27]$ cp System.map $PRJROOT/images/System.map-2.4.27-vrs1
[armlinux@lqm linux-2.4.27]$ cp .config $PRJROOT/images/2.4.27-vrs1.config
9、生成uImage
[armlinux@lqm linux-2.4.27]$ arm-linux-objcopy -O binary -S vmlinux linux.bin
[armlinux@lqm linux-2.4.27]$ gzip -v9 linux.bin
linux.bin: 55.7% -- replaced with linux.bin.gz
[armlinux@lqm linux-2.4.27]$ ../../bootloader/u-boot-1.1.2/tools/mkimage -n 'RAM disk' -A arm -O linux -T kernel -C gzip -a 0x20008000 -e 0x20008000 -d linux.bin.gz uImage
//纠正:利用mkimage制作uImage,其中-T后跟type,此时应该为kernel,不应该为ramdisk。如果是ramdisk,那么bootm肯定无法正常引导,会显示Wrong Image Type for bootm command。
Image Name: RAM disk
Created: Wed Aug 23 09:09:27 2006
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 617981 Bytes = 603.50 kB = 0.59 MB
Load Address: 0x20008000
Entry Point: 0x20008000
10、准备好uImage,先通过超级终端下载到ram里面,检测,然后烧写到flash里面。
可以发现已经成功。