该文档的目的是总结我们在工作中的一些经验,并把它们分享给喜欢 ARM和Linux的朋友, 如有错误 之处,请大家多多指点. 同样, 我们也希望更多人能把自己的工作经验和体会加入该文档,让大家共同进步. 该文档是一份交流性文档, 只供个人学习与交流,不允许公司和企业用于商业行为.
第一部分前言
1硬件环境
1.1主机硬件环境 开发机:Pentium-4 CPU 内存: 512MB 硬盘: 60GB
1.2目标板硬件环境 CPU: S3C2410 SDRAM: HY57V561620 Nand flash: K9F1208U0B(64MB) 以太网芯片:CS8900A (10M/100MB)
1.3工具介绍 仿真器:Dragon-ICE 电缆:串口线,并口线
2软件环境
2.1主机软件环境 2.1.1 Windows操作系统 ADS编译工具:ADS1.2 仿真器软件: Dragon-ICE daemon程序
2.1.2 Linux操作系统 GNU交叉编译工具:
2.95.3: 作用:编译u-boot 3.3.2, 3.4.4:
作用:编译内核和应用程序 其它工作:
tree工具:
作用:查看文件目录树
下载:从 下载编译
2.1.3目标板最后运行的环境 启动程序: u-boot-1.1.4 内核: linux-2.6.14.1 应用程序:
1. busybox-1.1.3 2. TinyLogin-1.4 3. Thttpd-2.25 2.2 Linux下工作用户及环境 2.2.1交叉工具的安装 工具链的编译过程请参考第三部分. 1. 下载交叉工具 2.95.3 下载地址:. tar.bz2 3.3.4 下载地址: 2. 编译交叉工具 [root@localhost ~]mkdir /usr/local/arm [root@localhost ~]cd /usr/local/arm 把cross-2.95.2.tar.bz2, cross-3.4.4.tar.gz 拷贝到/usr/local/arm目录中。解压这两个包。 [root@localhost ~]tar -xjvf cross-2.95.2.tar.bz2 [root@localhost ~]tar -xzvf cross-3.4.4.tar.gz 2.2.2 u-boot移植工作目录 1. 添加工作用户 [root@localhost ~]#useradd -G root -g root -d/home/uboot uboot 2. 建立工作目录 [uboot@localhost ~]$mkdir dev_home [uboot@localhost ~]$cd dev_home [uboot@localhost dev_home]$mkdir doc mybootloader uboot . |-- doc |-- mybootloader `-- uboot 3. 建立环境变量 [uboot@localhost ~]vi ~/.bashrc export PATH=/usr/local/arm/2.95.3/bin:$PATH 2.2.3内核及应用程序移植工作 1. 添加工作用户 [root@localhost ~]#useradd -G root -g root -d/home/arm arm 2. 建立工作目录 [arm@localhost arm]$mkdir dev_home [arm@localhost arm]$cd dev_home [arm@localhost arm]$mkdir bootldr btools debug doc images kernel localapps \ rootfs sysapps tmp tools [arm@localhost arm]$tree -L 1 . |-- bootldr |-- btools |-- debug |-- doc |-- images |-- kernel |-- localapps |-- rootfs |-- sysapps |-- tmp `-- tools
可以看到如上树形结构。
注:tree命令
3. 建立环境变量设置脚本 [arm@localhost arm]$vi env_sh #!/bin/bash PRJROOT=~/dev_home KERNEL=$PRJROOT/kernel ROOTFS=$PRJROOT/rootfs LAPP=$PRJROOT/localapps DOC=$PRJROOT/doc TMP=$PRJROOT/tmp export PRJROOT KERNEL LAPP ROOTFS export PATH=/usr/local/arm/3.4.4/bin:$PATH
4. 登陆时启动环境变量 [arm@localhost arm]$vi ~/.bashrc . ~/dev_home/env_sh 重新登陆 arm用户,环境变量生效 [arm@localhost arm]$su arm
2.3配置系统服务 2.3.1 tftp服务器的配置 如果用下面一条命令能够看到服务已经启动, 则不用安装, 否则需要按 1或 2点安装tftp-server服务器. [arm@localhost arm]#netstat -a | grep tftp udp 0 0 *:tftp *:*
1. 从 RPM包安装tftp-server 从对应Linux操作系统版本的安装光盘上找到tftp-server的安装包. 下面 tftp-server-0.32-4.i386.rpm包为例,把rpm包拷贝到dev_home/btools/下. [arm@localhost arm]#cp tftp-server-0.32-4.i386.rpm /home/arm/dev_home/btools/ [arm@localhost arm]#su root [root@localhost arm]#rpm -q tftp-server 如果没有安装tftp-server,就要用下面命令安装,否则,直接进入第2步配置服务. [root@localhost arm]#cd /home/arm/dev_home/btools/ [root@localhost btools]#rpm -ivh tftp-server-0.32-4.i386.rpm
建立tftp的主工作目录 [root@localhost btools]#mkdir /tftpboot
2. 修改配置文件并启动服务 备份配置文件 [root@localhost btools]#if [ -f /etc/xinetd.d/tftp ] > then > cp /etc/xinetd.d/tftp /etc/xinetd.d/tftp.old > fi
修改配置文件
[root@localhost btools]#vi /etc/xinetd.d/tftp service tftp {
disable = no socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -s /tftpboot per_source = 11 cps = 100 2 flags = IPv4
} 检查 tftp服务是否打开 [root@localhost btools]#chkconfig --list 如果 tftp的服务没有打开,则用下面命令打开tftp服务开关 [root@localhost btools]#chkconfig tftp on
重启服务
#/etc/init.d/xinetd restart #netstat -a | grep tftp udp 0 0 *:tftp *:*
2.3.2 NFS服务器的配置 1. 安装NFS服务器 [root@localhost btools]#rpm -q nfs-utils 如果没有安装,从对应Linux操作系统版本的安装光盘上找到nfs-utils的安装包.Fedora 5中的安装包 名称为nfs-utils-1.0.8.rc2-4.FC5.2.i386.rpm。下面以该安装包为例说明: [root@localhost btools]#rpm -ivh nfs-utils-1.0.8.rc2-4.FC5.2.i386.rpm
2. 配置NFS服务器 [root@localhost btools]#vi /etc/exports #加入要允许被另外计算机mount的目录: #/home/arm/dev_home/tmp 为被另外计算机mount的目录 #192.168.1.134 允许另外计算机mount的IP #rw,sync,no_root_squash表示访问限制,更详细说明见相关手册. /home/arm/dev_home/tmp 192.168.1.134(rw,sync,no_root_squash)
3. 启动NFS服务器 第一启动NFS服务器时用下面命令. [root@localhost btools]#/etc/init.d/nfs start 如果你已经启动了 NFS服务器时,并且重新修改了/etc/exports文件,用如下命令使新加入的目录生效:
[root@localhost btools]#/etc/init.d/nfs reload
4. 测试NFS服务器 [root@localhost btools]#netstat -a | grep nfs 5. 显示被 export出的目录列表 [root@localhost btools]#exportfs 2.4工具使用 2.4.1 minicom的使用 1.切换到root用户. [root@localhost btools]#su 2. 查找有效的串设备. [root@localhost ~]#cat /proc/devices ... 4 ttyS ... 188 ttyUSB ...
如果是普通串口设备, 设备名前缀为ttyS, 第一串口为ttyS0, 第二串口为ttyS1,依次类推. 如果是 USB转串口的设备, 设备名前缀为ttyUSB, 第一串口为ttyUSB0.
3. 配置ttyUSB设备 [root@localhost ~]#minicom -s ttyUSB0 会出现一个configuration窗口, ┌──[configuration]────┐ │ Filenames and paths │ │ File transfer protocols │ │ Serial port setup │ │ Modem and dialing │ │ Screen and keyboard │ │ Save setup as ttyUSB0 │ │ Save setup as.. │ │ Exit │ │ Exit from Minicom │ └───────────────┘
选择Serial port setup配置. 会出现如下窗口: ┌────────────────────────────────────────────┐ │ A -Serial Device : /dev/ttyUSB0 │ │ B -Lockfile Location : /var/lock │ │ C -Callin Program : │ │ D -Callout Program : │ │ E -Bps/Par/Bits : 115200 8N1 │ │ F -Hardware Flow Control : No │ │ G -Software Flow Control : No │ ││ │ Change which setting? │
└───────────────────────────────────────────┘ 我的设置如上所示, 设置完成后, Change which setting?项上按回车退出当前窗口, 回到第一个窗口.按 Save setup as ttyUSB0保存设置. 再按 Exit from Minicom退出 Minicom.
4. 启动minicom [root@localhost ~]#minicom 3作者介绍
3.1策划,组织,指导,发布者 刘勇
email:
如果您有新的内容,请发到这个电子邮件,我们会把您的内容加入文档,并在作者列表中加入您的名字.
3.2 ADS bootloader部分 作者:刘勇
email:
3.3交叉工具部分 作者:孙贺
email:
3.4 uboot部分 作者:聂强
email:
作者:孙贺
email:
3.5内核部分 作者:聂大鹏 email: 作者:牛须乐(8900a网卡移植部分) email:
3.6应用程序部分 作者:聂大鹏 email:
3.7 Nand Flash 驱动部分 作者:孙磊,刘勇 email:
第部分系统启动 bootloader的编写(ADS) 1工具介绍
1.1 ADS命令行命令介绍 1.1.1 armasm 1. 命令:armasm [选项] -o 目标文件源文件 2. 选项说明 -Errors 错误文件名 ;指定一个错误输出文件 -I 目录[,目录] ;指定源文件搜索目录 -PreDefine 预定义宏 ;指定预定义的宏 -NOCache ;编译源代码时禁止使用Cache进行优化 -MaxCache ;编译源代码时使用Cache进行优化 -NOWarn ;关闭所有的警告信息 -G ;输出调试表 -keep ;在目标文件中保存本地符号表 -LIttleend ;生成小端(Little-endian) ARM代码 -BIgend ;生成大端(Big-endian) ARM代码 -CPU ;设立目标板ARM核类型,如: arm920t. -16 ;建立16位的thumb指令. -32 ;建立32位的ARM指令. 3. 编译一个汇编文件
c:\adsloader>armasm -LIttleend -cpu ARM920T -32 bdinit.s 把汇编语言编译成小端, 32位, ARM920T CPU.
1.1.2 armcc, armcpp 1. 命令:armcc [选项] 源文件1 源文件2 ... 源文件n 2. 选项说明 -c ;编译但是不连接 -D ;指定一个编译时使用的预定义宏常量 -E ;仅仅对 C源文件做预处理 -g ;产生调试信息表 -I ;指头文件的搜索路径 -o ;指定一个输出的目标文件 -O[0/1/2] ;指定源代码的优化级别 -S ;输出汇编代码来代替目标文件 -CPU ;设立目标板ARM核类型,如: arm920t. 3.编译一个C程序
c:\adsloader>armcc -c -O1 -cpu ARM920T bdisr.c 编译不连接, 二级优化, ARM920T CPU.
1.1.3 armlink 1. 命令:armlink [选项] 输入文件 2. 选项说明 -partial ;合并目标文件 -Output 文件 ;指定输出文件名 -scatter 文件 ;按照指定的文件为可执行文件建立内存映射 -ro-base 地址值 ;只读代码段的起始地址 -rw-base 地址值 ;RW/ZI段的起始地址
3. 把多个目标文件合并成一个目标文件 c:\adsloader>armlink -partial bdmain.o bdport.o bdserial.o bdmmu.o bdisr.o -o bd.o 4. 把几个目标文件编译一个可执地文件 c:\adsloader>armlink bd.o bdinit.o -scatter bdscf.scf -o bd.axf 1.1.4 fromelf 1. 命令:fromelf [选项] 输入文件 2. 选项说明 -bin 二进制文件名 ;产生的二进制文件 -elf elf文件名 ;产生一个elf文件 -text text文件名 ;产生text文件
3. 产生一个可执行的二进制代码 c:\adsloader>fromelf.bd.axf.bin.o.bd. bin 2基本原理
2.1可执行文件组成及内存映射 2.1.1可执行文件的组成 在ADS下,可执行文件有两种,一种是.axf文件,带有调试信息,可供AXD调试工具使用.另一种是.bin 文件,可执行的二进制代码文件。我们重点是讲描.bin文件的组成。 我们把可执行文件分为两种情况:分别为存放态和运行态。
1. 存放态 存放态是指可执行文件通过fromelf产生后,在存储介质(flash或磁盘)上的分布. 此时可执行文件一 般由两部分组成:分别是代码段和数据段。代码段又分为可执行代码段(.text)和只读数据段(.rodata), 数据段又分为初始化数据段(.data)和未初始化数据段(.bss)。可执行文件的存放态如下:
+-------------+----------| .bss | +-------------+--数据段 | .data | +-------------+----------| .rodata | |_____________| 代码段 | .text | +-------------+----------
2. 运行态 可执行文件通过装载过程, 搬入到RAM中运行, 这时候可执行文件就变成运行态。在ADS下对可执行代 码各段有另一个名称: | ... | +-------------+----------| .bss | ZI 段 +-------------+--数据段
| .data | RW 段 +-------------+----------| .rodata | |_____________| 代码段(RO 段) | .text | +-------------+----------| ... |
装载前
当可执行文件装载后, 在RAM中的分布如下:
| ... | +-------------+-- ZI段结束地址 | ZI 段 | +-------------+-- ZI段起始地址 | 保留区2 | +-------------+-- RW段结束地址 | RW 段 | +-------------+-- RW段起始地址 | 保留区1 | +-------------+-- RO段结束地址 | RO 段 | +-------------+-- RO段起始地址 | ... |
装载后
所以装载过程必须完成把执行文件的各个段从存储介质上搬到RAM指定的位置。而这个装载过程由谁来完 成呢?由我们的启动程序来完成.
2.1.2装载过程 在ADS中,可以通过两种方式来指定可执行代码各段在RAM中的位置,一个是用armlink来指定,一种是 用scatter文件来指定.RAM区的起始地址:0x30000000.
1. armlink指定代码段地址 我们通常的代码,只用指定两个段开始地址, RO段的起始地址和RW段的起始地址, ZI段紧接在RW段之 后.示例见该部分的1.1.3.
2. scatter指定代码段地址 我们也可以通过scatter文件指定可执行文件各段的详细地址. Scatter文件如下: MYLOADER.0x30000000 ;MYLOADER:为可执行文件的名称,.可自定义 ;0x3000000:起始地址
{ RO.0x30000000 ;RO只读代码段的名称 ;0x30000000:只读代码段的起始地址 { init.o.(Init,.+First) .....;.Init代码段为可执行文件的第一部分.
*.(+RO).;所有其它的代码段和只读数据段放在该部分 } RW.+0
;RW:.RW段的名称
;+0:表示 RW段紧接着 RO段
{
*.(+RW).;所有RW段放在该部分 } ZI..+0
;ZI:.ZI段的名称
;+0:表示 ZI段紧接着 RW段
{
*(+ZI).;所有ZI段放在该部分 } }
3..ADS产生的各代码段宏 |Image$$RO$$Base|./*.RO代码段起始地址 */ |Image$$RO$$Limit|./*.RO代码段结束地址 */ |Image$$RW$$Base|./*.RW代码段起始地址 */ |Image$$RW$$Limit|./*.RW代码段结束地址 */ |Image$$ZI$$Base|./*.ZI代码段起始地址 */ |Image$$ZI$$Limit|./*.ZI代码段结束地址 */ 注意:在两个$$之间的名称,.与scatter中指定的段的名称相同..
4..装载过程说明 当从 NorFlash启动时,.要把flash芯片的首地址映射到0x00000000位置,.系统启动后,.启动程序本身把自己从 flash中搬到RAM中运行..搬移后的各段起始地址,.由以上宏来确定. 当从 NandFlash启动时,.S3C2410会自动把前NandFlash的前4k搬到S3C2410的内部RAM中,并把内部 RAM的首地址设为0x00000000,CPU从 0x00000000开始执行..所以,.在nandFlash的前4k程序中,必须包含从 NandFlash把BootLoader的其余部分装入RAM的程序.
2.1.3启动过程的汇编部分 当系统启动时,.ARM.CPU会跳到0x00000000去执行。一般 BootLoader都包括如下几个部分: 1..建立中断向量异常表 2..显示的切换到SVC且 32指令模式 3..关闭S3C2410的内部看门狗 4..禁止所有的中断 5..配置系统时钟频率和总线频率 6..设置内存区的控制寄存器 7..初始化中断 8..安装中断向表量 9..把可执行文件的各个段搬到运行态的各个位置 10..跳到C代码部分执行 2.1.4启动过程的C部分 1..初始化MMU 2.初始化外部端口 3..中断处理程序表初始化 4..串口初始化 5..其它部分初始化(可选) 6..主程序循环 3 AXD的使用以及源代码说明
3.1源代码说明 3.1.1汇编源代码说明 ;=============================================================================== ;引用头文件 ;===============================================================================
get bdinit.h
;=============================================================================== ;引用标准变量 ;===============================================================================
IMPORT |Image$$RO$$Base| ; Base address of RO section IMPORT |Image$$RO$$Limit| ; End address of RO section IMPORT |Image$$RW$$Base| ; Base address of RW section IMPORT |Image$$RW$$Limit| ; End address of RW section IMPORT |Image$$ZI$$Base| ; Base address of ZI section IMPORT |Image$$ZI$$Limit| ; End addresss of ZI section
IMPORT bdmain ; The entry function of C program
;=============================================================================== ;宏定义 ;===============================================================================
; macro HANDLER
MACRO $HandlerLabel HANDLER $HandleLabel
$HandlerLabel sub sp,sp,#4 ;Decrement sp (to store jump address) stmfd sp!,{r0} ;PUSH the work register to stack ldr r0,=$HandleLabel;Load the address of HandleXXX to r0 ldr r0,[r0] ;Load the contents(service routine start address) of HandleXXX str r0,[sp,#4] ;Store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) MEND
;=============================================================================== ;汇编语言的入口代码 ;===============================================================================
AREA Init,CODE,READONLY CODE32
ENTRY ;===================== ; 建立中断向量表 ;=====================
b reset_handler ;0x00000000: Reset (SVC) b undef_handler ;0x00000004: Undefined instruction (Undef) b swi_handler ;0x00000008: Software Interrupt (SVC) b iabr_handler ;0x0000000C: Instruction Abort (Abort) b dabr_handler ;0x00000010: Data Abort (Abort) b no_handler ;0x00000014: b irq_handler ;0x00000018: IRQ (IRQ) b fiq_handler ;0x0000001C: FIQ (FIQ) LTORG
undef_handler HANDLER HandleUndef
swi_handler HANDLER HandleSWI iabr_handler HANDLER HandlePabort dabr_handler HANDLER HandleDabort no_handler HANDLER HandleReserved irq_handler HANDLER HandleIRQ fiq_handler HANDLER HandleFIQ
;============================= ;复位时运行的主程序 ;=============================
reset_handler ;Set the cpu to SVC32 mode
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr_cxsf,r0
;Turn off watchdog
ldr r0,=WTCON
ldr r1,=0x0
str r1,[r0]
;Disable all the first level interrupts
ldr r0,=INTMSK ldr r1,=0xffffffff str r1,[r0]
;Disable all the second level interrupts ldr r0,=INTSUBMSK ldr r1,=0x7ff str r1,[r0]
;Configure MPLL ldr r0,=MPLLCON ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) str r1,[r0]
;Set FCLK:HCLK:PCLK = 1:2:4 ldr r0, =CLKDIVN mov r1, #3 str r1, [r0]
;Set memory control registers ldrr0,=SMRDATA
;Fin=12MHz,Fout=200MHz
ldr r1,=BWSCON add r2, r0, #52 ;End address of SMRDATA 0 ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne %B0
;Initialize stacks bl InitStacks
;Setup IRQ handler ldr r0,=HandleIRQ ;This routine is needed ldr r1,=IsrIRQ str r1,[r0]
;Copy RW/ZI section into RAM ldr r0, =|Image$$RO$$Limit|;Get pointer to ROM data ldr r1, =|Image$$RW$$Base| ;and RAM copy ldr r3, =|Image$$ZI$$Base|
cmp r0, r1 ; Check that they are different beq %F2
1 cmp r1, r3 ; Copy init data ldrcc r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4 strcc r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4 bcc %B1
2 ldr r1, =|Image$$ZI$$Limit| ; Top of zero init segment mov r2, #0
3 cmp r3, r1 ; Zero init strcc r2, [r3], #4 bcc %B3
bl bdmain ;Jump to the main function
;Dead loop
1 nop b %B1
;=============================================================================== ;初始中断处理程序 ;===============================================================================
IsrIRQ sub sp,sp,#4 ;reserved for PC stmfd sp!,{r8-r9}
ldr r9,=INTOFFSET ldr r9,[r9] ldr r8,=HandleEINT0 add r8,r8,r9,lsl #2 ldr r8,[r8]
str r8,[sp,#8] ldmfd sp!,{r8-r9,pc}
;=============================================================================== ;初始化各个模式下堆栈 ;===============================================================================
InitStacks mrs r0,cpsr bic r0,r0,#MODEMASK orr r1,r0,#UNDEFMODE|NOINT msr cpsr_cxsf,r1 ;UndefMode ldr sp,=UndefStack
orr r1,r0,#ABORTMODE|NOINT msr cpsr_cxsf,r1 ;AbortMode ldr sp,=AbortStack
orr r1,r0,#IRQMODE|NOINT msr cpsr_cxsf,r1 ;IRQMode ldr sp,=IRQStack
orr r1,r0,#FIQMODE|NOINT msr cpsr_cxsf,r1 ;FIQMode ldr sp,=FIQStack
bic r0,r0,#MODEMASK|NOINT
orr r1,r0,#SVCMODE msr cpsr_cxsf,r1 ;SVCMode ldr sp,=SVCStack
mov pc,lr ;Return the call routine
LTORG
;=============================================================================== ;内存区控制寄存器值表;你可根据需要修改bdinit.h文件,.下面代码不用做任何改动 ;===============================================================================
SMRDATA DATA
DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+( B7_BWSCON<<28))
DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0 DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) ;GCS1 DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) ;GCS2 DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) ;GCS3 DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) ;GCS4 DCD
((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) ;GCS5 DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ;GCS6 DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ;GCS7
DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) DCD 0x32 ;SCLK power saving mode, BANKSIZE 128M/128M DCD 0x30 ;MRSR6 CL=3clk DCD 0x30 ;MRSR7
ALIGN
;=============================================================================== ;异常及中断向量表空间;安装异常或中断处理程序在bdisr.c中,isr_setup()来完成. ;===============================================================================
AREA RamData, DATA, READWRITE
^ _ISR_STARTADDRESS ;表示下面数据区从_ISR_STARTADDRESS指定的位置开始 HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4
;============================= ;.The.Interrupt.table ;=============================
HandleEINT0 # 4 HandleEINT1 # 4 HandleEINT2 # 4 HandleEINT3 # 4 HandleEINT4_7 # 4 HandleEINT8_23 # 4 HandleRSV6 # 4 HandleBATFLT # 4 HandleTICK # 4 HandleWDT # 4 HandleTIMER0 # 4 HandleTIMER1 # 4 HandleTIMER2 # 4 HandleTIMER3 # 4 HandleTIMER4 # 4 HandleUART2 # 4 HandleLCD # 4 HandleDMA0 # 4 HandleDMA1 # 4 HandleDMA2 # 4 HandleDMA3 # 4 HandleMMC # 4 HandleSPI0 # 4 HandleUART1 # 4 HandleRSV24 # 4 HandleUSBD # 4 HandleUSBH # 4 HandleIIC # 4 HandleUART0 # 4 HandleSPI1 # 4
HandleRTC # 4 HandleADC # 4 END
3.1.2 C语言源代码说明 void bdmain(void)
{
/* 禁止Cache 和MMU */
cache_disable();
mmu_disable();
/* 端口初始化 */ port_init();
/* 中断处理程序 */ isr_init();
/* 串口初始化 */ serial_init(0, 115200);
/* 输出信息进行主循环 */ serial_printf("is ok!\n"); while(1) {
} } 通常基本ADS的测试程序都可以在这个架构上加入自己的代码..
3.2 AXD的使用 3.2.1配置仿真器 1..为仿真器安装Server 一般的仿真器都对应有一个Server程序,所以在使用在线仿真之前,必须先安装这个Server程序。我使用 是 DragonICE 仿真器,.所以先要安装DragonICE.Server 程序。
2..连接仿真器 把dragonICE 仿真器的JTAG口连接上 ARM板(注意:ARM板要断电连接),.另一端通过并口连接到PC上, 有的仿真器是通过USB口连接到PC上,.这与仿真器的硬件相关。连接好后,.打开ARM电源,启动ARM板。 当ARM通电启动后,启动DragonICE.Server 检测ARM板,详细步骤及设置参见对应的仿真器手册。我的 dragonICE.Server 启动,.按”自动检测”可以检测到ARM920T。
3.2.2启动AXD配置开发板 1..启动AXD 先启动DragonICE.Server 程序. 按如下步聚启动AXD: 开始> 所有程序> ARM.Developer.Suite.v1.2> AXD.Debugger
2..装载仿真器库文件 从 AXD菜单的Options> .Configure.Target...启动”Choose.Target”目标板配置窗口. 在”Choose.Target”窗口中,点击” Add”按钮,选择仿真器的库文件..我的仿真器服务器程序安装在 c:\DragonICE 下,所以选择项 c:\DragonICE\ dragonice. dll文件. 3..为AXD在线仿真配置仿真器 在"Target.Environments"中选中DragonICE 中,点击右边的"Configure"按钮. 在”FJB.DragonICE.Release.v1.2” 窗口点击"This.computer..."按钮,再点击"OK"按钮。 回到”Choose.Target”窗口,点击"OK"按钮。完成配置. 回到主界面,.在右边的”Target”窗口会出现 ARM920T_0.这表明AXD已经进入ARM板的在线仿真状态. 点击菜单"System.Views">" Controls.Monitors".会出现"ARM920TRegister" 窗口.此时,会显示当前ARM板上所 有寄存器的状态。
4..配置ARM板 如果 ARM板通电后,没有程序运行并把内存区控制寄存器配置好的说,外部RAM是不能使用的..所以必须 通过仿真器来设置这些寄存器..如果 ARM板已经有启动程序并且已经配置好,.这一步可以省略. 首先把2410cfg.txt拷贝到c:\下. 回到AXD主界面,.从菜单” System.Views”.> .“Command.Line.Interface”。会出现一个Command.Line
Interface的调试命令行窗口,并显示如下提示符: Debug.> 输入obey.c:\2410cfg.txt装载所有配置命令. Debug.>obey.c:\2410cfg.txt
5..2410cfg.txt文件说明 sreg.psr,.0x00000013 ;设置当前CPSR的值,.把CPU的模式切换到SVC模式和32位指令集,.关闭IRQ和FIQ。 smem.0x53000000,0,32 ;设置看门狗控制寄存器WTCON ;禁止看门狗定时器
smem.0x4C000004,((0x74<<12)+(0x3<<4)+0x1),32 ;设置主频率设置寄存器MPLLCON ;目前CPU的工作频率FCLK是 124.00MHz
smem.0x4C000014,0x3,32 ;设置时钟分频寄存器CLKDIVN ;设置FCLK/HCLK/PCLK.的频率比例 1:2:4
smem.0x48000000,((2<<28)+(2<<24)+(1<<20)+(1<<16)+(1<<12)+(1<<8)+(1<<4)+0),32 ;设置内存总线控制BWSCON ;SDRAM.BANK.6&7.is.32位 ;其它 BANK.is.16位
smem.0x48000004,((3<<13)+(3<<11)+(7<<8)+(3<<6)+(3<<4)+(3<<2)+3),32 ;设置寄存器区0控制寄存器:BANKCON0
smem.0x4800001c,((3<<15)+(1<<2)+1),32 ;设置寄存器区6控制寄存器:.BANKCON6(SDRAM) ;RAS.to.CAS延时3.时钟周期 ;列地址是 9位
smem.0x48000020,((3<<15)+(1<<2)+1),32 ;设置寄存器区7控制寄存器:.BANKCON7(SDRAM) ;RAS.to.CAS延时 3.时钟周期 ;列地址是 9位
smem.0x48000024,((1<<23)+(3<<18)+(2<<16)+1113),32 ;set外部RAM刷新寄存器:REFRESH ;允许自刷新 ;HCLK=FCLK/2,.60MHz,刷新计算器是 1113
smem.0x48000028,0x31,32
;设置寄存器的大小 ;禁止burst操作 ;允许 SDRAM.power.down模式 ;SCLK在访问期间仍在活动状态
;SDRAM模式寄存器设置 smem.0x4800002c,0x30,32 smem.0x48000030,0x30,32
3.2.3使用AXD在线仿真调试程序 1..装载可执行的文件 AXD只支持.axf格式的可执行文件. 启动AXD,.在菜单的File中,选择Load.Image...,.选择c:\adsbloadter\prj\prj_Data\DebugRel\prj.axf加载执行 image..就可以执行并调试了..AXD提供了非常方便的调试手段,.包括在线单步,.自由设置断点等.
第三部分 GNU交叉工具链
1设置环境变量,准备源码及相关补丁
1.1设置环境变量 [arm@localhost arm]#vi.~/.bashrc export.PREFIX=/usr/local/arm/3.4.4 export.TARGET=armlinux export.SYSROOT=${PREFIX}/sysroot export.ARCH=arm export.CROSS_COMPILE=${TARGET}export.PATH=${ PREFIX}/bin:$PATH export.SRC=/home/arm/dev_home/btools/tchain3.4.4
1. 2准备源码包 1.2.1 binuils 名称:binutils2.16. tar.gz 下载地址:. tar.gz
1.2.2 gcc 名称:gcc3.4.4. tar.bz2 下载地址: gcc3.4.4. tar.bz2
1.2.3 glibc 名称:glibc2.3.5. tar.gz glibclinuxthreads2.3.5. tar.gz 下载地址: . tar.gz . tar.gz
1.2.4 linux kernel 名称:linux2.6.14.1. tar.gz
下载地址: . tar.gz
1.3准备补丁 1.3.1 ioperm.c.diff 作用:打修正 ioperm()函数. 下载地址:
1.3.2 flow.c.diff 作用:该补丁用于产生crti.o和crtn.o文件。 下载地址: cvsweb.cgi/gcc/gcc/flow.c.diff?cvsroot=gcc&only_with_tag=cslarmbranch& r1=1.563.4.2&r2=1.563.4.3
1.3.3 t-linux.diff 作用:修改gcc一处 bug 下载地址:. diff
1.4编译 GNU binutils 重新以arm用户登陆,让新设置的环境变量起作用. [arm@localhost.arm]#su.arm [arm@localhost.arm]#cd.${SRC} [arm@localhost.tchain3.4.4]#tar.xzvf.binutils2.16. tar.gz [arm@localhost.tchain3.4.4]#mkdir.p.BUILD/ binutils2.16 [arm@localhost.binutils2.16]# cd.BUILD/binutils2.16 [arm@localhost.binutils2.16]# .../../binutils2.16/ configure.prefix=${ PREFIX}.target=${ TARGET}.\ withsysroot=${ SYSROOT} [arm@localhost.binutils2.16]# make [arm@localhost.binutils2.16]# su.root [root@localhost.binutils2.16]# make.install [root@localhost.binutils2.16]# exit [arm@localhost.binutils2.16]#
1.5准备内核头文件 1.5.1使用当前平台的gcc编译内核头文件 [arm@localhost tchain3.4.4]#cd.${KERNEL} [arm@localhost.kernel]#tar.xvfz.linux2.6.14.1. tar.gz [arm@localhost.kernel]#cd..linux2.6.14.1 [arm@localhost.linux2.6.14.1]# make.ARCH=arm.menuconfig [arm@localhost.linux2.6.14.1]# make
1.5.2复制内核头文件 [arm@localhost.kernel]#su.root [root@localhost.kernel]#mkdir.p.${ SYSROOT}/usr/include
[root@localhost.kernel]#cp.a.include/ linux.${SYSROOT}/usr/include/linux [root@localhost.kernel]#cp.a.include/ asmi386.${ SYSROOT}/usr/include/asm [root@localhost.kernel]#cp.a.include/ asmgeneric.${ SYSROOT}/usr/include/asmgeneric [root@localhost.kernel]#exit [arm@localhost.kernel]#
1.6译编glibc头文件 [arm@localhost.kernel]#cd.${SRC} [arm@localhost.chain3.4.4]#tar.xvfz.glibc2.3.5. tar.gz [arm@localhost.chain3.4.4]#patch.d.glibc2.3.5.p1.< .ioperm.c.diff [arm@localhost.glibc2.3.5]# cd.glibc2.3.5 [arm@localhost.glibc2.3.5]# tar.xvfz.../glibclinuxthreads2.3.5. tar.gz [arm@localhost.chain3.4.4]#cd... [arm@localhost.chain3.4.4]#mkdir.BUILD/glibc2.3.5headers [arm@localhost.chain3.4.4]#cd.BUILD/glibc2.3.5headers [arm@localhost.glibc2.3.5headers]#../../ glibc2.3.5/ configure.prefix=/ usr.host=${ TARGET}.\ enableaddons= linuxthreads.–withheaders=${ SYSROOT}/usr/include [arm@localhost.glibc2.3.5headers]# su.root [root@localhost.glibc2.3.5headers]# make.crosscompiling= yes.install_root=${SYSROOT}.installheaders [root@localhost.glibc2.3.5headers]# touch.${SYSROOT}/usr/include/gnu/stubs.h.[root@localhost.glibc2.3.5headers]# touch.${SYSROOT}/usr/include/bits/stdio_lim.h [root@localhost.glibc2.3.5headers]# exit [arm@localhost.glibc2.3.5headers]# 注意:.prefix=/ usr.:是 gcc寻找库的搜索路径。
1.7编译gcc第一阶段 [arm@localhost.glibc2.3.5headers]# cd.${SRC} [arm@localhost.chain3.4.4]#tar.xjvf.gcc3.4.4. tar.bz2 [arm@localhost.chain3.4.4]#patch.d.gcc3.4.4.p1.< .flow.c.diff [arm@localhost.chain3.4.4]#patch.d.gcc3.4.4.p1.< .tlinux. diff [arm@localhost.chain3.4.4]#mkdir.p.BUILD/ gcc3.4.4stage1 [arm@localhost.chain3.4.4]#cd.BUILD/gcc3.4.4stage1 [arm@localhost.gcc3.4.4stage1]#../../ gcc3.4.4/ configure.prefix=${ PREFIX}.target=${ TARGET}.\ enablelanguages= c.withsysroot=${ SYSROOT}
注意:不能加上"disableshared" 选项。 [arm@localhost.gcc3.4.4stage1]# make.allgcc [arm@localhost.gcc3.4.4stage1]# su.root [root@localhost.gcc3.4.4stage1]# make.installgcc [root@localhost.gcc3.4.4stage1]# exit [arm@localhost.gcc3.4.4stage1]#
1.8编译完整的glibc [arm@localhost.gcc3.4.4stage1] .#cd.${SRC} [arm@localhost.tchain3.4.4]#mkdir.BUILD/glibc2.3.5 [arm@localhost.tchain3.4.4]#cd.BUILD/glibc2.3.5 [arm@localhost.glibc2.3.5]# BUILD_CC=gcc.CC=${CROSS_COMPILE}gcc.AR=${CROSS_COMPILE}ar.\ RANLIB=${CROSS_COMPILE}ranlib.AS=${CROSS_COMPILE}as.LD=${CROSS_COMPILE}ld.\ ../../glibc2.3.5/ configure.prefix=/ usr.build= i386redhatlinux.host= armunknownlinuxgnu.\ target= armunknownlinuxgnu.without__ thread.enableaddons= linuxthreads.\ withheaders=${ SYSROOT}/usr/include
说明: prefix: 指定安装路径。 target: 指定目标平台。 host: 指定当前平台。 build: 指定编译平台。 withsysroot: 用于指定编译所需要的头文件,及链接库。 enableaddons: 加入其它的库,如线程库等。 enablelanguages: 指定gcc所支持的语言。
[arm@localhost.glibc2.3.5]# make [arm@localhost.glibc2.3.5]# su.root [root@localhost.glibc2.3.5]# make.install_root=${SYSROOT}.install [root@localhost.glibc2.3.5]# exit [arm@localhost.glibc2.3.5]#
1.9编译完整的gcc [arm@localhost.glibc2.3.5]# cd.${SRC} [arm@localhost.tchain3.4.4]#mkdir.BUILD/gcc3.4.4 [arm@localhost.tchain3.4.4]#cd.BUILD/gcc3.4.4 [arm@localhost.gcc3.4.4]#../../ gcc3.4.4/ configure.prefix=${ PREFIX}.target=${ TARGET}.\ enablelanguages= c.withsysroot=${ SYSROOT} [arm@localhost.gcc3.4.4]# make [arm@localhost.gcc3.4.4]# su.root [root@localhost.gcc3.4.4]# make.install [root@localhost.gcc3.4.4]# exit [arm@localhost.gcc3.4.4]#
2 GNU交叉工具链的下载
2.1 ARM官方网站 工具链的官方下载地址:
.... 可以从该站点下载2.95.3,.3.0以及3.2工具链
. tar.bz2 . tar.bz2 . tar.bz2
3 GNU交叉工具链的介绍与使用
3.1常用工具介绍 名称归属作用
armlinuxas binutils编译ARM汇编程序
armlinuxar binutils把多个.o合并成一个.o或静态库(.a)
armlinuxranlib binutils为库文件建立索引,相当于 armlinuxar.s
armlinuxld binutils连接器(Linker),.把多个.o或库文件连接成一个可执行文件
名称归属作用
armlinuxobjdump binutils查看目标文件(.o)和库(.a)的信息
armlinuxobjcopy binutils转换可执行文件的格式
armlinuxstrip binutils去掉elf可执行文件的信息..使可执行文件变小
armlinuxreadelf binutils读 elf可执行文件的信息
armlinuxgcc gcc编译.c或.S开头的C程序或汇编程序
armlinuxg++ gcc编译c++程序
3.2主要工具的使用 3.2.1 arm-linux-gcc的使用 1..编译C文件,生成elf可执行文件 h1.c源文件 #include. void.hellofirst(void) { printf("The.first.hello!.\n"); }
h2.c源文件 #include. void.hellosecond(void) {
printf("The.second.hello!.\n"); }
hello.c源文件 #include. void.hellosecond(void); void.hellofirst(void);
int.main(int.argc,.char.*argv[])
{ hellofirst(); hellosecond(); return(0);
} 编译以上 3个文件,有如下几种方法: 方法1: [arm@localhost.gcc]#armlinuxgcc.c.h1. c [arm@localhost.gcc]#armlinuxgcc.c.h2. c [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.h1.o.h2.o 方法2: [arm@localhost.gcc]#armlinuxgcc.c.h1. c.h2.c [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.h1.o.h2.o 方法3: [arm@localhost.gcc]#armlinuxgcc.c.o.h1. o.h1.c
[arm@localhost.gcc]#armlinuxgcc.c.o.h1. o.h1.c [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.h1.o.h2.o 方法4: [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.h1.c.h2.c c: 只编译不连接。 o: 编译且连接。
2..产生一个预处理文件 当要看一个宏在源文件中产生的结果时,比较合适。 [arm@localhost.gcc]#armlinuxgcc.E.h1. i.h1.c E: 产生一个预处理文件.
3..产生一个动态库 动态库是在运行时需要的库。 [arm@localhost.gcc]#armlinuxgcc.c.fpic.h1. c.h2.c [arm@localhost.gcc]#armlinuxgcc.shared.h1. o.h2.o.o.hello. so [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.hello.so 把hello.so拷贝到目标板的/lib目录下,把可执行文件拷贝目标板的/tmp目录下,在目标板上运行hello. #/tmp/hello 或把hello.so和hello一起拷贝到/tmp目标下,并设置LD_LIBRARY_PATH环境变量 #export.LD_LIBRARY_PATH.=/tmp:$LD_LIBRARY_PATH #/tmp/hello
3.2.2 arm-linux-ar和 arm-linux-ranlib的使用 静态库是在编译时需要的库。
1..建立一个静态库 [arm@localhost.gcc]#armlinuxar.r.libhello. a.h1.o.h2.o 2..为静态库建立索引 [arm@localhost.gcc]#armlinuxar.s.libhello. a [arm@localhost.gcc]#armlinuxranlib.libhello. a 3..由静态库产生可执行文件 [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.lhello.L./ [arm@localhost.gcc]#armlinuxgcc.o.hello.hello. c.libhello.a hello文件可以直接拷贝到/tmp目录下运行,不需 libhello.a. 3.2.3 arm-linux-objdump的使用 1..查看静态库或.o文件的组成文件 [arm@localhost.gcc]$.armlinuxobjdump.a.libhello. a 2..查看静态库或.o文件的络组成部分的头部分 [arm@localhost.gcc]$.armlinuxobjdump.h.libhello. a 3..把目标文件代码反汇编 [arm@localhost.gcc]$.armlinuxobjdump.d.libhello. a 3.2.4 arm-linux-readelf的使用 1..读 elf文件开始的文件头部 [arm@localhost.gcc]$.armlinuxreadelf.h.hello
ELF.Header: Magic:...7f.45.4c.46.01.01.01.61.00.00.00.00.00.00.00.00
Class: Data: Version: OS/ABI: ABI.Version: Type: Machine: Version: Entry.point.address: Start.of.program.headers: Start.of.section.headers: Flags: Size.of.this.header: Size.of.program.headers:
.....................................ELF32 ......................................2's.complement,.little.endian ................................1.(current) ................................ARM 0 .....................................EXEC.(Executable.file) ................................ARM 0x1
0x82b4 ......52.(bytes.into.file) ........10240.(bytes.into.file) .....................................0x2,.has.entry.point ................52.(bytes) .......32.(bytes)
Number.of.program.headers:.6 Size.of.section.headers:..........40.(bytes) Number.of.section.headers:.28 Section.header.string.table.index:.25
2..读 elf文件中所有ELF的头部: [arm@localhost.gcc]#armlinuxreadelf.e.hello ...... 3..显示整个文件的符号表 [arm@localhost.gcc]#armlinuxreadelf.s.hello ...... 4..显示使用的动态库 [arm@localhost.gcc]#armlinuxreadelf.d.hello ...... 3.2.5 arm-linux-strip的使用 1..移除所有的符号信息 [arm@localhost.gcc]#cp.hello.hello1 [arm@localhost.gcc]#armlinuxstrip.stripall.hello stripall: 是移除所有符号信息 [arm@localhost.gcc]#ll rwxrxrx..1.arm.root.2856.7 月 3.15:14.hello rwxrxrx..1.arm.root.13682..7 月 3.15:13.hello1 被 strip后的hello程序比原来的hello1程序要小很多。 2..移除调试符号信息 [arm@localhost.gcc]#armlinuxstrip.g.hello [arm@localhost.gcc]#ll rwxrxrx..1.arm.root.4501.7 月 3.15:17.hello rwxrxrx..1.arm.root.13682..7 月 3.15:13.hello1 3.2.6 arm-linux-copydump的使用 生成可以执行的2进制代码 [arm@localhost.gcc]#armlinuxcopydump.O.binary.hello.hello. bin
4 ARM GNU常用汇编语言介绍
4.1 ARM GNU常用汇编伪指令介绍 1..abort .abort:停止汇编 .align.ab***pr1, .ab***pr2: 以某种对齐方式,在未使用的存储区域填充值..第一个值表示对齐方式,4,.8,16或 32..第 二个表达式值表示填充的值. 2..if...else...endif .if .else .endif:支持条件预编译 3..include .include."file":包含指定的头文件,.可以把一个汇编常量定义放在头文件中. 4..comm .comm.symbol,.length:在bss段申请一段命名空间,该段空间的名称叫 symbol,.长度为length...Ld连接器在连接会 为它留出空间. 5..data .data.subsection:说明接下来的定义归属于 subsection数据段. 6..equ .equ.symbol,.expression:把某一个符号(symbol)定义成某一个值(expression).该指令并不分配空间. 7..global .global.symbol:定义一个全局符号,.通常是为ld使用. 8..ascii .ascii."string":定义一个字符串并为之分配空间. 9..byte .byte.expressions:定义一个字节,.并为之分配空间. 10..short .short.expressions:定义一个短整型,.并为之分配空间. 11..int .int.expressions:定义一个整型,并为之分配空间. 12.long .long.expressions:定义一个长整型,.并为之分配空间. 13.word .word.expressions:定义一个字,并为之分配空间,.4bytes. 14..macro/endm .macro:定义一段宏代码,..macro表示代码的开始,..endm表示代码的结束. 15..req name..req.register.name:为寄存器定义一个别名. 16..code .code.[16|32]:指定指令代码产生的长度,.16表示 Thumb指令,.32表示 ARM指令. 17..ltorg .ltorg:表示当前往下的定义在归于当前段,并为之分配空间. 4.2 ARM GNU专有符号 1..@ 表示注释从当前位置到行尾的字符.
2..# 注释掉一整行. 3..; 新行分隔符. 4.3操作码 1..NOP nop 空操作,.相当于 MOV.r0,.r0 2..LDR ldr..,.=. 相当于 PC寄存器或其它寄存器的长转移.
3.ADR adr.. 相于 PC寄存器或其它寄存器的小范围转移.
ADRL adrl.. 相于 PC寄存器或其寄存器的中范围转移.
5可执行生成说明
5.1 lds文件说明 5.1.1主要符号说明 1..OUTPUT_FORMAT(bfdname) 指定输出可执行文件格式. 2. . OUTPUT_ARCH(bfdname) 指定输出可执行文件所运行CPU平台 3..ENTRY(symbol) 指定可执行文件的入口段
5.1.2段定义说明 1. . 段定义格式 SECTIONS.{.... 段名 :.{ 内容
} ... }
5.1.3.uboot. lds文件说明 OUTPUT_FORMAT("elf32littlearm", ."elf32littlearm", ."elf32littlearm") ;指定输出可执行文件是 elf格式,32位 ARM指令,小端 OUTPUT_ARCH(arm) ;指定输出可执行文件的平台为ARM ENTRY(_start) ;指定输出可执行文件的起始代码段为_start. SECTIONS {
..=.0x00000000..;从 0x0位置开始
..=.ALIGN(4).;代码以4字节对齐 .text.:.;指定代码段 {
cpu/arm920t/start.o..(.text).;代码的第一个代码部分
*(.text).;其它代码部分 } ..=.ALIGN(4) .rodata.:.{.*(.rodata).}.;指定只读数据段
..=.ALIGN(4); .data.:.{.*(.data).}.;指定读/写数据段
..=.ALIGN(4); .got.:.{.*(.got).}.;指定got段,.got段式是 uboot自定义的一个段,.非标准段
__u_boot_cmd_start.=...;把__u_boot_cmd_start赋值为当前位置,.即起始位置 .u_boot_cmd.:.{.*(.u_boot_cmd).}.;指定u_boot_cmd段,.uboot把所有的uboot命令放在该段. __u_boot_cmd_end.=..;把__u_boot_cmd_end赋值为当前位置,即结束位置
..=.ALIGN(4); __bss_start.=..;把__bss_start赋值为当前位置,即 bss段的开始位置 .bss.:.{.*(.bss).};指定bss段 _end.=..;把_end赋值为当前位置,即 bss段的结束位置
}
第四部分 u-boot的移植
1 u-boot的介绍及系统结构
1.1 u-boot介绍 Uboot 是德国 DENX小组的开发用于多种嵌入式CPU的bootloader程序,.UBoot 不仅仅支持嵌入式Linux 系统的引导,当前,它还支持NetBSD,.VxWorks,.QNX,.RTEMS,.ARTOS,.LynxOS嵌入式操作系统。UBoot 除 了支持PowerPC系列的处理器外,还能支持MIPS、 x86、ARM、NIOS、XScale等诸多常用系列的处理器。
1.2获取u-boot 以uboot用户登陆.
[uboot@localhost.~]#mkdir.p.dev_ home/uboot [uboot@localhost.~]#cd.dev_home/uboot 从下面地址下载uboot 的源代码。
[uboot@localhost.uboot]#tar.xjvf.uboot1.1.4. tar.bz2 [uboot@localhost.uboot]#cd.uboot1.1.4
1.3 u-boot体系结构 1.3.1 u-boot目录结构 1..目录树 [uboot@localhost.uboot1.1.4]# tree.L.1.d . |board |common |cpu |disk |doc |drivers |dtt |examples |fs |include |lib_ arm |lib_ generic |lib_ i386 |lib_ m68k |lib_ microblaze |lib_ mips |lib_ nios |lib_ nios2 |lib_ ppc |net |post |rtc `tools 2..board:和一些已有开发板有关的文件..每一个开发板都以一个子目录出现在当前目录中,比如说:SMDK2410, 子目录中存放与开发板相关的配置文件. 3..common:实现 uboot 命令行下支持的命令,每一条命令都对应一个文件。例如 bootm命令对应就是 cmd_bootm.c。 4..cpu:与特定CPU架构相关目录,每一款 Uboot 下支持的CPU在该目录下对应一个子目录,比如有子目录 arm920t等。 5..disk:对磁盘的支持。 5..doc:文档目录。Uboot 有非常完善的文档,推荐大家参考阅读。 6..drivers:Uboot 支持的设备驱动程序都放在该目录,比如各种网卡、支持CFI的Flash、串口和USB等。 7..fs:支持的文件系统,Uboot 现在支持cramfs、fat、fdos、jffs2和registerfs。 8..include:Uboot 使用的头文件,还有对各种硬件平台支持的汇编文件,系统的配置文件和对文件系统支持的 文件。该目录下configs目录有与开发板相关的配置头文件,如 smdk2410.h。该目录下的asm目录有与CPU体 系结构相关的头文件,asm对应的是 asmarm.
9..lib_xxxx:与体系结构相关的库文件。如与ARM相关的库放在lib_arm中。 10..net:与网络协议栈相关的代码,BOOTP协议、TFTP协议、RARP协议和NFS文件系统的实现。 11..tools:生成Uboot 的工具,如:mkimage,.crc等等。 2 uboot的启动过程及工作原理
2.1启动模式介绍 大多数 Boot.Loader都包含两种不同的操作模式:"启动加载"模式和"下载"模式,这种区别仅对于开发人 员才有意义。但从最终用户的角度看,Boot.Loader的作用就是用来加载操作系统,而并不存在所谓的启动加 载模式与下载工作模式的区别。
启动加载(Boot.loading)模式:这种模式也称为"自主"(Autonomous)模式。也即 Boot.Loader从目标机 上的某个固态存储设备上将操作系统加载到 RAM中运行,整个过程并没有用户的介入。这种模式是 Boot Loader的正常工作模式,因此在嵌入式产品发布的时侯,Boot.Loader显然必须工作在这种模式下。
下载(Downloading)模式:在这种模式下,目标机上的 Boot.Loader将通过串口连接或网络连接等通信手 段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被 Boot Loader保存到目标机的 RAM中,然后再被 BootLoader写到目标机上的FLASH.类固态存储设备中。Boot Loader的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 Boot Loader的这种工作模式。工作于这种模式下的 Boot.Loader通常都会向它的终端用户提供一个简单的命令 行接口。
UBoot 这样功能强大的 Boot.Loader同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行 切换。
大多数bootloader都分为阶段1(stage1)和阶段2(stage2)两大部分,uboot 也不例外。依赖于 CPU体系结构 的代码(如 CPU初始化代码等)通常都放在阶段1中且通常用汇编语言实现,而阶段2则通常用C语言来实 现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
2.2阶段1介绍 uboot 的stage1代码通常放在start.s文件中,它用汇编语言写成,其主要代码部分如下:
2.2.1定义入口 由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0 地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
1..board/crane2410/uboot. lds:..ENTRY(_start)...==>.cpu/arm920t/start.S:..globl._start 2..uboot代码区(TEXT_BASE.=.0x33F80000)定义在board/crane2410/config.mk 2.2.2设置异常向量 _start: b reset @ 0x00000000 ldr pc, _undefined_instruction @ 0x00000004 ldr pc, _software_interrupt @ 0x00000008 ldr pc, _prefetch_abort @ 0x0000000c ldr pc, _data_abort @ 0x00000010 ldr pc, _not_used @ 0x00000014 ldr pc, _irq @ 0x00000018 ldr pc, _fiq @ 0x0000001c
当发生异常时,执行cpu/arm920t/interrupts.c中定义的中断处理函数。
2.2.3设置CPU的模式为SVC模式 mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0
2.2.4关闭看门狗 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) ldr r0, =pWTCON mov r1, #0x0 @ 根据三星手册进行调置。 str r1, [r0]
2.2.5禁掉所有中断 mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0]
# if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0]
2.2.6设置以CPU的频率 默认频率为 FCLK:HCLK:PCLK = 1:2:4,默认FCLK的值为120 MHz,该值为S3C2410手册的推荐值。 ldr r0, =CLKDIVN mov r1, #3 str r1, [r0]
2.2.7设置CP15 设置CP15,.失效指令(I)Cache和数据(D)Cache后,.禁止MMU与Cache。 cpu_init_crit: mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* 失效 I/D cache, 见 S3C2410手册附录的 2-16 */ mcr p15, 0, r0, c8, c7, 0 /* 失效 TLB, 见 S3C2410手册附录的 2-18 */
/*
* 禁止 MMU 和caches, 详见S3C2410手册附录2-11 */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 /* 清除 bits 13, 9:8 (--V- --RS)
* Bit 8: Disable System Protection * Bit 7: Disable ROM Protection * Bit 13: 异常向量表基地址: 0x0000 0000 */ bic r0, r0, #0x00000087 /* 清除 bits 7, 2:0 (B--- -CAM)
* Bit 0: MMU disabled * Bit 1: Alignment Fault checking disabled * Bit 2: Data cache disabled * Bit 7: 0 = Little-endian operation */ orr r0, r0, #0x00000002 /* set bit 2 (A) Align, 1 = Fault checking enabled */ orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache, 1 = Instruction cache enabled
*/ mcr p15, 0, r0, c1, c0, 0
2.2.8配置内存区控制寄存器 配置内存区控制寄存器,寄存器的具体值通常由开发板厂商或硬件工程师提供..如果您对总线周期及外围 芯片非常熟悉,.也可以自己确定,.在UBOOT 中的设置文件是 board/crane2410/lowlevel_init.S,.该文件包含 lowleve_init程序段..详细寄存器设置及值的解释见 3.2.2.启动AXD配置开发板一节中的第5点.
mov ip, lr bl lowlevel_init mov lr, ip
2.2.9安装U-BOOT使的栈空间 下面这段代码只对不是从 Nand.Flash启动的代码段有意义,对从 Nand.Flash启动的代码,没有意义。因为 从 Nand.Flash中把UBOOT执行代码搬移到RAM,由 2.1.9中代码完成..
#ifndef CONFIG_SKIP_RELOCATE_UBOOT ... #endif stack_setup:
ldr r0, _TEXT_BASE /* 代码段的起始地址 */ sub r0, r0, #CFG_MALLOC_LEN/* 分配的动态内存区 */ sub r0, r0, #CFG_GBL_DATA_SIZE /* UBOOT开发板全局数据存放 */
#ifdef CONFIG_USE_IRQ/* 分配IRQ和FIQ栈空间 */ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif sub sp, r0, #12 /* 留下3个字为Abort */
2.2.10 BSS段清0 clear_bss: ldr r0, _bss_start/* BSS段的起始地址 */ ldr r1, _bss_end/* BSS段的结束地址 */ mov r2, #0x00000000/* BSS段置0 */
clbss_l:str r2, [r0]/* 循环清除BSS段 */ add r0, r0, #4 cmp r0, r1 ble clbss_l
2.2.11搬移Nand Flash代码 从 Nand.Flash中,.把数据拷贝到RAM,.是由 copy_myself程序段完成,.该程序段详细解释见:第七部分的3.1节.
#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
2.2.12进入C代码部分 ldr pc, _start_armboot _start_armboot: .word start_armboot
2.3阶段2的C语言代码部分 lib_arm/board.c中的start.armboot是 C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个 uboot( armboot)的主函数,该函数主要完成如下操作:
2.3.1调用一系列的初始化函数 1..指定初始函数表: init_fnc_t *init_sequence[] = {
cpu_init, board_init, interrupt_init, env_init, init_baudrate, serial_init, console_init_f, display_banner, dram_init, display_dram_config,
/* cpu的基本设置 */ /* 开发板的基本初始化 */ /* 初始化中断 */ /* 初始化环境变量 */ /* 初始化波特率 */ /* 串口通讯初始化 */ /* 控制台初始化第一阶段 */ /* 通知代码已经运行到该处 */ /* 配制可用的内存区 */
#if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2) checkboard, #endif NULL, };
执行初始化函数的代码如下:
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
2..配置可用的Flash区 flash_init ()
3..初始化内存分配函数 mem_malloc_init()
4..nand.flash初始化 #if (CONFIG_COMMANDS & CFG_CMD_NAND) puts ("NAND:"); nand_init();/* 初始化 NAND */
见第七部分3.2.3 节中的第3点nand_init()函数.
5..初始化环境变量 env_relocate.();
6. . 外围设备初始化 devices_init() 7..I2C总线初始化 i2c_init(); 8..LCD初始化 drv_lcd_init(); 9..VIDEO初始化 drv_video_init(); 10..键盘初始化 drv_keyboard_init(); 11..系统初始化 drv_system_init(); 2.3.2初始化网络设备 初始化相关网络设备,填写IP、MAC地址等。 1..设置IP地址 /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* MAC Address */
{ int i; ulong reg; char *s, *e; uchar tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp)); s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) { gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0; if (s)
s = (*e) ? e + 1 : e; } }
2.3.3进入主UBOOT命令行 进入命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。 for (;;) { main_loop (); /* 在common/main.c */ }