天天看點

u-boot 移植步驟詳解

1 U-Boot簡介

U-Boot,全稱Universal Boot Loader,是遵循GPL條款的開放源碼項目。從FADSROM、8xxROM、PPCBOOT逐漸發展演化而來。其源碼目錄、編譯形式與Linux核心很相似,事實上,不少U-Boot源碼就是相應的Linux核心源程式的簡化,尤其是一些裝置的驅動程式,這從U-Boot源碼的注釋中能展現這一點。但是U-Boot不僅僅支援嵌入式Linux系統的引導,目前,它還支援NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式作業系統。其目前要支援的目标作業系統是OpenBSD,

NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。這是U-Boot中Universal的一層含義,另外一層含義則是U-Boot除了支援PowerPC系列的處理器外,還能支援MIPS、x86、ARM、NIOS、XScale等諸多常用系列的處理器。這兩個特點正是U-Boot項目的開發目标,即支援盡可能多的嵌入式處理器和嵌入式作業系統。就目前來看,U-Boot對PowerPC系列處理器支援最為豐富,對Linux的支援最完善。其它系列的處理器和作業系統基本是在2002年11月PPCBOOT改名為U-Boot後逐漸擴充的。從PPCBOOT向U-Boot的順利過渡,很大程度上歸功于U-Boot的維護人德國DENX軟體工程中心Wolfgang

Denk[以下簡稱W.D]本人精湛專業水準和持着不懈的努力。目前,U-Boot項目正在他的領軍之下,衆多有志于開放源碼BOOT LOADER移植工作的嵌入式開發人員正如火如荼地将各個不同系列嵌入式處理器的移植工作不斷展開和深入,以支援更多的嵌入式作業系統的裝載與引導。

選擇U-Boot的理由:

① 開放源碼;

② 支援多種嵌入式作業系統核心,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;

③ 支援多個處理器系列,如PowerPC、ARM、x86、MIPS、XScale;

④ 較高的可靠性和穩定性;

⑤ 高度靈活的功能設定,适合U-Boot調試、作業系統不同引導要求、産品釋出等;

⑥ 豐富的裝置驅動源碼,如序列槽、以太網、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、鍵盤等;

⑦ 較為豐富的開發調試文檔與強大的網絡技術支援;

2 U-Boot主要目錄結構

- board 目标闆相關檔案,主要包含SDRAM、FLASH驅動;

- common 獨立于處理器體系結構的通用代碼,如記憶體大小探測與故障檢測;

- cpu 與處理器相關的檔案。如mpc8xx子目錄下含序列槽、網口、LCD驅動及中斷初始化等檔案;

- driver 通用裝置驅動,如CFI FLASH驅動(目前對INTEL FLASH支援較好)

- doc U-Boot的說明文檔;

- examples可在U-Boot下運作的示例程式;如hello_world.c,timer.c;

- include U-Boot頭檔案;尤其configs子目錄下與目标闆相關的配置頭檔案是移植過程中經常要修改的檔案;

- lib_xxx 處理器體系相關的檔案,如lib_ppc, lib_arm目錄分别包含與PowerPC、ARM體系結構相關的檔案;

- net 與網絡功能相關的檔案目錄,如bootp,nfs,tftp;

- post 上電自檢檔案目錄。尚有待于進一步完善;

- rtc RTC驅動程式;

- tools 用于建立U-Boot S-RECORD和BIN鏡像檔案的工具;

3 U-Boot支援的主要功能

U-Boot可支援的主要功能清單

系統引導 支援NFS挂載、RAMDISK(壓縮或非壓縮)形式的根檔案系統

支援NFS挂載、從FLASH中引導壓縮或非壓縮系統核心;

基本輔助功能 強大的作業系統接口功能;可靈活設定、傳遞多個關鍵參數給作業系統,适合系統在不同開發階段的調試要求與産品釋出,尤對Linux支援最為強勁;

支援目标闆環境參數多種存儲方式,如FLASH、NVRAM、EEPROM;

CRC32校驗,可校驗FLASH中核心、RAMDISK鏡像檔案是否完好;

裝置驅動 序列槽、SDRAM、FLASH、以太網、LCD、NVRAM、EEPROM、鍵盤、USB、PCMCIA、PCI、RTC等驅動支援;

上電自檢功能 SDRAM、FLASH大小自動檢測;SDRAM故障檢測;CPU型号;

特殊功能 XIP核心引導;

4 移植前的準備

(1)、首先讀讀uboot自帶的readme檔案,了解了一個大概。

