天天看點

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

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*/

ldr pc,=HandleZDMA0 /*mGB*/

ldr pc,=HandleZDMA1

ldr pc,=HandleBDMA0

ldr pc,=HandleBDMA1

ldr pc,=HandleWDT

ldr pc,=HandleUERR01 /*mGB*/

ldr pc,=HandleTIMER0 /*mGC*/

ldr pc,=HandleTIMER1

ldr pc,=HandleTIMER2

ldr pc,=HandleTIMER3

ldr pc,=HandleTIMER4

ldr pc,=HandleTIMER5 /*mGC*/

ldr pc,=HandleURXD0 /*mGD*/

ldr pc,=HandleURXD1

ldr pc,=HandleIIC

ldr pc,=HandleSIO

ldr pc,=HandleUTXD0

ldr pc,=HandleUTXD1 /*mGD*/

ldr pc,=HandleRTC /*mGKA*/

b . /*mGKA*/

ldr pc,=HandleADC /*mGKB*/

b . /*mGKB*/

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

/*****************************************************

* Set clock control registers *

*****************************************************/

ldr r0,LOCKTIME

ldr r1,=800 // count = t_lock * Fin (t_lock=200us, Fin=4MHz) = 800

ldr r0,PLLCON /*temporary setting of PLL*/

ldr r1,PLLCON_DAT /*Fin=10MHz,Fout=40MHz or 60MHz*/

ldr r0,CLKCON

ldr r1,=0x7ff8 //All unit block CLK enable

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);

devId=_RD(0x1);

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{

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");

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);

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");

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);

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,0xa0);

*tempPt=high;

_WAIT();

*(tempPt+1)=low;

wirte_word 則想flash 裡面寫入unsigned long 類型的data,因為flash 一次隻能寫入16bits,是以這裡分兩次寫入。

繼續閱讀