经过几周的折腾,终于搞定了
u-boot-1.2.0,写这篇粗解希望能给后来的朋友一些启示和帮助。下面就从以下几个方面写粗一些
u-boot-1.2.0方面的知识:
·启动流程
·各命令简解
·移植步骤
·nor flash底层驱动的修改
·cs8900网络驱动与tftp
·Linux下tftp-server的安装与配置
开饭板资源说明:
处理器:S3C2410A;
内存:SDRAM 64M;
Flash:nor flash sst39vf1601;
网卡芯片:CS8900A,映射到nGCS3。
1. u-boot启动流程
关于u-boot的启动流程网上也有很多资料描述,这里再赘述一番。S3c2410采用ARM920T核,u-boot就从<yourpath>/cpu/arm920t下的Start.S启动。Start.S主要完成了以下一些工作:安装中断,设置时钟,cpu的初始化,将自身搬运到目的地(0x3ff80000处),设置堆栈,清除bss段,最后跳转到start_armboot。
在Start.S中,cpu的初始化由cpu_init_crit实现,它完成以下功能:flush v4 I/D caches, 关闭MMU stuff,关闭caches和调用lowlevel_init初始化BWSCON。lowlevel_init在<yourpath>/board/yourboard中实现。
Start.S中实现代码如下:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
从上面可以看到cpu_init_crit是条件编译的,这主要用于调试阶段。如果是通过其他bootloader加载u-boot并调试的话,那么,#define CONFIG_SKIP_LOWLEVEL_INIT一下是很有必要的。假设通过vivi将u-boot.bin加载到RAM中执行,如果没有定义CONFIG_SKIP_LOWLEVEL_INIT那么在终端上我们什么也看不到。原因是u-boot.bin本来就在RAM中了,而u-boot.bin运行到cpu_init_crit后,CPU被初始化了一番,u-boot.bin也就全部在RAM中消失了。所以在调试阶段记住#define CONFIG_SKIP_LOWLEVEL_INIT(我在<yourpath>/include/configs/xxxx.h中定义它)。
Start.S完成了自己底层方面初始化的历史使命后,将自己的接力棒就交给了start_armboot。我们可以在<yourpath>/lib_arm/board.c文件中找到它的踪迹。这个函数实现了什么东西呢?相信下面这个网址比我说得清楚。当然有些地方还是要读读源代码,说到读源代码,有些功能强大的编辑器还是值得提一下,source insight,sourcenav,当然还有uedit,这些都是很好的编辑器。
对于执行流程呢,u-boot在start_armboot中就会死执行main_loop ()函数,这个函数就会捕捉从控制台传来的数据,然后u-boot根据这些数据采取相应的动作,如执行u-boot命令等。
2. u-boot命令简解
u-boot提供了丰富的命令,这些命令总共有60个左右。但这些命令有些可选择的,也就是用户可以选择自己相关的命令,这些命令在网上可以找到它们的详细解释和用法, http://www.freescale.com/files/32bit/doc/quick_ref_guide/LITE5200BUBPG/LITE5200BUBPG.pdf。当然u-boot也提供了良好的命令扩展,用户可以添加自己需要的命令。如果用户想自己添加一些自己的私房命令,不妨看看下面文字。
在u-boot
中,每个命令都用一个命令结构体来描述,这个结构体定义在<yourpath>/include/command.h
中。
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
Int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage; /* Usage message (short) */
#ifdef CFG_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
其中Cmd
就是实际需要调用的命令函数!name
为该命令名字符串(比如go
,bootm
等)。
在command.h
里面还一个叫U_BOOT_CMD
的宏。
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) /
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
这段代码的意思是说,对于U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
来说,就是定义一个cmd_tbl_t
(cmd_tbl_t
就是struct cmd_tbl_s
)类型的__u_boot_cmd_name
变量,并将此变量放到.u_boot_cmd
段,.u_boot_cmd
的定义可以在board/yourboard/u-boot.lds
找到。在U-boot
中的shell
中,根据用户输入的命令,就会在.u_boot_cmd
这个内存区域中查找,当.u_boot_cmd
中某一个cmd_tbl_s
命令结构体的cmd_tbl_s.name
和输入的命令字符串相符时,就调用该命令。
下面以添加mycmd
命令为例说明方法。
1).
在include/configs/xxxx.h
中的CONFIG_COMMDNDS
中增加一项CFG_ CMD_MYCMD
:
CFG_CMD_USBLOAD,
/***********************************************************
* Command definition
***********************************************************/
#define CONFIG_COMMANDS /
( CONFIG_CMD_DFL | /
CFG_CMD_CACHE | /
CFG_CMD_NET | /
CFG_CMD_ENV | /
CFG_CMD_FLASH | /
CFG_CMD_PING | /
CFG_CMD_NAND | /
/*CFG_CMD_EEPROM |*/ /
/*CFG_CMD_I2C |*/ /
/*CFG_CMD_USB |*/ /
CFG_CMD_REGINFO | /
CFG_CMD_DATE | /
CFG_CMD_ELF |/
CFG_ CMD_MYCMD
)
2).
在include/cmd_confdefs.h
中加入命令标志位
#define CFG_CMD_MYCMD 0x000055AAULL (
可以是任意值,只要不和其他命令冲突)
3).
在common/
下面加入mycmd.c
大体结构如下:
#If
(CONFIG_COMMANDS & CFG_CMD_ELF
)
int my-cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[ ] ){
………………….
}
U_BOOT_CMD{
mycmd,
2,
1,
1,
my-cmd,
“……”, /* write some words to describe usage of this command */
“……” /* describe help messages of my command*/
};
#endif
4).
在<yourpath>/common/Makefile
文件中的COBJS
后加上mycmd.o
5).
重新编译u-boot
,就OK
。
3. u-boot的移植步骤
总的来说u-boot的移植步骤比较简单主要以下几个步骤:
1). 在<yourpath>/board/下建立一个自己的文件夹,我在此目录下建立了foolish这个文件夹,foolish的内容是从smdk2410中copy过来的,因为我的板子和smdk2410差不多。在foolish中主要有两个文件很重要,它们是flash.c和foolish.c(foolish.c是和我选择的名字有关,在smdk2410中叫smdk2410.c),flash.c是关于开发板中flash的最底层的驱动程序,它主要实现了对flash的基本操作,如读、写、擦除等操作,这也是需要修改的文件。foolish.c实际上和smdk2410.c一样,因为我的板子和smdk2410差不多。
2). 将<yourpath>/board/foolish中的Makefile文件的COBJS和SOBJS修改成下面的值。
COBJS := foolish.o flash.o
SOBJS := lowlevel_init.o
3). 修改存放在<yourpath>/include/foolish.h文件,这个文件需要自己添加。当然它也是由同目录下的smdk2410.h修改而来。我的foolish配置如下:
#ifndef __CONFIG_H
#define __CONFIG_H
//#define CONFIG_SKIP_LOWLEVEL_INIT
/*
* High Level Configuration Options
* (easy to change)
*/
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
#define CONFIG_S3C2410 1 /* in a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK2410 1 /* on a SAMSUNG SMDK2410 Board */
/* input clock of PLL */
#define CONFIG_SYS_CLK_FREQ 12000000/* the SMDK2410 has 12MHz input clock */
#define USE_920T_MMU 1
#undef CONFIG_USE_IRQ /* we don't need IRQ/FIQ stuff */
/*
Size of malloc() pool
*/
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024)
#define CFG_GBL_DATA_SIZE 128 /* size in bytes reserved for initial data */
/*
Hardware drivers
*/
#define CONFIG_DRIVER_CS8900 1 /* we have a CS8900 on-board */
#define CS8900_BASE 0x19000300
#define CS8900_BUS16 1 /* the Linux driver does accesses as shorts */
/*
select serial console configuration
*/
#define CONFIG_SERIAL1 1 /* we use SERIAL 1 on SMDK2410 */
/************************************************************
* RTC
************************************************************/
#define CONFIG_RTC_S3C24X0 1
/* allow to overwrite serial and ethaddr */
#define CONFIG_ENV_OVERWRITE
#define CONFIG_BAUDRATE 115200
/***********************************************************
* Command definition
***********************************************************/
#define CONFIG_COMMANDS /
(CONFIG_CMD_DFL | /
CFG_CMD_CACHE | /
/*CFG_CMD_NAND |*/ /
/*CFG_CMD_EEPROM |*/ /
/*CFG_CMD_I2C |*/ /
/*CFG_CMD_USB |*/ /
CFG_CMD_REGINFO | /
CFG_CMD_DATE | /
CFG_CMD_ELF | /
CFG_CMD_PING)
/* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */
#include <cmd_confdefs.h>
#define CONFIG_BOOTDELAY 10
#define CONFIG_BOOTARGS "root=ramfs devfs=mount console=ttySAC0,115200"
#define CONFIG_ETHADDR 08:00:3e:26:0a:5b
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 202.192.248.147
#define CONFIG_SERVERIP 202.192.248.46
#define CONFIG_BOOTFILE "zImage"
#define CONFIG_BOOTCOMMAND "tftp; bootm"
#if (CONFIG_COMMANDS & CFG_CMD_KGDB)
#define CONFIG_KGDB_BAUDRATE 115200 /* speed to run kgdb serial port */
/* what's this ? it's not used anywhere */
#define CONFIG_KGDB_SER_INDEX 1 /* which serial port to use */
#endif
/*
* Miscellaneous configurable options
*/
#define CFG_LONGHELP /* undef to save memory */
#define CFG_PROMPT "foolish # " /* Monitor Command Prompt */
#define CFG_CBSIZE 256 /* Console I/O Buffer Size */
#define CFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */
#define CFG_MAXARGS 16 /* max number of command args */
#define CFG_BARGSIZE CFG_CBSIZE /* Boot Argument Buffer Size */
#define CFG_MEMTEST_START 0x30000000 /* memtest works on */
#define CFG_MEMTEST_END 0x33F00000 /* 63 MB in DRAM */
#undef CFG_CLKS_IN_HZ /* everything, incl board info, in Hz */
#define CFG_LOAD_ADDR 0x30200000 /* default load address */
/* the PWM TImer 4 uses a counter of 15625 for 10 ms, so we need */
/* it to wrap 100 times (total 1562500) to get 1 sec. */
#define CFG_HZ 1562500
/* valid baudrates */
#define CFG_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 }
/*-----------------------------------------------------------------------
* Stack sizes
*
* The stack sizes are set up in start.S using the settings below
*/
#define CONFIG_STACKSIZE (128*1024) /* regular stack */
#ifdef CONFIG_USE_IRQ
#define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */
#define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */
#endif
/*-----------------------------------------------------------------------
* Physical Memory Map
*/
#define CONFIG_NR_DRAM_BANKS 1 /* we have 1 bank of DRAM */
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE PHYS_FLASH_1
/*-----------------------------------------------------------------------
* FLASH and environment organization
*/
#define CONFIG_AMD_LV400 1 /* uncomment this if you have a LV400 flash */
#if 0
#define CONFIG_AMD_LV800 1 /* uncomment this if you have a LV800 flash */
#endif
#define CFG_MAX_FLASH_BANKS 1 /* max number of memory banks */
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE 0x00100000 /* 1MB */
#define CFG_MAX_FLASH_SECT (19) /* max number of sectors on one chip */
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE 0x00200000 /* 2MB */
#define CFG_MAX_FLASH_SECT (32) /* max number of sectors on one chip */
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x20000) /* addr of environment */
#endif
/* timeout values are in ticks */
#define CFG_FLASH_ERASE_TOUT (5*CFG_HZ) /* Timeout for Flash Erase */
#define CFG_FLASH_WRITE_TOUT (5*CFG_HZ) /* Timeout for Flash Write */
#define CFG_ENV_IS_IN_FLASH 1
#define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
#endif /* __CONFIG_H */
对于该文件中的选项就不一一描述了,这些选项在<yourpath>/readme文件中有详细的说明。
4). 在<yourpath>/Makefile文件中添加下面的两行,
foolish_config: unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t foolish NULL s3c24x0
OK!现在可以编译u-boot了!
make foolish_config
make ARCH=arm CROSS_COMIPE=arm-linux-
最后是否就生成了u-boot.bin等文件了呢?到这里算是成功1/4了。
4. nor flash底层驱动的修改
下载u-boot.bin到板子里(当然定义了CONFIG_SKIP_LOWLEVEL_INIT,要不然没反应就不好了),u-boot是不是给我们打印一个CRC错误的消息,这主要由于没有将env变量存到flash中,所以u-boot给我们警告。试试saveenv命令将env变量存到flash,居然不行。原来smdk2410的nor flash是AMD的LV400,而我的却是sst39vf1601。仔细看看才发现它们的写时序不同,这可以参考两中芯片的datasheet。最后我改写<yourpath>/board/foolish/flash.c文件中flash_erase和write_hword函数。
在flash_erase函数中改写了从“if (info->protect[sect] == 0) { /* not protected */”到“if (ctrlc ())”之间的代码,这些代码如下:
……
if (info->protect[sect] == 0) { /* not protected */
vu_short *addr = (vu_short *) (info->start[sect]);
MEM_FLASH_ADDR1 = CMD_UNLOCK1;
MEM_FLASH_ADDR2 = CMD_UNLOCK2;
MEM_FLASH_ADDR1 = CMD_ERASE_SETUP;
MEM_FLASH_ADDR1 = CMD_UNLOCK1;
MEM_FLASH_ADDR2 = CMD_UNLOCK2;
*addr = CMD_ERASE_CONFIRM;
/* wait until flash is ready */
while(1){
unsigned short i;
i = *((volatile unsigned short *)addr)&0x40;
if(i!=*((volatile unsigned short *)addr)&0x40)
continue;
if(*((volatile unsigned short *)addr)&0x80)
break;
}
printf ("ok./n");
} else { /* it was protected */
printf ("protected!/n");
}
}
if (ctrlc ())
……
对于write_hword函数如下面的代码所示:
volatile static int write_hword (flash_info_t * info, ulong dest, ushort data)
{
vu_short *addr = (vu_short *) dest;
ushort result;
int rc = ERR_OK;
int cflag, iflag;
int chip;
/*
* Check if Flash is (sufficiently) erased
*/
result = *addr;
if ((result & data) != data)
return ERR_NOT_ERASED;
/*
* Disable interrupts which might cause a timeout here. Remember that our exception
* vectors are at address 0 in the flash, and we don't want a (ticker) exception to happen
* while the flash chip is in programming mode.
*/
cflag = icache_status ();
icache_disable ();
iflag = disable_interrupts ();
MEM_FLASH_ADDR1 = CMD_UNLOCK1;
MEM_FLASH_ADDR2 = CMD_UNLOCK2;
MEM_FLASH_ADDR1 = CMD_PROGRAM;
*addr = data;
/* arm simple, non interrupt dependent timer */
reset_timer_masked ();
/* wait until flash is ready */
while(1){
unsigned short i = *(volatile unsigned short *)addr & 0x40;
if(i != *(volatile unsigned short *)addr & 0x40) //D6 == D6
continue;
if((*(volatile unsigned short *)addr & 0x80)==(data & 0x80)){
rc = ERR_OK;
break; //D7 == D7
}
}
if (iflag)
enable_interrupts ();
if (cflag)
icache_enable ();
return rc;
}
到现在位置整个关于nor flash sst39vf1601就差不多搞定了,但还有一个比较烦人的地方是,在打开u-boot时的时候,居然打印的是AMD的信息,这可不行得让u-boot打印sst39vf1601的信息,为了达到这个小小的要求,就需要改写flash_print_info(也在flash.c中)函数了,将该函数中
case (AMD_MANUFACT & FLASH_VENDMASK):
printf ("AMD: ");
和
case (AMD_ID_LV400B & FLASH_TYPEMASK):
printf ("1x Amd29LV400BB (4Mbit)/n");
分别修改为:
case (AMD_MANUFACT & FLASH_VENDMASK):
printf ("SST: ");
和
case (AMD_ID_LV400B & FLASH_TYPEMASK):
printf ("1x 39VF1601 (2Mbit)/n");
这样整个flash部分就算完毕了,但有一点小小的毛病,就是在saveenv命令时,速度有点慢,还希望高手改改。接下来应该是网络部分了。
5. cs8900网络驱动与tftp
之所以利用u-boot其中最重要的原因之一是它的tftp功能。对于网络部分,板子上的网络芯片是cs8900,幸运的是这和smdk2410一样。使得网络部分移植太简单了。根本就不需要什么大的修改就ok了,不过这还是花了n多时间才搞定。主要问题有两个:一是错误是CS8900 Ethernet chip not found?!错误;二是为什么地址是0x19000000。
后面这个看来原理图后发现cs8900用nGCS3,也就是说基地址应该从0x18000000开始,但是发现cs8900用了地址线A24作为I/O方式和MEMORY方式,smdk2410使用了I/O方式,所以处理器在送出基地址时要考虑A24,并且I/O方式A24=1,从这里可知cs8900的基地址应该是0x18000000+1<<24=0x19000000。
对于CS8900 Ethernet chip not found?!错误这个问题,就困惑了很久,网上都说不用修改就可以使用,后来发现每次u-boot探测到的ID都不一样,这说明片选是正确的,可能是处理器Bank3时序问题,后来仔细查看发现Bank3的参数没有配置正确。通过修改<yourpath>/board/foolish/ lowlevel_init.S文件中的Bank3参数搞定了第一个错误,Bank3的配置如下:
#define B3_Tacs 0x0 /* 0clk */
#define B3_Tcos 0x3 /* 4clk */
#define B3_Tacc 0x7 /* 14clk */
#define B3_Tcoh 0x1 /* 1clk */
#define B3_Tah 0x3 /* 4clk */
#define B3_Tacp 0x3 /* 6clk */
#define B3_PMC 0x0 /* normal */
到此网络方面就Ok了!试试tftp,嘿嘿!OK!
5. Linux下tftp-server的安装与配置
将系统切换到linux(fedora core 6)下,发现fc6下面还没有装tftp-server,在网上下载一个tftp-hpa-0.42,下载地址为http://www.kernel.org/pub/software/network/tftp/。装上此server后,将在/etc/xinetd.d/下产生tftp文件,我们需要配置此文件完成tftp sever正确运行。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
}
server_args项的-s参数指定了tftp server的根目录地址,这可以根据自己的需要修改。好了,tftp server已经配置完成,迫不及待的试试u-boot和linux之间的tftp怎么样?打开u-boot输入tftp,理想中应该是一群可爱的“#”符号,但现实和理想总有那么一段距离,没办法!迎接我的却是一帮不怎么可爱的“T”形字母。为什么呢?????在windows下,不是成功了吗?难道linux真的不行吗?这个问题一个困惑就是几个昼夜啊,网上也没找到什么蛛丝马迹,到底该怎么办呢?离胜利就只有一步之遥了,光明就在前方啊!真是功夫不负好心人,朋友当你出现上面的问题时,请你在fc6的防火墙与安全中把udp的69号端口(tftp使用的端口)放行吧,要不然白白的郁闷就不好玩了。
好了,ladies and gentlemen!u-boot节目到此结束,祝各位晚安!