****************************************************
* u-boot(uboot)在EasyARM2200和SmartARM2200上的移植 *
****************************************************
------ 浅谈《ecos增值包》在辅助开发方面的用途
2007/03/25 asdjf@163.com www.armecos.com
现在想学习GNU/Linux的网友越来越多,但是很多人找不到切入点,如何才能利用现有的资源轻松进入呢?如果你手头有EasyARM2200和/或SmartARM2200开发板,那么,下面**介绍的方法或许对你有用......
--------------------
| 学习u-boot的好处 |
--------------------
好处可多啦:
(1)学习GNU开发工具链的使用,如:makefile写法、常用shell命令、gcc等;
(2)学习的副产品---u-boot可以用来引导uCLinux/Linux、WinCE等操作系统;
(3)学习程序编写,看专家写的程序本身就是一种美的享受;
(4)借鉴(抄)其中的设备驱动,u-boot支持的CPU和外围硬件很丰富;
......
总之,最好研究一下u-boot,不只是为了得到一个bootloader,更重要的是为学习GNU/Linux打基础。
--------------
| 初学者难点 |
--------------
经常在网上看到类似的提问:cygwin怎么工作不正常?gcc命令为什么找不到?编译怎么一大堆错误?我是不是要装Linux环境?那得花一天时间啊!如何装虚拟机?如何在虚拟机和windows间互传文件?......
初学者遇到的最大难点是环境搭建。听起来好笑,但很多网友就是由于未能搭建好开发环境或者开发环境出问题,导致迟迟不能迈出第一步,耽误了宝贵的时间。其实最难的是第一步,走过这一步,后面就是一马平川了。
--------------------------
| 《ecos增值包》解决方案 |
--------------------------
开发平台采用“cygwin + gcc for arm + redboot”。
redboot和u-boot一样都是bootloader,支持串口/网口下载、烧录flash、查看内存等功能,利用ecos中已有的redboot来调试u-boot可以避免反复烧写flash,尽管flash可烧写十万次,但在RAM里调试程序仍是好习惯。
GNU开发工具链支持很多种体系架构,没有版权限制,大部分开源软件都是用GNU编译器开发。光盘中提供编译好的GNUTOOLS for ARM,当然,如果你感兴趣,可以自己从源码制作工具(binutils、gcc、gdb等),这样可以自行升级工具版本,《ecos增值包》提供step by step的指导。
cygwin是虚拟机,可以在windows上虚拟类UNIX的shell界面,相当于windows应用程序。使用cygwin的好处是简单方便,只要5-15分钟就可以搭建起环境。大部分人使用的是windows系统,尤其是初学者,纯Linux系统环境并不是最佳选择。如果装VmWare虚拟机,情况会好些,但在windows和虚拟机间交换文件(FTP、Samba等)也不容易。使用cygwin是最简单的方式,用户可以直接在windows下使用UltraEdit、VC6、文件搜索、文件比较等工具,考虑到大多数人的实际情况,采用cygwin开发比较现实。也许有人热衷于纯Linux环境,也许有人对cygwin存有偏见,但绝大部分情况是,cygwin没有问题,而是使用cygwin的人出了差错。其实,cygwin非常可靠易用,从原理上讲,它和纯Linux环境的执行效果是完全一样的。cygwin经常遭人诟病的是其执行效率低,这是事实,但对于编译100多K大小u-boot程序的初学者来说,此速度损失可忽略不计。而且make只会编译受改变影响的相关文件,加上使用已编译的库,即使编译大程序,执行速度也可以接受。
顺便说下,《ecos增值包》可不仅仅只是做这个用途的,这只是它的一个很小的应用。它还提供web server、ftp server、lwip、GUI、TCP/IP、FS、视频监控、税控机、行驶记录仪、自动绣花纺织机、远程监控、通信设备等应用,以后会陆续给出例子。
------------------
| u-boot移植规划 |
------------------
u-boot的功能很强大,决定先做以下驱动:
(1)串口驱动。用于串口下载程序和命令行调试界面;
(2)Flash驱动。保存参数,存储文件;
(3)网络驱动。实现TFTP和NFS。
暂时没有实现的是:(主要是因为EasyARM2200上没有这些设备)
USB host、NAND Flash、PCMCIA(CF)、RTC、reset等。
------------------
| u-boot结构分析 |
------------------
从ecos的观点(学习ecos就是好啊,看问题都抽象了^_^),u-boot包含了三个抽象层:
(1)体系结构抽象层
EasyARM2200和SmartARM2200使用LPC2210芯片,属于ARM7体系架构,u-boot已经提供了(或抽象了)通用的ARM驱动程序,即lib_arm目录。
(2)变种抽象层
很多公司都生产ARM(7)芯片,LPC2210是NXP公司设计的,基于ARM7的变种。其他如s3c44b0、pxa、ixp、arm946等都是不同芯片公司生产的ARM体系的变种。在cpu/目录下,就是各种变种的驱动目录,在此,我们增加cpu/lpc2xxx/目录。主要完成程序搬移、串口驱动、中断处理。
(3)平台抽象层
很多制造商都使用LPC2210芯片生产设备,EasyARM2200和SmartARM2200是zlg Co.生产的两款不同的硬件平台,虽然都使用LPC2210,但地址分配不同,外围设备不同(“价格差别也很大”)。在board/目录下就是各种不同硬件平台的驱动,在此,我们加入board/easyarm2200或board/smartarm2200目录。主要完成板子上电初始化、TEXT_BASE初始化、链接脚本lds、flash初始化。
除了这3个目录及文件,还有一些与移植有关:
include/configs/ZLGARM2200.h 这个头文件用于配置开发板平台,如各种参数、地址范围、功能选择等。实际使用时将ZLGARM2200.h换成相应的EasyARM2200.h或SmartARM2200.h
include/configs/asm-arm/arch-lpc2xxx/hardware.h 这个头文件定义开发板的各种寄存器地址宏,操作宏、初始化宏。
Makefile 这个文件里要增加开发板的配置选项,ZLGARM2200_config(实际使用时将ZLGARM2200_config换成相应的EasyARM2200_config或SmartARM2200_config)。还有要把交叉编译前缀“#CROSS_COMPILE = arm-linux-”换成“CROSS_COMPILE = arm-elf-”
还有一些需要改动的文件:
drivers/rtl8019.h 主要是适应8019的8位数据接口。
为了清晰起见,将要修改的目录文件总结如下:(小技巧:把这些目录或文件做成快捷方式放在桌面上)
board/easyarm2200/
cpu/lpc2xxx/
include/configs/asm-arm/arch-lpc2xxx/hardware.h
include/configs/EASYARM2200.h
drivers/rtl8019.h
Makefile
------------------------
| u-boot各部分移植详述 |
------------------------
u-boot不支持LPC2210,所以需要新增加变种和平台抽象层目录board/easyarm2200/和cpu/lpc2xxx/;以及配置文件hardware.h和EASYARM2200.h;同时,有些不适应的文件需要修改,如rtl8019.h和Makefile。
Makefile要修改的是:
---------------------------------------------------------------------------
#CROSS_COMPILE = arm-linux-
CROSS_COMPILE = arm-elf-
#########################################################################
## EASYARM2200 Systems
#########################################################################
EASYARM2200_config : unconfig
@$(MKCONFIG) $(@:_config=) arm lpc2xxx EASYARM2200 easyarm2200
---------------------------------------------------------------------------
drivers/rtl8019.h要修改的是:
---------------------------------------------------------------------------
#define RTL8019_REG_00 (RTL8019_BASE + 0x00 * 2)
......
---------------------------------------------------------------------------
include/configs/asm-arm/arch-lpc2xxx/hardware.h
---------------------------------------------------------------------------
// UART
#define LPC2XXX_UART0_BASE 0xE000C000
#define LPC2XXX_UART1_BASE 0xE0010000
#define LPC2XXX_UART_RBR 0x00
#define LPC2XXX_UART_THR 0x00
#define LPC2XXX_UART_IER 0x04
#define LPC2XXX_UART_IIR 0x08
#define LPC2XXX_UART_FCR 0x08
......
---------------------------------------------------------------------------
include/configs/EASYARM2200.h
---------------------------------------------------------------------------
#define DEBUG 10
#define CONFIG_COMMANDS ( CONFIG_CMD_DFL | \
CFG_CMD_DATE | \
CFG_CMD_ELF | \
CFG_CMD_NET | \
CFG_CMD_ENV | CFG_CMD_FLASH )
/*
* Physical Memory Map
*/
#define CONFIG_NR_DRAM_BANKS 1 /* we have 1 banks of DRAM */
#define PHYS_SDRAM_1 0x81000000 /* SDRAM Bank #1 */
#define PHYS_SDRAM_1_SIZE 0x00800000 /* 8 MB */
#define PHYS_FLASH_1 0x80000000 /* Flash Bank #1 */
#define PHYS_FLASH_SIZE 0x00200000 /* 2 MB */
#define CFG_FLASH_BASE PHYS_FLASH_1
......
---------------------------------------------------------------------------
cpu/lpc2xxx/
---------------------------------------------------------------------------
========
serial.c
========
static int serial_flush_input(void)
static int serial_flush_output(void)
void serial_putc (const char c)
int serial_tstc (void)
void serial_setbrg (void)
{
unsigned char * base = (unsigned char *)LPC2XXX_UART0_BASE;
HAL_WRITE_UINT8(base + LPC2XXX_UART_LCR,LPC2XXX_UART_LCR_DLAB);
HAL_WRITE_UINT8(base+LPC2XXX_UART_DLM, SIO_BRDDIV / 256);
HAL_WRITE_UINT8(base+LPC2XXX_UART_DLL, SIO_BRDDIV % 256);
// 8-1-no parity.
HAL_WRITE_UINT8(base + LPC2XXX_UART_LCR,
LPC2XXX_UART_LCR_8_DBITS | LPC2XXX_UART_LCR_1_SBITS | LPC2XXX_UART_LCR_NO_PARITY);
}
int serial_getc (void)
{
int rv;
unsigned int c;
unsigned char ch;
for(;;) {
rv = serial_tstc();
if(rv > 0){
HAL_READ_UINT8(LPC2XXX_UART0_BASE+LPC2XXX_UART_RBR, c);
ch = (unsigned char)(c & 0xFF);
return ch;
}
}
}
=======
start.S
=======
如果从ROM里启动,那么开始部分的reset代码地址为80000030。用arm-elf-objdump -d u-boot > 1.txt得知reset偏址。
.globl _start
_start:
ldr pc, =0x80000030
加载位置TEXT_BASE在easyarm2200中是0x81060000,在smartarm2200中是0x817c0000。
/*************************************************/
/* interrupt vectors */
/*************************************************/
real_vectors:
ldr pc,.reset //0x00
ldr pc,.undefined_instruction //0x04
ldr pc,.software_interrupt //0x08
ldr pc,.prefetch_abort //0x0C
ldr pc,.data_abort //0x10
ldr pc,.not_used //0x14
ldr pc,.irq //0x18
ldr pc,.fiq //0x1C
/*************************************************/
.reset: .word reset
.undefined_instruction: .word undefined_instruction
.software_interrupt: .word software_interrupt
.prefetch_abort: .word prefetch_abort
.data_abort: .word data_abort
.not_used: .word not_used
.irq: .word irq
.fiq: .word fiq
......
============
interrupts.c
============
void udelay (unsigned long usec)
int interrupt_init (void)
---------------------------------------------------------------------------
board/easyarm2200/
---------------------------------------------------------------------------
=====================
EASYARM2200/config.mk
=====================
TEXT_BASE = 0x817C0000
PLATFORM_CPPFLAGS += -Uarm
===========================
EASYARM2200/lowlevel_init.S
===========================
#include <asm/hardware.h>
.globl lowlevel_init
lowlevel_init:
PLATFORM_SETUP1
mov pc, lr
==============
common/flash.c
==============
static ulong flash_get_size (vu_long *addr, flash_info_t *info)
......
case (CFG_FLASH_WORD_SIZE)SST_ID_xF1601:
case (CFG_FLASH_WORD_SIZE)SST_ID_xF160A:
info->flash_id += FLASH_SST160A;
info->sector_count = 32;
info->size = 0x00200000;
break; /* => 2 MB */
......
---------------------------------------------------------------------------
------------------
| u-boot移植难点 |
------------------
board/easyarm2200/EASYARM2200/config.mk中的TEXT_BASE决定u-boot在RAM中的加载位置,board/easyarm2200/EASYARM2200/u-boot.lds中虽然使用“. = 0x00000000;”,但config.mk中的LDFLAGS标志“LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)”却将text段指定在了$(TEXT_BASE),所以,最终text段的位置由TEXT_BASE决定。
cpu/lpc2xxx/start.S中判断程序是否位于指定加载位置,如果不是,就把代码搬移到TEXT_BASE指定位置,注意:“adr r0, _start”是把当前代码位置赋给r0,可不是“mov r0, _start”。
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq copy_vector
内存搬移
copy_vector:
cpu/lpc2xxx/start.S中的中断向量表要按照给出的方式定义,否则,中断可能无法长跳转到中断服务子程序,导致uClinux运行不正常(在开中断cli后,跳不到中断服务子程序)。
u-boot的平台配置文件include/configs/EASYARM2200.h需要仔细设置,注意不要引起地址冲突,u-boot自身需要占用一部分flash和内存空间,注意确保这些空间不被使用。
u-boot上电后会自动枚举flash,通过flash ID判断加载适当驱动,u-boot已经支持开发板的flash驱动了,出问题的话主要是没有找到对应ID,加上即可。
8019驱动u-boot也支持,主要是寄存器访问要改。
LPC2210没有cache和SDRAM,简化了移植工作。
------------------
| u-boot编译方法 |
------------------
cd /u-boot-1.2.0 进入u-boot目录。小技巧:输入cd/u-,然后按TAB键,shell会自动补全路径,很方便。
make clean 清除垃圾,只第一次需要,以后可不用此步骤。有时编译不正确,清一下垃圾就好了。
make EASYARM2200_config 配置EASYARM2200开发板编译环境,生成config.h和指定体系结构、平台、变种、配置文件名等。只第一次需要。
make 编译
------------------
| u-boot使用方法 |
------------------
u-boot功能强大,命令很多,详细使用方法可以在网上查找到,这里只给出一些范例。
help 帮助命令,可用“?”代替,可以用“help 指令”形式获得更详细说明。
printenv 打印环境变量
setenv ethaddr 12:34:56:78:9A:BC
setenv ipaddr 192.168.0.6
setenv serverip 192.168.0.1 (tftp服务器的地址)
tftp 81200000 a.bin 把server(IP=环境变量中设置的serverip)中/tftproot/下的a.bin通过TFTP读入到内存0x81200000处。
saveenv 保存环境变量到flash等不挥发的存储设备上。
nfs 32000000 192.168.0.1:aa.txt 把192.168.0.1(LINUX的NFS文件系统)上的NFS文件系统中的aa.txt 读入内存0x32000000处。
----------------------------------
| 《ecos增值软件包》辅助开发方法 |
----------------------------------
开发调试u-boot非常困难,因为串口还没有正常工作,如果出现问题,很难调试,而大多数初学者又没有仿真器,所以,这一状况更显严峻。当然不反对使用仿真器,不过使用一根串口线配合redboot也可以做到,调试效率不是很低,没有办法的办法。
每次编译完u-boot,就用redboot下载到内存调试运行:
lo -b 0x817c0000 -r -m xmodem
go 0x817c0000
程序可能出错,使用内存打印技术发现问题:
汇编中内存打印
ldr r1, =0x81040000
add r1, r1, #2
mov r0, #'a'
strb r0,[r1]
add r1,r1,#1
mov r0, #'2'
strb r0,[r1]
C程序中内存打印
unsigned char *yybuf = 0x81030000;
*(unsigned char *)yybuf++ = 'y';
*(unsigned char *)yybuf++ = '1';
把这些语句加到合适的地方,出错后,使用:
du -b 0x81040000 -l 300
就可以看到打印语句的输出(十六进制和ASCII码显示),通过判断打印语句的情况,就可以了解程序运行情况。
当然,在使用显存前要先清零:
mfill -b 0x81040000 -l 300 -p 0
在RAM里调试的好处是不必反复烧写flash,一旦调试正确,就可以固化。
由此可见,有了《ecos增值软件包》辅助开发,u-boot移植再也不是什么难事了,调试手段也够用,cygwin环境很方便,gcc编译器适用于各种体系结构,编译调试u-boot可以为使用Linux打基础,还能借鉴许多现成代码。
使用EasyARM2200和SmartARM2200的初学者现在就可以开始动手玩u-boot了,一般5-15分钟就可以搭建好开发环境,三天之内怎么也能跑起来了。 |