(2)、看看common.h,這個檔案定義了一些基本的東西,并包含了一些必要的頭檔案。再看看flash.h,這個檔案裡面定義了flash_info_t為一個struct。包含了flash的一些屬性定義。并且定義了所有的flash的屬性,其中,AMD的有:AMD_ID_LV320B,定義為“#define AMD_ID_LV320B 0x22F922F9”。

(3)、對于“./borad/at91rm9200dk/flash.c”的修改,有以下的方面:

“void flash_identification(flash_info_t *info)”這個函數的目的是确認flash的型号。注意的是,這個函數裡面有一些宏定義,直接讀寫了flash。并獲得ID号。

(4)、修改:”./board/at91rm9200dk/config.mk”為

TEXT_BASE=0x21f80000 為TEXT_BASE=0x21f00000(當然,你應該根據自己的闆子來修改,和一級boot的定義的一緻即可)。

(5)、再修改”./include/configs/at91rm9200dk.h”為

修改flash和SDRAM的大小。

(6)、另外一個要修改的檔案是:

./borad/at91rm9200dk/flash.c。這個檔案修改的部分比較的多。

       a. 首先是OrgDef的定義,加上目前的flash。

       b. 接下來,修改”#define FLASH_BANK_SIZE 0x200000”為自己flash的  容量

       c. 在修改函數flash_identification(flash_info_t * info)裡面的列印資訊,這部分将在u-boot啟動的時候顯示。

       d. 然後修改函數flash_init(void)裡面對一些變量的指派。

       e. 最後修改的是函數flash_print_info(flash_info_t * info)裡面實際列印的函數資訊。

       f. 還有一個函數需要修改,就是:“flash_erase”,這個函數要檢測先前知道的flash類型是否比對,否則,直接就傳回了。把這裡給注釋掉。

(7)、接下來看看SDRAM的修改。

這個裡面對于“SIZE”的定義都是基于位元組計算的。

隻要修改”./include/configs/at91rm9200dk.h”裡面的

“#define PHYS_SDRAM_SIZE 0X200000”就可以了。注意,SIZE是以位元組為機關的。

(8)、還有一個地方要注意

就是按照目前的設定,一級boot把u_boot加載到了SDRAM的空間為:21F00000 ->21F16B10,這恰好是SDRAM的高端部分。另外,BSS為21F1AE34。

(9)、編譯後,可以寫入flash了。

     a. 壓縮這個u-boot.bin

“gzip –c u-boot.bin > u-boot.gz”

壓縮後的檔案大小為:

43Kbytes

      b. 接着把boot.bin和u-boot.gz燒到flash裡面去。

Boot.bin大約11kBytes,在flash的0x1000 0000 ~ 0x1000 3fff

5 U-Boot移植過程

④ 在建立的開發環境下進行移植工作。絕大多數的開發環境是交叉開發環境。在這方面,DENX和MontaVista均提供了完整的開發工具集;

⑤在目标闆與開發主機間接入硬體調試器。這是進行U-Boot移植應當具備且非常關鍵的調試工具。因為在整個U-Boot的移植工作中,尤其是初始階段,硬體調試器是我們了解目标闆真實運作狀态的唯一途徑。在這方面,W.D本人和衆多嵌入式開發人員傾向于使用BDI2000。一方面,其價格不如ICE調試器昂貴,同時其可靠性高,功能強大,完全能勝任移植和調試U-Boot。另外,網上也有不少關于BDI2000調試方面的參考文檔。

⑥ 如果在參考開發闆上移植U-Boot,可能需要移除目标闆上已有的BOOT LOADER。可以根據闆上BOOT LOADER的說明文檔,先着手解決在移除目前BOOT LOADER的情況下,如何進行恢複。以便今後在需要場合能重新裝入原先的BOOT LOADER。

6. U-Boot移植方法

目前,對于U-Boot的移植方法,大緻分為兩種。一種是先用BDI2000建立目标闆初始運作環境,将U-Boot鏡像檔案u-boot.bin下載下傳到目标闆RAM中的指定位置,然後,用BDI2000進行跟蹤調試。其好處是不用将U-Boot鏡像檔案燒寫到FLASH中去。但弊端在于對移植開發人員的移植調試技能要求較高,BDI2000的配置檔案較為複雜。另外一種方法是用BDI2000先将U-Boot鏡像檔案燒寫到FLASH中去,然後利用GDB和BDI2000進行調試。這種方法所用BDI2000的配置檔案較為簡單,調試過程與U-Boot移植後運作過程相吻合,即U-Boot先從FLASH中運作,再重載至RAM中相應位置,并從那裡正式投入運作。唯一感到有些麻煩的就是需要不斷燒寫FLASH。但考慮到FLASH正常擦寫次數基本為10萬次左右,作為移植U-Boot,不會占用太多的次數,應該不會為FLASH燒寫有什麼擔憂。同時,W.

D本人也極力推薦使用後一種方法。筆者建議,除非U-Boot移植資深人士或有強有力的技術支援,建議采用第二種移植方法。

7. U-Boot移植主要修改的檔案

從移植U-Boot最小要求-U-Boot能正常啟動的角度出發,主要考慮修改如下檔案:

①<目标闆>.h頭檔案,如include/configs/RPXlite.h。可以是U-Boot源碼中已有的目标闆頭檔案,也可以是新命名的配置頭檔案;大多數的寄存器參數都是在這一檔案中設定完成的;

②<目标闆>.c檔案,如board/RPXlite/RPXlite.c。它是SDRAM的驅動程式,主要完成SDRAM的UPM表設定,上電初始化。

③ FLASH的驅動程式,如board/RPXlite/flash.c,或common/cfi_flash.c。可在參考已有FLASH驅動的基礎上,結合目标闆FLASH資料手冊,進行适當修改;

④ 序列槽驅動,如修改cpu/mpc8xx/serial.c序列槽收發器晶片使能部分。

8. U-Boot移植要點

① BDI2000的配置檔案。如果采用第二種移植方法,即先燒入FLASH的方法,配置項隻需很少幾個,就可以進行U-Boot的燒寫與調試了。對PPC 8xx系列的主機闆,可參考DULG文檔中TQM8xx的配置檔案進行相應的修改。下面,筆者以美國Embedded Planet公司的RPXlite DW闆為例,給出在嵌入式Linux交叉開發環境下的BDI2000參考配置檔案以作參考:

; bdiGDB configuration file for RPXlite DW or LITE_DW

; --------------------------------------------

[INIT]

; init core register

WSPR 149 0x2002000F ;DER : set debug enable register

; WSPR 149 0x2006000F ;DER : enable SYSIE for BDI flash program

WSPR 638 0xFA200000 ;IMMR : internal memory at 0xFA200000

WM32 0xFA200004 0xFFFFFF89 ;SYPCR

[TARGET]

CPUCLOCK 40000000 ;the CPU clock rate after processing the init list

BDIMODE AGENT ;the BDI working mode (LOADONLY | AGENT)

BREAKMODE HARD ;SOFT or HARD, HARD uses PPC hardware breakpoints

[HOST]

IP 173.60.120.5

FILE uImage.litedw

FORMAT BIN

LOAD MANUAL ;load code MANUAL or AUTO after reset

DEBUGPORT 2001

START 0x0100

[FLASH]

CHIPTYPE AM29BX8 ;;Flash type (AM29F | AM29BX8 | AM29BX16 | I28BX8 | I28BX16)

CHIPSIZE 0x400000 ;;The size of one flash chip in bytes

BUSWIDTH 32 ;The width of the flash memory bus in bits (8 | 16 | 32)

WORKSPACE 0xFA202000 ; RAM buffer for fast flash programming

FILE u-boot.bin ;The file to program

FORMAT BIN 0x00000000

ERASE 0x00000000 BLOCK

ERASE 0x00008000 BLOCK

ERASE 0x00010000 BLOCK

ERASE 0x00018000 BLOCK

[REGS]

DMM1 0xFA200000

FILE reg823.def

② U-Boot移植參考闆。這是進行U-Boot移植首先要明确的。可以根據目标闆上CPU、FLASH、SDRAM的情況,以盡可能相一緻為原則,先找出一個與所移植目标闆為同一個或同一系列處理器的U-Boot支援闆為移植參考闆。如RPXlite DW闆可選擇U-Boot源碼中RPXlite闆作為U-Boot移植參考闆。對U-Boot移植新手,建議依照循序漸進的原則,目标闆檔案名暫時先用移植參考闆的名稱,在逐漸熟悉U-Boot移植基礎上,再考慮給目标闆重新命名。在實際移植過程中,可用Linux指令查找移植參考闆的特定代碼,如grep–r

RPXlite ./可确定出在U-Boot中與RPXlite闆有關的代碼,依此對照目标闆實際進行屏蔽或修改。同時應不局限于移植參考闆中的代碼,要廣泛借鑒U-Boot中已有的代碼更好地實作一些具體的功能。

③ U-Boot燒寫位址。不同目标闆,對U-Boot在FLASH中存放位址要求不盡相同。事實上,這是由處理器中斷複位向量來決定的,與主機闆硬體相關,對MPC8xx主機闆來講,就是由硬體配置字(HRCW)決定的。也就是說,U-Boot燒寫具體位置是由硬體決定的,而不是程式設計來選擇的。程式中相應U-Boot起始位址必須與硬體所确定的硬體複位向量相吻合;如RPXlite DW闆的中斷複位向量設定為0x00000100。是以, U-Boot的BIN鏡像檔案必須燒寫到FLASH的起始位置。事實上,大多數的PPC系列的處理器中斷複位向量是0x00000100和0xfff00100。這也是一般所說的高位啟動和低位啟動的BOOT

LOADER所在位置。可通過修改U-Boot源碼<目标闆>.h頭檔案中CFG_MONITOR_BASE和board/<目标闆>/config.mk中的TEXT_BASE的設定來與硬體配置相對應。

④ CPU寄存器參數設定。根據處理器系列、類型不同,寄存器名稱與作用有一定差别。必須根據目标闆的實際,進行合理配置。一個較為可行和有效的方法,就是借鑒參考移植闆的配置,再根據目标闆實際,進行合理修改。這是一個較費功夫和考驗耐力的過程,需要仔細對照處理器各寄存器定義、參考設定、目标闆實際作出選擇并不斷測試。MPC8xx處理器較為關鍵的寄存器設定為SIUMCR、PLPRCR、SCCR、BRx、ORx。

⑤序列槽調試。能從序列槽輸出資訊,即使是亂碼,也可以說U-Boot移植取得了實質性突破。依據筆者調試經曆,序列槽是否有輸出,除了與序列槽驅動相關外,還與FLASH相關的寄存器設定有關。因為U-Boot是從FLASH中被引導啟動的,如果FLASH設定不正确,U-Boot代碼讀取和執行就會出現一些問題。是以,還需要就FLASH的相關寄存器設定進行一些參數調試。同時,要注意序列槽收發晶片相關引腳工作波形。依據筆者調試情況,如果序列槽無輸出或出現亂碼,一種可能就是該晶片損壞或工作不正常。

⑥ 與啟動FLASH相關的寄存器BR0、OR0的參數設定。應根據目标闆FLASH的資料手冊與BR0和OR0的相關位含義進行合理設定。這不僅關系到FLASH能否正常工作,而且與序列槽調試有直接的關聯。

⑦關于CPLD電路。目标闆上是否有CPLD電路絲毫不會影響U-Boot的移植與嵌入式作業系統的正常運作。事實上,CPLD電路是一個集中将闆上電路的一些邏輯關系可程式設計設定的一種實作方法。其本身所起的作用就是實作一些目标闆所需的脈沖信号和電路邏輯,其功能完全可以用一些邏輯電路與CPU口線來實作。

⑧ SDRAM的驅動。序列槽能輸出以後,U-Boot移植是否順利基本取決于SDRAM的驅動是否正确。與序列槽調試相比,這部分工作更為核心,難度更大。MPC8xx目标闆SDRAM驅動涉及三部分。一是相關寄存器的設定;二是UPM表;三是SDRAM上電初始化過程。任何一部分有問題,都會影響U-Boot、嵌入式作業系統甚至應用程式的穩定、可靠運作。是以說,SDRAM的驅動不僅關系到U-Boot本身能否正常運作,而且還與後續部分相關,是相當關鍵的部分。

⑨補充功能的添加。在獲得一個能工作的U-Boot後,就可以根據目标闆和實際開發需要,添加一些其它功能支援。如以太網、LCD、NVRAM等。與序列槽和SDRAM調試相比,在已有基礎之上,這些功能添加還是較為容易的。大多隻是在參考現有源碼的基礎上,進行一些修改和配置。

另外,如果在自主設計的主機闆上移植U-Boot,那麼除了考慮上述軟體因素以外,還需要排查目标闆硬體可能存在的問題。如原理設計、PCB布線、元件好壞。在移植過程中,敏銳判斷出故障态是硬體還是軟體問題,往往是關系到項目進度甚至移植成敗的關鍵,相應難度會增加許多。

       下面以移植u-boot到44B0開發闆的步驟為例,移植中上僅需要修改和硬體相關的部分。在代碼結構上:

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,是以這裡分兩次寫入。

繼續閱讀