u-boot移植资料
================================================================================
from: http://www.mcublog.com/blog/user1/6385/archives/2006/12029.html
u-boot 移植步骤详解 |
[ 2006-5-13 14:58:56 | By: 逛逛 ] |
1 U-Boot简介 U-Boot,全称Universal Boot
Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内
核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。
但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS,
LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux,
SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX,
RTEMS,
ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、
x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作
系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11
月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工
程中心Wolfgang
Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT
LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。 选择U-Boot的理由: ① 开放源码; ② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS; ③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale; ④ 较高的可靠性和稳定性; ④ 较高的可靠性和稳定性; ⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等; ⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等; ⑦ 较为丰富的开发调试文档与强大的网络技术支持;
2 U-Boot主要目录结构 - board 目标板相关文件,主要包含SDRAM、FLASH驱动; - common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测; - cpu 与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件; - driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好) - doc U-Boot的说明文档; - examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c; - include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件; - lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件; - net 与网络功能相关的文件目录,如bootp,nfs,tftp; - post 上电自检文件目录。尚有待于进一步完善; - rtc RTC驱动程序; - tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具;
3 U-Boot支持的主要功能 U-Boot可支持的主要功能列表 系统引导 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统 支持NFS挂载、从FLASH中引导压缩或非压缩系统内核; 基本辅助功能 强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲; 支持目标板环境参数多种存储方式,如FLASH、NVRAM、EEPROM; CRC32校验,可校验FLASH中内核、RAMDISK镜像文件是否完好; 设备驱动 串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持; 上电自检功能 SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号; 特殊功能 XIP内核引导;
4 移植前的准备
(1)、首先读读uboot自带的readme文件,了解了一个大概。
(2)、看看common.h,这个文件定义了一些基本的东西,并包含了一些必要的头文件。再看看flash.h,这个文件里面定义了
flash_info_t为一个struct。包含了flash的一些属性定义。并且定义了所有的flash的属性,其中,AMD的
有:AMD_ID_LV320B,定义为“#define AMD_ID_LV320B 0x22F922F9”。
(3)、对于“./borad/at91rm9200dk/flash.c”的修改,有以下的方面: “void flash_identification(flash_info_t *info)”这个函数的目的是确认flash的型号。注意的是,这个函数里面有一些宏定义,直接读写了flash。并获得ID号。
(4)、修改:”./board/at91rm9200dk/config.mk”为 TEXT_BASE=0x21f80000 为TEXT_BASE=0x21f00000 (当然,你应该根据自己的板子来修改,和一级boot的定义的一致即可)。
(5)、再修改”./include/configs/at91rm9200dk.h”为 修改flash和SDRAM的大小。
(6)、另外一个要修改的文件是: ./borad/at91rm9200dk/flash.c。这个文件修改的部分比较的多。 a. 首先是OrgDef的定义,加上目前的flash。 b. 接下来,修改”#define FLASH_BANK_SIZE 0x200000”为自己flash的 容量 c. 在修改函数flash_identification(flash_info_t * info)里面的打印信息,这部分将在u-boot启动的时候显示。 d. 然后修改函数flash_init(void)里面对一些变量的赋值。 e. 最后修改的是函数flash_print_info(flash_info_t * info)里面实际打印的函数信息。 f. 还有一个函数需要修改,就是:“flash_erase”,这个函数要检测先前知道的flash类型是否匹配,否则,直接就返回了。把这里给注释掉。
(7)、接下来看看SDRAM的修改。 这个里面对于“SIZE”的定义都是基于字节计算的。 只要修改”./include/configs/at91rm9200dk.h”里面的 “#define PHYS_SDRAM_SIZE 0X200000”就可以了。注意,SIZE是以字节为单位的。
(8)、还有一个地方要注意 就是按照目前的设定,一级boot把u_boot加载到了SDRAM的空间为:21F00000 -> 21F16B10,这恰好是SDRAM的高端部分。另外,BSS为21F1AE34。
(9)、编译后,可以写入flash了。 a. 压缩这个u-boot.bin “gzip –c u-boot.bin > u-boot.gz” 压缩后的文件大小为: 43Kbytes b. 接着把boot.bin和u-boot.gz烧到flash里面去。 Boot.bin大约11kBytes,在flash的0x1000 0000 ~ 0x1000 3fff
5 U-Boot移植过程 ① 获得发布的最新版本U-Boot源码,与Linux内核源码类似,也是 bzip2的压缩格式。可从U-Boot的官方网站上获得; ② 阅读相关文档,主要是U-Boot源码根目录下的README文档和U-Boot官方网站的DULG(The DENX U-Boot and Linux Guide)文档。尤其是DULG文档,从如何安装建立交叉开发环境和解决U-Boot移植中常见问题都一一给出详尽的说明; ③ 订阅U-Boot用户邮件列表。在移植U-Boot过程中遇有问题,在参考相关文档和搜索U-Boot-User邮件档案库仍不能解决的情况下,第一时间提交所遇到的这些问题,众多热心的U-Boot开发人员会乐于迅速排查问题,而且很有可能,W.D本人会直接参与指导; ④ 在建立的开发环境下进行移植工作。绝大多数的开发环境是交叉开发环境。在这方面,DENX 和MontaVista均提供了完整的开发工具集; ⑤
在目标板与开发主机间接入硬件调试器。这是进行U-Boot移植应当具备且非常关键的调试工具。因为在整个U-Boot的移植工作中,尤其是初始阶段,硬
件调试器是我们了解目标板真实运行状态的唯一途径。在这方面,W.D本人和众多嵌入式开发人员倾向于使用BDI2000。一方面,其价格不如ICE调试器
昂贵,同时其可靠性高,功能强大,完全能胜任移植和调试U-Boot。另外,网上也有不少关于BDI2000调试方面的参考文档。 ⑥
如果在参考开发板上移植U-Boot,可能需要移除目标板上已有的BOOT LOADER。可以根据板上BOOT
LOADER的说明文档,先着手解决在移除当前BOOT LOADER的情况下,如何进行恢复。以便今后在需要场合能重新装入原先的BOOT
LOADER。
6. U-Boot移植方法 当前,对于U-Boot的移植方法,大致分为两种。一种是先用BDI2000创建目标板初始运行环境,将U-
Boot镜像文件u-boot.bin下载到目标板RAM中的指定位置,然后,用BDI2000进行跟踪调试。其好处是不用将U-Boot镜像文件烧写到
FLASH中去。但弊端在于对移植开发人员的移植调试技能要求较高,BDI2000的配置文件较为复杂。另外一种方法是用BDI2000先将U-Boot
镜像文件烧写到FLASH中去,然后利用GDB和BDI2000进行调试。这种方法所用BDI2000的配置文件较为简单,调试过程与U-Boot移植后
运行过程相吻合,即U-Boot先从FLASH中运行,再重载至RAM中相应位置,并从那里正式投入运行。唯一感到有些麻烦的就是需要不断烧写
FLASH。但考虑到FLASH常规擦写次数基本为10万次左右,作为移植U-Boot,不会占用太多的次数,应该不会为FLASH烧写有什么担忧。同
时,W. D本人也极力推荐使用后一种方法。笔者建议,除非U-Boot移植资深人士或有强有力的技术支持,建议采用第二种移植方法。
7. U-Boot移植主要修改的文件 从移植U-Boot最小要求-U-Boot能正常启动的角度出发,主要考虑修改如下文件: ① <目标板>.h头文件,如include/configs/RPXlite.h。可以是U-Boot源码中已有的目标板头文件,也可以是新命名的配置头文件;大多数的寄存器参数都是在这一文件中设置完成的; ② <目标板>.c文件,如board/RPXlite/RPXlite.c。它是SDRAM的驱动程序,主要完成SDRAM的UPM表设置,上电初始化。 ③ FLASH的驱动程序,如board/RPXlite/flash.c,或common/cfi_flash.c。可在参考已有FLASH驱动的基础上,结合目标板FLASH数据手册,进行适当修改; ④ 串口驱动,如修改cpu/mpc8xx/serial.c串口收发器芯片使能部分。
8. U-Boot移植要点 ①
BDI2000的配置文件。如果采用第二种移植方法,即先烧入FLASH的方法,配置项只需很少几个,就可以进行U-Boot的烧写与调试了。对PPC
8xx系列的主板,可参考DULG文档中TQM8xx的配置文件进行相应的修改。下面,笔者以美国Embedded Planet公司的RPXlite
DW板为例,给出在嵌入式Linux交叉开发环境下的BDI2000参考配置文件以作参考: ; bdiGDB configuration file for RPXlite DW or LITE_DW ; -------------------------------------------- [INIT] ; init core register WSPR 149 0x2002000F ;DER : set debug enable register ; WSPR 149 0x2006000F ;DER : enable SYSIE for BDI flash program WSPR 638 0xFA200000 ;IMMR : internal memory at 0xFA200000 WM32 0xFA200004 0xFFFFFF89 ;SYPCR [TARGET] CPUCLOCK 40000000 ;the CPU clock rate after processing the init list BDIMODE AGENT ;the BDI working mode (LOADONLY | AGENT) BREAKMODE HARD ;SOFT or HARD, HARD uses PPC hardware breakpoints [HOST] IP 173.60.120.5 FILE uImage.litedw FORMAT BIN LOAD MANUAL ;load code MANUAL or AUTO after reset DEBUGPORT 2001 START 0x0100 [FLASH] CHIPTYPE AM29BX8 ;;Flash type (AM29F | AM29BX8 | AM29BX16 | I28BX8 | I28BX16) CHIPSIZE 0x400000 ;;The size of one flash chip in bytes BUSWIDTH 32 ;The width of the flash memory bus in bits (8 | 16 | 32) WORKSPACE 0xFA202000 ; RAM buffer for fast flash programming FILE u-boot.bin ;The file to program FORMAT BIN 0x00000000 ERASE 0x00000000 BLOCK ERASE 0x00008000 BLOCK ERASE 0x00010000 BLOCK ERASE 0x00018000 BLOCK [REGS] DMM1 0xFA200000 FILE reg823.def ②
U-Boot移植参考板。这是进行U-Boot移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM的情况,以尽可能相一致为原则,先找出
一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。如RPXlite
DW板可选择U-Boot源码中RPXlite板作为U-Boot移植参考板。对U-Boot移植新手,建议依照循序渐进的原则,目标板文件名暂时先用移
植参考板的名称,在逐步熟悉U-Boot移植基础上,再考虑给目标板重新命名。在实际移植过程中,可用Linux命令查找移植参考板的特定代码,如
grep –r RPXlite ./
可确定出在U-Boot中与RPXlite板有关的代码,依此对照目标板实际进行屏蔽或修改。同时应不局限于移植参考板中的代码,要广泛借鉴U-Boot
中已有的代码更好地实现一些具体的功能。 ③
U-Boot烧写地址。不同目标板,对U-Boot在FLASH中存放地址要求不尽相同。事实上,这是由处理器中断复位向量来决定的,与主板硬件相关,对
MPC8xx主板来讲,就是由硬件配置字(HRCW)决定的。也就是说,U-Boot烧写具体位置是由硬件决定的,而不是程序设计来选择的。程序中相应
U-Boot起始地址必须与硬件所确定的硬件复位向量相吻合;如RPXlite DW板的中断复位向量设置为0x00000100。因此,
U-Boot
的BIN镜像文件必须烧写到FLASH的起始位置。事实上,大多数的PPC系列的处理器中断复位向量是0x00000100和0xfff00100。这也
是一般所说的高位启动和低位启动的BOOT
LOADER所在位置。可通过修改U-Boot源码<目标板>.h头文件中CFG_MONITOR_BASE
和board/<目标板>/config.mk中的TEXT_BASE的设置来与硬件配置相对应。 ④
CPU寄存器参数设置。根据处理器系列、类型不同,寄存器名称与作用有一定差别。必须根据目标板的实际,进行合理配置。一个较为可行和有效的方法,就是借
鉴参考移植板的配置,再根据目标板实际,进行合理修改。这是一个较费功夫和考验耐力的过程,需要仔细对照处理器各寄存器定义、参考设置、目标板实际作出选
择并不断测试。MPC8xx处理器较为关键的寄存器设置为SIUMCR、PLPRCR、SCCR、BRx、ORx。 ⑤
串口调试。能从串口输出信息,即使是乱码,也可以说U-Boot移植取得了实质性突破。依据笔者调试经历,串口是否有输出,除了与串口驱动相关外,还与
FLASH相关的寄存器设置有关。因为U-Boot是从FLASH中被引导启动的,如果FLASH设置不正确,U-Boot代码读取和执行就会出现一些问
题。因此,还需要就FLASH的相关寄存器设置进行一些参数调试。同时,要注意串口收发芯片相关引脚工作波形。依据笔者调试情况,如果串口无输出或出现乱
码,一种可能就是该芯片损坏或工作不正常。 ⑥ 与启动 FLASH相关的寄存器BR0、OR0的参数设置。应根据目标板FLASH的数据手册与BR0和OR0的相关位含义进行合理设置。这不仅关系到FLASH能否正常工作,而且与串口调试有直接的关联。 ⑦
关于CPLD电路。目标板上是否有CPLD电路丝毫不会影响U-Boot的移植与嵌入式操作系统的正常运行。事实上,CPLD电路是一个集中将板上电路的
一些逻辑关系可编程设置的一种实现方法。其本身所起的作用就是实现一些目标板所需的脉冲信号和电路逻辑,其功能完全可以用一些逻辑电路与CPU口线来实
现。 ⑧
SDRAM的驱动。串口能输出以后,U-Boot移植是否顺利基本取决于SDRAM的驱动是否正确。与串口调试相比,这部分工作更为核心,难度更大。
MPC8xx目标板SDRAM驱动涉及三部分。一是相关寄存器的设置;二是UPM表;三是SDRAM上电初始化过程。任何一部分有问题,都会影响U-
Boot、嵌入式操作系统甚至应用程序的稳定、可靠运行。所以说,SDRAM的驱动不仅关系到U-Boot本身能否正常运行,而且还与后续部分相关,是相
当关键的部分。 ⑨
补充功能的添加。在获得一个能工作的U-Boot后,就可以根据目标板和实际开发需要,添加一些其它功能支持。如以太网、LCD、NVRAM等。与串口和
SDRAM调试相比,在已有基础之上,这些功能添加还是较为容易的。大多只是在参考现有源码的基础上,进行一些修改和配置。 另外,如果在自主设计的主板上移植U-Boot,那么除了考虑上述软件因素以外,还需要排查目标板硬件可能存在的问题。如原理设计、PCB布线、元件好坏。在移植过程中,敏锐判断出故障态是硬件还是软件问题,往往是关系到项目进度甚至移植成败的关键,相应难度会增加许多。
下面以移植u-boot 到44B0开发板的步骤为例,移植中上仅需要修改和硬件相关的部分。在代码结构上: 1)
在board 目录下创建ev44b0ii 目录,创建ev44b0ii.c
以及flash.c,memsetup.S,u-boot.lds等。不需要从零开始,可选择一个相似的目录,直接复制过来,修改文件名以及内容。我在移
植u-boot 过程中,选择的是ep7312 目录。由于u-boot 已经包含基于s3c24b0 的开发板目录,作为参考,也可以复制相应的目录。 2) 在cpu 目录下创建arm7tdmi 目录,主要包含start.S,interrupts.c 以及cpu.c,serial.c几个文件。同样不需要从零开始建立文件,直接从arm720t 复制,然后修改相应内容。 3) 在include/configs 目录下添加ev44b0ii.h,在这里放上全局的宏定义等。 4) 找到u-boot 根目录下Makefile 修改加入 ev44b0ii_config : unconfig @./mkconfig $(@:_config=) arm arm7tdmi ev44b0ii 5) 运行make ev44bii_config,如果没有错误就可以开始硬件相关代码移植的工作
3. u-boot 的体系结构 1) 总体结构 u-boot
是一个层次式结构。从上图也可以看出,做移植工作的软件人员应当提供串口驱动(UART Driver),以太网驱动(Ethernet
Driver),Flash 驱动(Flash 驱动),USB 驱动(USB Driver)。目前,通过USB
口下载程序显得不是十分必要,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用,command
通过串口提供人机界面。我们可以使用一些命令做一些常用的工作,比如内存查看命令md。 Kermit 应用主要用来支持使用串口通过超级终端下载应用程序。TFTP 则是通过网络方式来下载应用程序,例如uclinux 操作系统。 2) 内存分布 在flash
rom 中内存分布图ev44b0ii 的flash 大小2M(8bits),现在将0-40000 共256k 作为u-boot
的存储空间。由于u-boot 中有一些环境变量,例如ip 地址,引导文件名等,可在命令行通过setenv 配置好,通过saveenv
保存在40000-50000(共64k)这段空间里。如果存在保存好的环境变量,u-boot
引导将直接使用这些环境变量。正如从代码分析中可以看到,我们会把flash 引导代码搬移到DRAM 中运行。下图给出u-boot
的代码在DRAM中的位置。引导代码u-boot 将从0x0000 0000 处搬移到0x0C700000 处。特别注意的由于ev44b0ii
uclinux 中断向量程序地址在0x0c00 0000 处,所以不能将程序下载到0x0c00 0000 出,通常下载到0x0c08 0000
处。
4. start.S 代码结构 1) 定义入口 一个可执行的Image 必须有一个入口点并且只能有一个唯一的全局入口,通常这个入口放在Rom(flash)的0x0 地址。例如start.S 中的 .globl _start _start: 值得注意的是你必须告诉编译器知道这个入口,这个工作主要是修改连接器脚本文件(lds)。 2) 设置异常向量(Exception Vector) 异
常向量表,也可称为中断向量表,必须是从0
地址开始,连续的存放。如下面的就包括了复位(reset),未定义处理(undef),软件中断(SWI),预去指令错误(Pabort),数据错误
(Dabort),保留,以及IRQ,FIQ 等。注意这里的值必须与uclinux 的vector_base 一致。这就是说如果uclinux
中vector_base(include/armnommu/proc-armv/system.h)定义为0x0c00
0000,则HandleUndef 应该在 0x0c00 0004。 b reset //for debug ldr pc,=HandleUndef ldr pc,=HandleSWI ldr pc,=HandlePabort ldr pc,=HandleDabort b . ldr pc,=HandleIRQ ldr pc,=HandleFIQ ldr pc,=HandleEINT0 /*mGA H/W interrupt vector table*/ ldr pc,=HandleEINT1 ldr pc,=HandleEINT2 ldr pc,=HandleEINT3 ldr pc,=HandleEINT4567 ldr pc,=HandleTICK /*mGA*/ b . b . ldr pc,=HandleZDMA0 /*mGB*/ ldr pc,=HandleZDMA1 ldr pc,=HandleBDMA0 ldr pc,=HandleBDMA1 ldr pc,=HandleWDT ldr pc,=HandleUERR01 /*mGB*/ b . b . ldr pc,=HandleTIMER0 /*mGC*/ ldr pc,=HandleTIMER1 ldr pc,=HandleTIMER2 ldr pc,=HandleTIMER3 ldr pc,=HandleTIMER4 ldr pc,=HandleTIMER5 /*mGC*/ b . b . ldr pc,=HandleURXD0 /*mGD*/ ldr pc,=HandleURXD1 ldr pc,=HandleIIC ldr pc,=HandleSIO ldr pc,=HandleUTXD0 ldr pc,=HandleUTXD1 /*mGD*/ b . b . ldr pc,=HandleRTC /*mGKA*/ b . b . b . b . b . /*mGKA*/ b . b . ldr pc,=HandleADC /*mGKB*/ b . b . b . b . b . /*mGKB*/ b . b . ldr pc,=EnterPWDN 作为对照:请看以上标记的值: .equ HandleReset, 0xc000000 .equ HandleUndef,0xc000004 .equ HandleSWI, 0xc000008 .equ HandlePabort, 0xc00000c .equ HandleDabort, 0xc000010 .equ HandleReserved, 0xc000014 .equ HandleIRQ, 0xc000018 .equ HandleFIQ, 0xc00001c /*the value is different with an address you think it may be. *IntVectorTable */ .equ HandleADC, 0xc000020 .equ HandleRTC, 0xc000024 .equ HandleUTXD1, 0xc000028 .equ HandleUTXD0, 0xc00002c .equ HandleSIO, 0xc000030 .equ HandleIIC, 0xc000034 .equ HandleURXD1, 0xc000038 .equ HandleURXD0, 0xc00003c .equ HandleTIMER5, 0xc000040 .equ HandleTIMER4, 0xc000044 .equ HandleTIMER3, 0xc000048 .equ HandleTIMER2, 0xc00004c .equ HandleTIMER1, 0xc000050 .equ HandleTIMER0, 0xc000054 .equ HandleUERR01, 0xc000058 .equ HandleWDT, 0xc00005c .equ HandleBDMA1, 0xc000060 .equ HandleBDMA0, 0xc000064 .equ HandleZDMA1, 0xc000068 .equ HandleZDMA0, 0xc00006c .equ HandleTICK, 0xc000070 .equ HandleEINT4567, 0xc000074 .equ HandleEINT3, 0xc000078 .equ HandleEINT2, 0xc00007c .equ HandleEINT1, 0xc000080 .equ HandleEINT0, 0xc000084 3) 初始化CPU 相关的pll,clock,中断控制寄存器 依次为关闭watch dog timer,关闭中断,设置LockTime,PLL(phase lock loop),以及时钟。 这些值(除了LOCKTIME)都可从Samsung 44b0 的手册中查到。 ldr r0,WTCON //watch dog disable ldr r1,=0x0 str r1,[r0] ldr r0,INTMSK ldr r1,MASKALL //all interrupt disable str r1,[r0] /***************************************************** * Set clock control registers * *****************************************************/ ldr r0,LOCKTIME ldr r1,=800 // count = t_lock * Fin (t_lock=200us, Fin=4MHz) = 800 str r1,[r0] ldr r0,PLLCON /*temporary setting of PLL*/ ldr r1,PLLCON_DAT /*Fin=10MHz,Fout=40MHz or 60MHz*/ str r1,[r0] ldr r0,CLKCON ldr r1,=0x7ff8 //All unit block CLK enable str r1,[r0] 4) 初始化内存控制器 内存控制器,主要通过设置13 个从1c80000 开始的寄存器来设置,包括总线宽度, 8 个内存bank,bank 大小,sclk,以及两个bank mode。 /***************************************************** * Set memory control registers * *****************************************************/ memsetup: adr r0,SMRDATA ldmia r0,{r1-r13} ldr r0,=0x01c80000 //BWSCON Address stmia r0,{r1-r13} 5) 将rom 中的程序复制到RAM 中 首先利用PC 取得bootloader 在flash 的起始地址,再通过标号之差计算出这个程序代 码的大小。这些标号,编译器会在连接(link)的时候生成正确的分布的值。取得正 确信息后,通过寄存器(r3 到r10)做为复制的中间媒介,将代码复制到RAM 中。 relocate: /* * relocate armboot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r2, _armboot_start ldr r3, _armboot_end sub r2, r3, r2 /* r2 <- size of armboot */ ldr r1, _TEXT_BASE /* r1 <- destination address */ add r2, r0, r2 /* r2 <- source end address */ /* * r0 = source address * r1 = target address * r2 = source end address */ copy_loop: ldmia r0!, {r3-r10} stmia r1!, {r3-r10} cmp r0, r2 ble copy_loop 6) 初始化堆栈 进入各种模式设置相应模式的堆栈。 InitStacks: /*Don't use DRAM,such as stmfd,ldmfd...... SVCstack is initialized before*/ mrs r0,cpsr bic r0,r0,#0X1F orr r1,r0,#0xDB /*UNDEFMODE|NOINT*/ msr cpsr,r1 /*UndefMode*/ ldr sp,UndefStack orr r1,r0,#0XD7 /*ABORTMODE|NOINT*/ msr cpsr,r1 /*AbortMode*/ ldr sp,AbortStack orr r1,r0,#0XD2 /*IRQMODE|NOINT*/ msr cpsr,r1 /*IRQMode*/ ldr sp,IRQStack orr r1,r0,#0XD1 /*FIQMODE|NOINT*/ msr cpsr,r1 /*FIQMode*/ ldr sp,FIQStack bic r0,r0,#0XDF /*MODEMASK|NOINT*/ orr r1,r0,#0X13 msr cpsr,r1 /*SVCMode*/ ldr sp,SVCStack 7) 转到RAM 中执行 使用指令ldr,pc,RAM 中C 函数地址就可以转到RAM 中去执行。 5. 系统初始化部分 1. 串口部分 串
口的设置主要包括初始化串口部分,值得注意的串口的Baudrate 与时钟MCLK 有很大关系,是通过:rUBRDIV0=(
(int)(MCLK/16./(gd ->baudrate) + 0.5) -1
)计算得出。这可以在手册中查到。其他的函数包括发送,接收。这个时候没有中断,是通过循环等待来判断是否动作完成。 例如,接收函数: while(!(rUTRSTAT0 & 0x1)); //Receive data read return RdURXH0(); 2. 时钟部分 实现了延时函数udelay。 这里的get_timer 由于没有使用中断,是使用全局变量来累加的。 3. flash 部分 flash 作为内存的一部分,读肯定没有问题,关键是flash 的写部分。 Flash 的写必须先擦除,然后再写。 unsigned long flash_init (void) { int i; u16 manId,devId; //first we init it as unknown,even if you forget assign it below,it's not a problem for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){ flash_info[i].flash_id = FLASH_UNKNOWN; flash_info[i].sector_count=CFG_MAX_FLASH_SECT; } /*check manId,devId*/ _RESET(); _WR(0x555,0xaa); _WR(0x2aa,0x55); _WR(0x555,0x90); manId=_RD(0x0); _WR(0x555,0xaa); _WR(0x2aa,0x55); _WR(0x555,0x90); devId=_RD(0x1); _RESET(); printf("flashn"); printf("Manufacture ID=%4x(0x0004), Device ID(0x22c4)=%4xn",manId,devId); if(manId!=0x0004 && devId!=0x22c4){ printf("flash check faliluren"); return 0; }else{ for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){ flash_info[i].flash_id=FLASH_AM160T;/*In fact it is fujitu,I only don't want to modify common files*/ } } /* Setup offsets */ flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); /* zhangyy comment #if CFG_MONITOR_BASE >= CFG_FLASH_BASE //onitor protection ON by default flash_protect(FLAG_PROTECT_SET, CFG_MONITOR_BASE, CFG_MONITOR_BASE+monitor_flash_len-1, &flash_info[0]); #endif */ flash_info[0].size =PHYS_FLASH_SIZE; return (PHYS_FLASH_SIZE); } flash_init 完成初始化部分,这里的主要目的是检验flash 的型号是否正确。 int flash_erase (flash_info_t *info, int s_first, int s_last) { volatile unsigned char *addr = (volatile unsigned char *)(info->start[0]); int flag, prot, sect, l_sect; //ulong start, now, last; u32 targetAddr; u32 targetSize; /*zyy note:It is required and can't be omitted*/ rNCACHBE0=( (0x2000000>>12)<<16 )|(0>>12); //flash area(Bank0) must be non-cachable area. rSYSCFG=rSYSCFG & (~0x8); //write buffer has to be off for proper timing. if ((s_first < 0) || (s_first > s_last)) { if (info->flash_id == FLASH_UNKNOWN) { printf ("- missingn"); } else { printf ("- no sectors to erasen"); } return 1; } if ((info->flash_id == FLASH_UNKNOWN) || (info->flash_id > FLASH_AMD_COMP)) { printf ("Can't erase unknown flash type - abortedn"); return 1; } prot = 0; for (sect=s_first; sect<=s_last; ++sect) { if (info->protect[sect]) { prot++; } } if (prot) { printf ("- Warning: %d protected sectors will not be erased!n", prot); } else { printf ("n"); } l_sect = -1; /* Disable interrupts which might cause a timeout here */ flag = disable_interrupts(); /* Start erase on unprotected sectors */ for (sect = s_first; sect<=s_last; sect++) { if (info->protect[sect] == 0) {/* not protected */ targetAddr=0x10000*sect; if(targetAddr<0x1F0000) targetSize=0x10000; else if(targetAddr<0x1F8000) targetSize=0x8000; else if(targetAddr<0x1FC000) targetSize=0x2000; else targetSize=0x4000; F29LV160_EraseSector(targetAddr); l_sect = sect; if(!BlankCheck(targetAddr, targetSize)) printf("BlankCheck Errorn"); } } /* re-enable interrupts if necessary */ if (flag) enable_interrupts(); /* wait at least 80us - let's wait 1 ms */ udelay (1000); /* *We wait for the last triggered sector */ if (l_sect < 0) goto DONE; DONE: printf (" donen"); return 0; } int BlankCheck(int targetAddr,int targetSize) { int i,j; for(i=0;i{ j=*((u16 *)(i+targetAddr)); if( j!=0xffff) { printf("E:%x=%xn",(i+targetAddr),j); return 0; } } return 1; } flash_erase 擦除flash,BlankCheck 则检查该部分内容是否擦除成功。 /*----------------------------------------------------------------------- *Write a word to Flash, returns: * 0 - OK * 1 - write timeout * 2 - Flash not erased */ static int write_word (flash_info_t *info, ulong dest, ulong data) { volatile u16 *tempPt; /*zhangyy note:because of compatiblity of function,I use low & hi*/ u16 low = data & 0xffff; u16 high = (data >> 16) & 0xffff; low=swap_16(low); high=swap_16(high); tempPt=(volatile u16 *)dest; _WR(0x555,0xaa); _WR(0x2aa,0x55); _WR(0x555,0xa0); *tempPt=high; _WAIT(); _WR(0x555,0xaa); _WR(0x2aa,0x55); _WR(0x555,0xa0); *(tempPt+1)=low; _WAIT(); return 0; } wirte_word 则想flash 里面写入unsigned long 类型的data,因为flash 一次只能写入16bits,所以这里分两次写入。 |
================================================================================
from: http://www.mcublog.com/blog/user1/6385/archives/2006/12026.html
一步一步教你在skyeye上运行uboot |
[ 2006-5-13 14:23:43 | By: 逛逛 ] |
By faif
1. 简介
skyeye
是一个很好的,基于各种ARM系列CPU的,SOC和主板级的模拟器。uboot是一个可以在各种cpu(arm,mips,powerpc)的主板上运
行的引导程序,相当于PC机的BIOS但是又远远的强于普通的BIOS,比如支持网络引导,引导各种内核,甚至一个简单的shell,等等。他们两个都是
基于GPL的开源自由软件。
这篇文章教你怎样在最少量的修改代码的情况下,用skyeye模拟EP7312并在上面运行uboot,给接触嵌入系统的新手一个感性的认识。
2. 建立开发环境
2.1 skyeye模拟器的安装
开
发环境是建立在Linux上的。首先下载安装skyeyes-0.8.5.1的源代码包,解压,按照里面的readme安装,注意你的linux要有
gtk的支持。安装的时候要以root的身份。在各种linux发行版下的安装注意事项参照论坛的相关帖子。安装成功以后,把skyeye的目标目录加入
你的路径,这样你就可以在任何目录下执行skyeye模拟器了。
2.2 交叉编译器的安装
交叉编译器是运行在主机
上编译另外一种体系结构的编译器。比如,我的主机是linux在x86上,我现在要编译基于ARM的代码,所以我就不能用普通的编译器而需要交叉编译器。
我曾经试过自己从gcc的源代码构建交叉编译器,很麻烦和耗时。uboot的作者同样也开发了一个很好的交叉编译器叫ELDK(Embedded
linux development kit)。我就使用这个,当然你也可以使用其他嵌入式公司提供的。你可以从以下的网址查看提供ELDK的镜像:
ELDK Availability:
ELDK有三个版本分别编译MIPS,PPC和ARM。我从下面的镜像下载了基于ARM的交叉编译器:
文件为"arm-2004-11-09.iso",它支持ARM7, ARM9, XScale, AT91RM9200 and other ARM based systems。
安装交叉编译器,我将交叉编译器安装到自己的目录“/opt/x86-arm/”里面:
代码: |
/mnt/cdrom/install -d /opt/x86-arm/ |
等待安装结束以后设置好用户环境:
代码: |
export PATH="${PATH}:/opt/x86-arm/usr/bin:/opt/x86-arm/bin" export CROSS_COMPILE=arm-linux-
|
这样你在任何目录也可以访问交叉编译器了。
测试:
代码: |
arm-linux-gcc -o testarm test.c file testarm testarm:
ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.2.5,
dynamically linked (uses shared libs), not stripped.
|
说明你编译好的文件是ARM上的代码了。你可以用arm-linux-gcc来编译你的文件了。
3. 修改Uboot
从uboot的网站上可以下载到最新的uboot源代码,你可以从以下的网址下载
ftp://ftp.denx.de/pub/u-boot/
uboot
的源码结构清晰,注释详细,是学习嵌入系统的很好的例子。我下载的是最新的U-Boot-1.1.2。因为我们要模拟EP7312的芯片,而uboot已
经支持一个基于EP7312的板子了,所以我们只要对uboot里面有关EP7312的板子的配置略作修改就可以了。uboot里面有关主板的配置文件都
在"include/configs/.h"下,所以我们找到include/configs/ep7312.h,
对它进行修改。
代码: |
找到 #define CONFIG_DRIVER_CS8900 1 改为 #define CONFIG_DRIVER_CS8900 0
找到 #define CONFIG_COMMANDS (CONFIG_CMD_DFL | CFG_CMD_JFFS2) 改为 #define CONFIG_COMMANDS (CONFIG_CMD_DFL) /*Skyeye doesn't have jffs2*/ |
然后回到uboot的根目录下,配置,编译:
代码: |
make ep7312_config make all
|
等待结束以后我们会发现u-boot.bin和u-boot两个文件,其中u-boot.bin是raw的二进制文件。u-boot是ELF格式的。
4. 配置skyeye,并运行uboot
首先,新建一个目录代表你的EP7312的主板。这样也可以保持文件的清洁和有序。
将你刚才编译成功的u-boot.bin拷贝到这个目录下来。skyeye支持raw binary和ELF的格式,这里我们用raw binary的格式。
编辑skyeye.conf,这个文件是用来配置主板的,详细说明见skyeye的相关文档。我的skyeye.conf如下:
代码: |
#skyeye config file for uboot cpu: arm720t mach: ep7312
mem_bank: map=I, type=RW, addr=0x80000000, size=0x00010000
#skyeye for uboot flash 16M bank 1 mem_bank: map=M, type=RW, addr=0x00000000, size=0x01000000, file=./u-boot.bin,boot=yes
#skyeye for uboot sdram 16m bank 1 mem_bank: map=M, type=RW, addr=0xc0000000, size=0x01000000
|
注意这里的内存的地址和容量的分配都是根据uboot里面的ep7312的配置文件调整的。这样也可以是我们对uboot的代码修改做到最小。
这时候你的skyeye-ep7312主板就配置好了。你可以试着运行了。在你现在的目录下打入:
然后在skyeye的界面下打入:
这时候你可以看到uboot的启动界面,和提示符,如果你键入“hlep”,可以查看所有uboot支持的命令,键入“version”可以查看当前uboot的版本,等等。
5. 进一步的工作
由于本文是最基本的介绍性的一步一步的指导。有很多工作还要去尝试。比如:
× 由于现在skyeye还不支持flash内存,所以我们是不是可以修改uboot上ep7312相对flash的代码来临时满足我们的需要,不然的话,对于在uboot上面的环境参数的设置,我们只能去修改源码里面的缺省参数。
× Uboot支持8019AS的以太网控制器,skyeye也支持了这个硬件的模拟,我们要进一步的使uboot的网络也在skyeye上用起来?
× 对于模拟flash的开发也可以用uboot来测试。uboot里面各种板子有大量的flash驱动程序。
还望各位高手指教,我进一步修改和提高。
================================================================================
from:
u-boot启动过程分析——基于lpc2210的移植代码
作者:刘咖,讲师。
u-boot是一种普遍用于嵌入式系统中的Bootloader。
Bootloader介绍
Bootloader是进行嵌入式开发必然会接触的一个概念,它是嵌入式学院<>二期课程中嵌入式linux系统开发方面的重要内容。本篇文章主要讲解Bootloader的基本概念以及内部原理,这部分内容的掌握将对嵌入式linux系统开发的学习非常有帮助!
Bootloader的定义:Bootloader是在操作系统运行之前执行的一小段程序,通过这一小段程序,我们可以初始化硬件设备、建立内存空间的映
射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。意思就是说如果我们要想让一个操作系统在我们的板子上运转起来,我们就必须首先对
我们的板子进行一些基本配置和初始化,然后才可以将操作系统引导进来运行。具体在Bootloader中完成了哪些操作我们会在后面分析到,这里我们先来
回忆一下PC的体系结构:PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OS Boot
Loader(比如LILO和GRUB等)一起组成的,BIOS在完成硬件检测和资源分配后,将硬盘MBR中的Boot
Loader读到系统的RAM中,然后将控制权交给OS Boot Loader。Boot
Loader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入口点去运行,即开始启动操作系统。在嵌入式系统中,通常并没有像
BIOS那样的固件程序(注:有的嵌入式cpu也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由Boot
Loader来完成。比如在一个基于ARM7TDMI
core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的Boot
Loader程序。(先想一下,通用PC和嵌入式系统为何会在此处存在如此的差异呢?)
Bootloader是基于特定硬件平台来实现的,因此几乎不可能为所有的嵌入式系统建立一个通用的Bootloader,不同的处理器架构都有不同的
Bootloader,Bootloader不但依赖于cpu的体系结构,还依赖于嵌入式系统板级设备的配置。对于2块不同的板子而言,即使他们使用的是
相同的处理器,要想让运行在一块板子上的Bootloader程序也能运行在另一块板子上,一般也需要修改Bootloader的源程序。
Bootloader的启动方式
Bootloader的启动方式主要有网络启动方式、磁盘启动方式和Flash启动方式。
1、网络启动方式
图1 Bootloader网络启动方式示意图
如图1所示,里面主机和目标板,他们中间通过网络来连接,首先目标板的DHCP/BIOS通过BOOTP服务来为Bootloader分配IP地址,配置
网络参数,这样才能支持网络传输功能。我们使用的u-boot可以直接设置网络参数,因此这里就不用使用DHCP的方式动态分配IP了。接下来目标板的
Bootloader通过TFTP服务将内核映像下载到目标板上,然后通过网络文件系统来建立主机与目标板之间的文件通信过程,之后的系统更新通常也是使
用Boot Loader的这种工作模式。工作于这种模式下的Boot Loader通常都会向它的终端用户提供一个简单的命令行接口。
2、磁盘启动方式
这种方式主要是用在台式机和服务器上的,这些计算机都使用BIOS引导,并且使用磁盘作为存储介质,这里面两个重要的用来启动linux的有LILO和GRUB,这里就不再具体说明了。
3、Flash启动方式
这是我们最常用的方式。Flash有NOR Flash和NAND Flash两种。NOR
Flash可以支持随机访问,所以代码可以直接在Flash上执行,Bootloader一般是存储在Flash芯片上的。另外Flash上还存储着参
数、内核映像和文件系统。这种启动方式与网络启动方式之间的不同之处就在于,在网络启动方式中,内核映像和文件系统首先是放在主机上的,然后经过网络传输
下载进目标板的,而这种启动方式中内核映像和文件系统则直接是放在Flash中的,这两点在我们u-boot的使用过程中都用到了。
U-boot的定义
U-boot,全称Universal Boot
Loader,是由DENX小组的开发的遵循GPL条款的开放源码项目,它的主要功能是完成硬件设备初始化、操作系统代码搬运,并提供一个控制台及一个指
令集在操作系统运行前操控硬件设备。U-boot之所以这么通用,原因是他具有很多特点:开放源代码、支持多种嵌入式操作系统内核、支持多种处理器系列、
较高的稳定性、高度灵活的功能设置、丰富的设备驱动源码以及较为丰富的开发调试文档与强大的网络技术支持。另外u-boot对操作系统和产品研发提供了灵
活丰富的支持,主要表现在:可以引导压缩或非压缩系统内核,可以灵活设置/传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,支
持多种文件系统,支持多种目标板环境参数存储介质,采用CRC32校验,可校验内核及镜像文件是否完好,提供多种控制台接口,使用户可以在不需要ICE的
情况下通过串口/以太网/USB等接口下载数据并烧录到存储设备中去(这个功能在实际的产品中是很实用的,尤其是在软件现场升级的时候),以及提供丰富的
设备驱动等。
u-boot源代码的目录结构
1、board中存放于开发板相关的配置文件,每一个开发板都以子文件夹的形式出现。
2、Commom文件夹实现u-boot行下支持的命令,每一个命令对应一个文件。
3、cpu中存放特定cpu架构相关的目录,每一款cpu架构都对应了一个子目录。
4、Doc是文档目录,有u-boot非常完善的文档。
5、Drivers中是u-boot支持的各种设备的驱动程序。
6、Fs是支持的文件系统,其中最常用的是JFFS2文件系统。
7、Include文件夹是u-boot使用的头文件,还有各种硬件平台支持的汇编文件,系统配置文件和文件系统支持的文件。
8、Net是与网络协议相关的代码,bootp协议、TFTP协议、NFS文件系统得实现。
9、Tooles是生成U-boot的工具。
对u-boot的目录有了一些了解后,分析启动代码的过程就方便多了,其中比较重要的目录就是/board、/cpu、/drivers和
/include目录,如果想实现u-boot在一个平台上的移植,就要对这些目录进行深入的分析。
u-boot的启动过程
系统启动的入口点。既然我们现在要分析u-boot的启动过程,就必须先找到u-boot最先实现的是哪些代码,最先完成的是哪些任务。另一方面一个可执
行的image必须有一个入口点,并且只能有一个全局入口点,所以要通知编译器这个入口在哪里。由此我们可以找到程序的入口点是在/board
/lpc2210/u-boot.lds中指定的,其中ENTRY(_start)说明程序从_start开始运行,而他指向的是cpu
/arm7tdmi/start.o文件。因为我们用的是ARM7TDMI的cpu架构,在复位后从地址0x00000000取它的第一条指令,所以我们
将Flash映射到这个地址上,这样在系统加电后,cpu将首先执行u-boot程序。
u-boot的启动过程是多阶段实现的,分了两个阶段。依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1中,而且通常都是用汇编
语言来实现,以达到短小精悍的目的。而stage2则通常是用C语言来实现的,这样可以实现复杂的功能,而且代码具有更好的可读性和可移植性。
下面我们先详细分析下stage1中的代码,如图2所示:
图2 Start.s程序流程
代码真正开始是在_start,设置异常向量表,这样在cpu发生异常时就跳转到/cpu/arm7tdmi/interrupts中去执行相应得中断代
码。在interrupts文件中大部分的异常代码都没有实现具体的功能,只是打印一些异常消息,其中关键的是reset中断代码,跳到reset入口地
址。
reset复位入口之前有一些段的声明。在reset中,首先是将cpu设置为svc32模式下,并屏蔽所有irq和fiq。在u-boot中除了定时器
使用了中断外,其他的基本上都不需要使用中断,比如串口通信和网络等通信等,在u-boot中只要完成一些简单的通信就可以了,所以在这里屏蔽掉了所有的
中断响应。
初始化外部总线。这部分首先设置了I/O口功能,包括串口、网络接口等的设置,其他I/O口都设置为GPIO。然后设置BCFG0~BCFG3,即外部总
线控制器。这里bank0对应Flash,设置为16位宽度,总线速度设为最慢,以实现稳定的操作;Bank1对应DRAM,设置和Flash相
同;Bank2对应RTL8019。
接下来是cpu关键设置,包括系统重映射(告诉处理器在系统发生中断的时候到外部存储器中去读取中断向量表)和系统频率。
lowlevel_init,设定RAM的时序,并将中断控制器清零。这些部分和特定的平台有关,但大致的流程都是一样的。
下面就是代码的搬移阶段了。为了获得更快的执行速度,通常把stage2加载到RAM空间中来执行,因此必须为加载Boot
Loader的stage2准备好一段可用的RAM空间范围。空间大小最好是memory
page大小(通常是4KB)的倍数,一般而言,1M的RAM空间已经足够了。flash中存储的u-boot可执行文件中,代码段、数据段以及BSS段
都是首尾相连存储的,所以在计算搬移大小的时候就是利用了用BSS段的首地址减去代码的首地址,这样算出来的就是实际使用的空间。程序用一个循环将代码搬
移到0x81180000,即RAM底端1M空间用来存储代码。然后程序继续将中断向量表搬到RAM的顶端。由于stage2通常是C语言执行代码,所以
还要建立堆栈去。在堆栈区之前还要将malloc分配的空间以及全局数据所需的空间空下来,他们的大小是由宏定义给出的,可以在相应位置修改。基本内存分
布图:
图3 搬移后内存分布情况图
接下来是u-boot启动的第二个阶段,是用c代码写的,这部分是一些相对变化不大的部分,我们针对不同的板子改变它调用的一些初始化函数,并且通过设置
一些宏定义来改变初始化的流程,所以这些代码在移植的过程中并不需要修改,也是错误相对较少出现的文件。在文件的开始先是定义了一个函数指针数组,通过这
个数组,程序通过一个循环来按顺序进行常规的初始化,并在其后通过一些宏定义来初始化一些特定的设备。在最后程序进入一个循环,main_loop。这个
循环接收用户输入的命令,以设置参数或者进行启动引导。
本篇文章将分析重点放在了前面的start.s上,是因为这部分无论在移植还是在调试过程中都是最容易出问题的地方,要解决问题就需要程序员对代码进行修改,所以在这里简单介绍了一下start.s的基本流程,希望能对大家有所帮助。
================================================================================
from: http://www.liuguo.net/mcu/d/200703/1356.html
用U-BOOT构建嵌入式系统的引导装载程序
摘要 BootLoader(引导装载程序)是嵌入式系统软件开发的第一个环节,它把操作系统和硬件平台衔接在一起,
对于嵌入式系统的后续软件开发十分重要,在整个开发中也占有相当大的比例。U-BOOT是当前比较流行、
功能强大的BootLoader,可以支持多种体系结构。LH7A400是Sharp公司生产的一款基于ARM922T内核的32位
RISC芯片,本文详细介绍U-BOOT的功能、特点以及在LH7A400处理器上的移植过程。
关键词 BootLoader U-BOOT移植 LH7A400 ARM922T
引言
本文以U-BOOT为例,介绍了如何在ARM9开发板上移植BootLoader的过程。LH7A400学习板是旋极公司推出
的一款高性能嵌入式开发板,其采用的处理器LH7A400是Sharp公司生产的一款基于ARM922T内核的32位RISC芯片。
该芯片集成了高性能的32位RISC处理器核ARM922T(运算速度200MHz,总线速度100MHz) ,能使处理速度达到
每秒220百万条指令(MIPS),能耗为1.33mW/MIPS,可以在低电压状态下工作(核心1.8V,输入/输出3.3 V),片
内带有锁相回路(PLL)和低能耗核心。此外该芯片还包括: 16KB 高速缓存(Cache), 存储器管理单元(MMU),
80KB 静态存储器(SRAM), 彩色液晶显示控制器(LCD), 直接存储控制器(10通道DMA), 异步串行口控制器(UART),
同步串行口控制器(SSP), PCMCIA控制器, AC97声音控制器, 智能卡控制器, 多媒体卡控制器, 电池控制器,
USB控制器和时钟/供电管理器。值得一提的是,LH7A400是一款宽温芯片,其工作温度范围为-40℃~+85℃
(降低时钟频率),可广泛应用于无线手持设备、智能电话、PDA、家庭娱乐控制器、PocketPC及各种工控设备。
该学习板还包括如下硬件:由2片16位Flash (32MB)和2片16位的SDRAM(64M)构成32位宽的高速存储器结构;
10/100M自适应网络芯片DM9000;Sharp 3.5’TFT LCD彩屏;触摸屏;USB Host/Device;CF卡插槽;全功能JTAG接口等。
1 U-BOOT简介
U-BOOT是由德国的工程师Wolfgang Denk从8XXROM代码发展而来的,它支持很多处理器,比如PowerPC、ARM、
MIPS和x86。目前,U-BOOT源代码在 sourceforge网站的社区服务器中,Internet上有一群自由开发人员对其进
行维护和开发,它的项目主页是。U-BOOT的最新版本源代码可以在
Sourceforge的CVS服务器中匿名获得。
#cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/U-BOOT login
#cvs -z6 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/U-BOOT \ co -P modulename
1.1 U-BOOT源代码目录结构
◆ board:和一些已有开发板有关的文件,比如Makefile和u-boot.lds等都和具体开发板的硬件和地址
分配有关。
◆ common:与体系结构无关的文件,实现各种命令的C文件。
◆ cpu:CPU相关文件,其中的子目录都是以U-BOOT所支持的CPU为名,比如有子目录arm926ejs、mips、
mpc8260和nios等,每个特定的子目录中都包括cpu.c和interrupt.c,start.S。其中cpu.c初始化CPU、设置
指令Cache和数据Cache 等;interrupt.c设置系统的各种中断和异常,比如快速中断、开关中断、时钟中断、
软件中断、预取中止和未定义指令等;start.S是U- BOOT启动时执行的第一个文件,它主要是设置系统堆栈
和工作方式,为进入C程序奠定基础。
◆ disk:disk驱动的分区处理代码。
◆ doc:文档。
◆ drivers:通用设备驱动程序,比如各种网卡、支持CFI的Flash、串口和USB总线等。
◆ fs:支持文件系统的文件,U-BOOT现在支持cramfs、fat、fdos、jffs2和registerfs。
◆ include:头文件,还有对各种硬件平台支持的汇编文件,系统的配置文件和对文件系统支持的文件。
◆ net:与网络有关的代码,BOOTP协议、TFTP协议、RARP协议和NFS文件系统的实现。
◆ lib_arm:与ARM体系结构相关的代码。
◆ tools:创建S-Record格式文件 和U-BOOT images的工具。
1.2 U-BOOT的特点
U-BOOT支持SCC/FEC以太网、OOTP/TFTP引导、IP和MAC的预置功能,这一点和其它BootLoader(如BLOB和
RedBoot等)类似。但U-BOOT还具有一些特有的功能。
◆ 在线读写Flash、DOC、IDE、IIC、EEROM、RTC,其它的BootLoader根本不支持IDE和DOC的在线读写。
◆ 支持串行口kermit和S-record下载代码,U-BOOT本身的工具可以把ELF32格式的可执行文件转换成为
S-record格式,直接从串口下载并执行。
◆ 识别二进制、ELF32、uImage格式的Image,对Linux引导有特别的支持。U-BOOT对Linux 内核进一步
封装为uImage。封装如下:
#{CROSS_COMPILE}-objcopy -O binary -R.note -R.comment -S vmlinux \ linux.bin
#gzip -9 linux.bin
#tools/mkimage -A arm -O linux -T kernel -C gzip -a 0xc0008000 -e\0xc0008000 -n “Linux-2.4.20”
-d linux.bin.gz /tftpboot/uImage
即在Linux内核镜像vmLinux前添加了一个特殊的头,这个头在include/image.h中定义,包括目标操作系统
的种类(比如 Linux,VxWorks等)、目标CPU的体系机构(比如ARM、PowerPC等)、映像文件压缩类型(比如gzip、
bzip2等)、加载地址、入口地址、映像名称和映像的生成时间。当系统引导时,U-BOOT会对这个文件头进行CRC
校验,如果正确,才会跳到内核执行。如下所示:
WT-ARM9# bootm 0xc1000000
## Checking Image at 0xc100000 ...
Image Name: Linux-2.4.20
Created: 2004-07-02 22:10:11 UTC
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 550196 Bytes = 537 kB = 0 MB
Load Address: 0xc0008000
Entry Point: 0xc0008000
Verifying Checksum ... OK
Uncompressing Kernel Image ……… OK
◆ 单任务软件运行环境。U-BOOT可以动态加载和运行独立的应用程序,这些独立的应用程序可以
利用U-BOOT控制台的I/O函数、内存申请和中断服务等。这些应用程序还可以在没有操作系统的情况下运行,
是测试硬件系统很好的工具。
◆ 监控(minitor)命令集:读写I/O,内存,寄存器、内存、外设测试功能等
◆ 脚本语言支持(类似BASH脚本)。利用U-BOOT中的autoscr命令,可以在U-BOOT中运行“脚本”。首先在文本
文件中输入需要执行的命令,然后用tools/mkimage封装,然后下载到开发板上,用autoscr执行就可以了。
① 编辑如下的脚本example.script。
echo
echo Network Configuration:
echo ----------------------
echo Target:
printenv ipaddr hostname
echo
echo Server:
printenv serverip rootpath
echo
② 用tools/mkimage对脚本进行封装。
# mkimage -A ARM -O linux -T script -C none -a 0 -e 0 -n "autoscr example script"
-d example.script /tftpboot/example.img
Image Name: autoscr example script
Created: Wes Sep 8 01:15:02 2004
Image Type: ARM Linux Script (uncompressed)
Data Size: 157 Bytes = 0.15 kB = 0.00 MB
Load Address: 0x00000000
Entry Point: 0x00000000
Contents:
Image 0: 149 Bytes = 0 kB = 0 MB
③ 在U-BOOT中加载并执行这个脚本。
WT-ARM9# tftp 100000 /tftpboot/example.img
ARP broadcast 1
TFTP from server 10.0.0.2; our IP address is 10.0.0.99
Filename '/tftpboot/TQM860L/example.img'.
Load address: 0x100000
Loading: #
done
Bytes transferred = 221 (dd hex)
WT-ARM9# autoscr 100000
## Executing script at 00100000
Network Configuration:
----------------------
Target:
ipaddr=10.0.0.99
hostname=arm
Server:
serverip=10.0.0.2
rootpath=/nfsroot
WT-ARM9#
◆ 支持WatchDog、LCD logo和状态指示功能等。如果系统支持splash screen,U-BOOT启动时,会把这个
图像显示到LCD上,给用户更友好的感觉。
◆ 支持MTD和文件系统。U-BOOT作为一种强大的BootLoader,它不仅支持MTD,而且可以在MTD基础上实现
多种文件系统,比如cramfs、fat和jffs2等。
◆ 支持中断。由于传统的BootLoader都分为stage1和stage2,所以在stage2中添加中断处理服务十分困难,
比如BLOB;而U-BOOT是把两个部分放到了一起,所以添加中断服务程序就很方便。
◆ 详细的开发文档。由于大多数BootLoader都是开源项目,所以文档都不是很充分。U-BOOT的维护人员
意识到了这个问题,充分记录了开发文档,所以它的移植要比BLOB等缺少文档的BootLoader方便。
2 对U-BOOT-1.1.0的修改
为了使U-BOOT-1.1.0支持新的开发板,一种简便的做法是在U-BOOT已经支持的开发板中选择一种接近的
进行修改。由于U- BOOT-1.10不支持ARM-922T内核,所以选择基于ARM-920T内核的smdk2400为模板。相关的
源代码在 board/smdk2400/下。
2.1 支持ARM-922T内核的代码修改
修改以下代码,使U-BOOT支持arm-922t内核。
① 在include/目录下新建文件arm922t.h,内容如下:
#ifndef __ARM922T_H__
#define __ARM922T_H__
#endif
② 在include/目录下新建文件wt-arm9.h,该文件描述了ARM922T中Timer、UART等寄存器的结构及若干
宏定义。具体内容要参考相关处理器手册。
③ 在cpu/目录下新建目录arm922t,将目录arm920t下的内容复制后,参考手册分别修改cpu.c、
interrupts.c和serial.c,其它文件不修改。
2.2 开发板的支持
建立自己开发板的目录和相关文件。
① 在include/configs目录中添加头文件lh7a400.h。这个文件是lh7a400开发板的配置文件,它包括开
发板的CPU、系统时钟、 RAM、Flash系统及其它相关的配置信息。其格式可参考include/configs/smdk2400.h。
② 在board/目录下新建wt-arm9目录,创建如下文件:flash.c、lhmemsetup.c、wt- arm9.c、Makefile
和u-boot.lds。
◆ flash.c。U-BOOT 读、写和删除Flash设备的源代码文件。由于不同开发板中Flash存储器的种类各不
相同,所以,修改flash.c时需参考相应的Flash芯片手册。它包括如下几个函数:
unsigned long flash_init (void ),Flash初始化;
void flash_print_info (flash_info_t *info),打印Flash信息;
int flash_erase (flash_info_t *info, int s_first, int s_last),Flash擦除;
volatile static int write_dword (flash_info_t *info, ulong dest, ulong data),Flash写入;
int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt),从内存复制数据。
◆ lhmemsetup.c。初始化时钟、SMC控制器和SDRAM控制器。
◆ wt-arm9.c。设置各种总线时钟,打开数据Cache和指令Cache,并设置相关内存参数。
◆ Makefile。直接拷贝board/smdk2400/Makefile,作如下修改:
OBJS := wt-arm9.o flash.o lhmemsetup.o
◆ u-boot.lds。设置U-BOOT中各个目标文件的连接地址,直接拷贝 board/smdk2400/u-boot.lds,作如下修改:
.text
{
cpu/arm922t/start.o (.text)
*(.text)
}
2.3 添加网口设备控制程序
在drivers/目录中添加网口设备控制程序dm9000.c 和dm9000.h,其中dm9000.c 主要包括以下函数:
int eth_init (bd_t *bd),初始化网络设备;
void eth_halt (void),关闭网络设备;
int eth_send (volatile void *packet,int len),发送数据包;
int eth_rx (void) 接收数据包。
用中断方式处理数据包的收发,因此还定义了另外两个函数:
void InitInterrupt (void) ,中断初始化;
void dm9000_irq (void) ,中断处理。
以上两个函数在cpu/arm922t/interrupts.c中被调用,最后在drivers/Makefile中加入dm9000.o。
2.4 修改Makefile
在u-boot-1.1.0/Makefile中加入
lh7a400_config : unconfig @./mkconfig $(@:_config=) arm arm922t wt-arm9
其中“arm”是CPU的种类, arm922t 是ARM CPU对应的代码目录,wt-arm9是自己开发板对应的目录。
交叉编译器安装在/opt/arm/3.3/bin/目录下,所以把CROSS_COMPILE设置成相应的路径:
export CROSS_COMPILE = /opt/arm/3.3/bin/arm-elf-
2.5 生成目标文件
先运行make clean,
[zeng@localhost u-boot-1.1.0]$make clean
然后运行make lh7a400_config,
[zeng@localhost u-boot-1.1.0]$ make lh7a400_config
Configuring for lh7a400 board...
再运行make,
[zeng@localhost u-boot-1.1.0]$make
之后会生成三个文件:
u-boot——ELF格式的文件,可以被大多数Debug程序识别;
u-boot.bin——二进制bin文件,纯粹的U-BOOT二进制执行代码,不保存ELF格式和调试信息。这个文件一般
用于烧录到用户开发板中;
u-boot.srec——Motorola S-Record格式,可以通过串行口下载到开发板中。
2.6 测试
通过JTAG口将u-boot.bin烧写到Flash的零地址,复位后执行u-boot。若运行正常,会从串口返回如下信息:
U-Boot 1.1.0 (Aug 21 2004 ?18:44:37)
U-BooT code: C3F80000 -> C3FA51A0 BSS: -> C3FA96EC
IRQ Stack: c3f1ff7c
FIQ Stack: c3f1ef7c
RAM Configuration:
Bank #0: c0000000 8 MB
Bank #1: c1000000 8 MB
……
Flash: 32 MB
In: serial
Out: serial
Err: serial
WT-ARM9 #
输入help得到所有命令列表,help command 列出该命令的功能。紧接着测试Flash和网卡,如果都正常工作的话,
表明移植U-BOOT的工作基本完成,可以接着调试内核和文件系统。
结语
BootLoader是操作系统和硬件的枢纽,它为操作系统内核的启动提供了必要的条件和参数。在移植过程中,
开发人员除了要掌握 BootLoader的结构和工作流程外,还要对相关硬件有一定的了解。目前,笔者移植的U-BOOT
已经能够稳定地运行在开发板上,而且可以通过 Flash和网络加载内核和文件系统,为后续开发,特别是驱动程序
的开发奠定了良好的基础。
================================================================================
from:
================================================================================
from:
================================================================================
from:
================================================================================
from: