U-Boot在44B0X開發板上的移植以及代碼分析

 

章永陽2003-10-30

1. u-boot 介紹

u-boot 是一個open source bootloader,目前版本是0.4.0u-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.Sinterrupts.c 以及cpu.c,serial.c幾個文件。同樣不需要從零開始建立文件,直接從arm720t 複製,然後修改相應內容。

3)      include/configs 目錄下添加ev44b0ii.h,在這裏放上全局的宏定義等。

4)      找到u-boot 根目錄下Makefile 修改加入

1.         ev44b0ii_config : unconfig

2.         @./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 通過串口提供人機界面。我們可以使用一些命令做一些常用的工作,比如內存查看命令mdKermit 應用主要用來支持使用串口通過超級終端下載應用程序。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,關閉中斷,設置LockTimePLL(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 個內存bankbank 大小,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("flash/n");

printf("Manufacture ID=%4x(0x0004), Device ID(0x22c4)=%4x/n",manId,devId);

if(manId!=0x0004 && devId!=0x22c4){

printf("flash check falilure/n");

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 ("- missing/n");

} else {

printf ("- no sectors to erase/n");

}

return 1;

}

if ((info->flash_id == FLASH_UNKNOWN) ||

(info->flash_id > FLASH_AMD_COMP)) {

printf ("Can't erase unknown flash type - aborted/n");

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 Error/n");

}

}

/* 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 (" done/n");

return 0;

}

int BlankCheck(int targetAddr,int targetSize)

{

int i,j;

for(i=0;i<targetSize;i+=2)

{

j=*((u16 *)(i+targetAddr));

if( j!=0xffff)

{

printf("E:%x=%x/n",(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

所以這裏分兩次寫入。 
發佈了26 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章