U-Boot 在44B0X 开发板上的移植以及代码分析
发表时间:2005-9-28 发布人:administrator 阅读次数:732
--------------------------------------------------------------------------------
armboot的原理
1、BIOS的源码,其作用是初始化硬件,并COPY Boot到SDRAM中
2、在SDRAM中运行BOOT达到在线升级的目的。
3、此源代码与硬件无关,在44b0x上测试通过
4、串口通讯协议自己制定,很好理解。在线升级很快
5、目前只支持SST39LVF160,可以自己修改成相信的NOR FLASH
U-Boot 在44B0X 开发板上的移植以及代码分析
1. u-boot 介绍
u-boot 是一个open source 的bootloader,目前版本是0.4.0。u-boot 是在ppcboot 以及armboot 的基础上发展而来,虽然宣称是0.4.0 版本,却相当的成熟和稳定,已经在许多嵌入式系统开发过程中被采用。由于其开发源代码,其支持的开发板众多。唯一遗憾的是并不支持我们现在学习所用samsung 44B0X 的开发板。
为什么我们需要u-boot?显然可以将ucLinux 直接烧入flash,从而不需要额外的
引导装载程序(bootloader)。但是从软件升级的角度以及程序修补的来说,软件的自
动更新非常重要。事实上,引导装载程序(bootloader)的用途不仅如此,但仅从软件的自动更新的需要就说明我们的开发是必要的。同时,u-boot 移植的过程也是一个对嵌入式系统包括软硬件以及操作系统加深理解的一个过程。
2. u-boot 移植的框架
移植u-boot 到新的开发板上仅需要修改和硬件相关的部分。在代码结构上:
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.flash_id = FLASH_UNKNOWN;
flash_info.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.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,所以这里分两次写入。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |