您好,歡迎進(jìn)入深圳市穎特新科技有限公司官方網(wǎng)站!
來自http://blog.ednchina.com/hhuwxf/1915416/message.aspx,感謝作者
一、Bootloader的引入從前面的硬件實(shí)驗(yàn)可以知道,系統(tǒng)上電之后,需要一段程序來進(jìn)行初始化:關(guān)閉 WATCHDOG、改變系統(tǒng)時(shí)鐘、初始化存儲(chǔ)控制器、將更多的代碼復(fù)制到內(nèi)存中等等。如果它能將操作系統(tǒng)內(nèi)核(無論從本地,比如Flash;還是從遠(yuǎn)端, 比如通過網(wǎng)絡(luò))復(fù)制到內(nèi)存中運(yùn)行,就稱這段程序?yàn)锽ootloader。
簡(jiǎn)單地說,Bootloader就是這么一小段程序,它在系統(tǒng)上電時(shí)開始執(zhí)行,初始化硬件設(shè)備、準(zhǔn)備好軟件環(huán)境,最后調(diào)用操作系統(tǒng)內(nèi)核。
可以增強(qiáng)Bootloader的功能,比如增加網(wǎng)絡(luò)功能、從PC上通過串口或網(wǎng)絡(luò)下載文件、 燒寫文件、將Flash上壓縮的文件解壓后再運(yùn)行等──這就是一個(gè)功能更為強(qiáng)大的Bootloader,也稱為Monitor。實(shí)際上,在最終產(chǎn)品中用戶 并不需要這些功能,它們只是為了方便開發(fā)。Bootloader的實(shí)現(xiàn)嚴(yán)重依賴于具體硬件,在嵌入式系統(tǒng)中硬件配置千差萬別,即使是相同的CPU,它的外設(shè)(比如Flash)也可能不同,所以不可能有一個(gè)Bootloader支持所有的CPU、所有的電路板。即使是支持CPU架構(gòu)比較多的U-Boot,也不是一拿來就可以使用的(除非里面的配置剛好與你的板子相同),需要進(jìn)行一些移植。
二、 Bootloader的啟動(dòng)方式CPU上電后,會(huì)從某個(gè)地址開始執(zhí)行。比如MIPS結(jié)構(gòu)的CPU會(huì)從0xBFC00000取 第一條指令,而ARM結(jié)構(gòu)的CPU則從地址0x0000000開始。嵌入式單板中,需要把存儲(chǔ)器件ROM或Flash等映射到這個(gè)地址,Bootloader就存放在這個(gè)地址開始處,這樣一上電就可以執(zhí)行。
在開發(fā)時(shí),通常需要使用各種命令操作Bootloader,一般通過串口來連接PC和開發(fā)板,可以在串口上輸入各種命令、觀察運(yùn)行結(jié)果等。這也只是對(duì)開發(fā)人員才有意義,用戶使用產(chǎn)品時(shí)是不用接串口來控制Bootloader的。從這個(gè)觀點(diǎn)來看,Bootloader可以分為兩種操作模式(Operation Mode):
(1)啟動(dòng)加載(Boot loading)模式。
上電后,Bootloader從板子上的某個(gè)固態(tài)存儲(chǔ)設(shè)備上將操作系統(tǒng)加載到RAM中運(yùn)行,整個(gè)過程并沒有用戶的介入。產(chǎn)品發(fā)布時(shí),Bootloader工作在這種模式下。
(2)下載(Downloading)模式。
在這種模式下,開發(fā)人員可以使用各種命令,通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)(Host)下載文件(比如內(nèi)核映像、文件系統(tǒng)映像),將它們直接放在內(nèi)存運(yùn)行或是燒入Flash類固態(tài)存儲(chǔ)設(shè)備中。
板子與主機(jī)間傳輸文件時(shí),可以使用串口的xmodem/ymodem/zmodem協(xié)議,它們使用簡(jiǎn)單,只是速度比較慢;還可以使用網(wǎng)絡(luò)通過tftp、nfs協(xié)議來傳輸,這時(shí),主機(jī)上要開啟tftp、nfs服務(wù);還有其他方法,比如USB等。像Blob或U-Boot等這樣功能強(qiáng)大的Bootloader通常同時(shí)支持這兩種工作模式,而且允許用戶在這兩種工作模式之間進(jìn)行切換。比如,U-Boot在啟動(dòng)時(shí)處于正常的啟動(dòng)加載模式,但是它會(huì)延時(shí)若干秒(這可以設(shè)置)等待終端用戶按下任意鍵而將U-Boot切換到下載模式。如果在指定時(shí)間內(nèi)沒有用戶按鍵,則U-Boot繼續(xù)啟動(dòng)Linux內(nèi)核。 ]
15.1.2 Bootloader的結(jié)構(gòu)和啟動(dòng)過程
在移植之前先了解Bootloader的一些通用概念,對(duì)理解它的代碼會(huì)有所幫助。
在一個(gè)嵌入式Linux系統(tǒng)中,從軟件的角度通?梢苑譃4個(gè)層次:
(1)引導(dǎo)加載程序,包括固化在固件(firmware)中的 boot 代碼(可選)和Bootloader兩大部分。
有些CPU在運(yùn)行Bootloader之前先運(yùn)行一段固化的程序(固件,firmware),比如x86結(jié)構(gòu)的CPU就是先運(yùn)行BIOS中的固件,然后才運(yùn)行硬盤第一個(gè)分區(qū)(MBR)中的Bootloader。
在大多嵌入式系統(tǒng)中并沒有固件,Bootloader是上電后執(zhí)行的第一個(gè)程序。
(2)Linux內(nèi)核。
特定于嵌入式板子的定制內(nèi)核以及內(nèi)核的啟動(dòng)參數(shù)。內(nèi)核的啟動(dòng)參數(shù)可以是內(nèi)核默認(rèn)的,或是由Bootloader傳遞給它的。
(3)文件系統(tǒng)。
包括根文件系統(tǒng)和建立于Flash內(nèi)存設(shè)備之上的文件系統(tǒng)。里面包含了Linux系統(tǒng)能夠運(yùn)行所必需的應(yīng)用程序、庫(kù)等,比如可以給用戶提供操作Linux的控制界面的shell程序,動(dòng)態(tài)連接的程序運(yùn)行時(shí)需要的glibc或uClibc庫(kù),等等。
(4)用戶應(yīng)用程序。
特定于用戶的應(yīng)用程序,它們也存儲(chǔ)在文件系統(tǒng)中。有時(shí)在用戶應(yīng)用程序和內(nèi)核層之間可能還會(huì)包括一個(gè)嵌入式圖形用戶界面。常用的嵌入式 GUI 有:Qtopia 和 MiniGUI 等。
顯然,在嵌入系統(tǒng)的固態(tài)存儲(chǔ)設(shè)備上有相應(yīng)的分區(qū)來存儲(chǔ)它們。
“Boot parameters”分區(qū)中存放一些可設(shè)置的參數(shù),比如IP地址、串口波特率、要傳遞給內(nèi)核的命令行參數(shù)等。正常啟動(dòng)過程中,Bootloader首先
運(yùn)行,然后它將內(nèi)核復(fù)制到內(nèi)存中(也有些內(nèi)核可以在固態(tài)存儲(chǔ)設(shè)備上直接運(yùn)行),并且在內(nèi)存某個(gè)固定的地址設(shè)置好要傳遞給內(nèi)核的參數(shù),最后運(yùn)行內(nèi)核。內(nèi)核啟
動(dòng)之后,它會(huì)掛接(mount)根文件系統(tǒng)(“Root filesystem”),啟動(dòng)文件系統(tǒng)中的應(yīng)用程序。
一般而言,這兩個(gè)階段完成的功能可以如下分類,但這不是絕對(duì)的:
(1)Bootloader第一階段的功能。
在第一階段進(jìn)行的硬件初始化一般包括:關(guān)閉WATCHDOG、關(guān)中斷、設(shè)置CPU的速度和時(shí)鐘頻率、RAM初始化等。這些并不都是必需的,比如S3C2410/S3C2440的開發(fā)板所使用的U-Boot中,就將CPU的速度和時(shí)鐘頻率的設(shè)置放在第二階段。
甚至,將第二階段的代碼復(fù)制到RAM空間中也不是必需的,對(duì)于NOR Flash等存儲(chǔ)設(shè)備,完全可以在上面直接執(zhí)行代碼,只不過這相比在RAM中執(zhí)行效率大為降低。
(2)Bootloader第二階段的功能。
為了方便開發(fā),至少要初始化一個(gè)串口以便程序員與Bootloader進(jìn)行交互。
所謂檢測(cè)內(nèi)存映射,就是確定板上使用了多少內(nèi)存,它們的地址空間是什么。由于嵌入式開發(fā)中,Bootloader多是針對(duì)某類板子進(jìn)行編寫,所以可以根據(jù)板子的情況直接設(shè)置,不需要考慮可以適用于各類情況的復(fù)雜算法。
Flash上的內(nèi)核映像有可能是經(jīng)過壓縮的,在讀到RAM之后,還需要進(jìn)行解壓。當(dāng)然,對(duì)于有自解壓功能的內(nèi)核,不需要Bootloader來解壓。
將根文件系統(tǒng)映像復(fù)制到RAM中,這不是必需的。這取決于是什么類型的根文件系統(tǒng),以及內(nèi)核訪問它的方法。
為內(nèi)核設(shè)置啟動(dòng)參數(shù)將在下一小節(jié)介紹。
將內(nèi)核存放在適當(dāng)?shù)奈恢煤螅苯犹降剿娜肟邳c(diǎn)即可調(diào)用內(nèi)核。調(diào)用內(nèi)核之前,下列條件要滿足:
(1)CPU 寄存器的設(shè)置。
(2)CPU工作模式。
(3)Cache 和 MMU 的設(shè)置。
如果用C語言,可以像下列示例代碼一樣來調(diào)用內(nèi)核:
void (*theKernel)(int zero, int arch, u32
params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE; ……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);
Bootloader與內(nèi)核的交互是單向的,Bootloader將各類參數(shù)傳給內(nèi)核。由于它們不能同時(shí)運(yùn)行,傳遞辦法只有一個(gè):Bootloader將參數(shù)放在某個(gè)約定的地方之后,再啟動(dòng)內(nèi)核,內(nèi)核啟動(dòng)后從這個(gè)地方獲得參數(shù)。除了約定好參數(shù)存放的地址外,還要規(guī)定參數(shù)的結(jié)構(gòu)。Linux 2.4.x
以后的內(nèi)核都期望以標(biāo)記列表(tagged list)的形式來傳遞啟動(dòng)參數(shù)。標(biāo)記,就是一種數(shù)據(jù)結(jié)構(gòu);標(biāo)記列表,就是挨著存放的多個(gè)標(biāo)記。標(biāo)記列表以標(biāo)記ATAG_CORE 開始,以標(biāo)記ATAG_NONE 結(jié)束。標(biāo)記的數(shù)據(jù)結(jié)構(gòu)為tag,它由一個(gè)tag_header結(jié)構(gòu)和一個(gè)聯(lián)合(union)組成。tag_header結(jié)構(gòu)表示標(biāo)記的類型及長(zhǎng)度,比如是 表示內(nèi)存還是表示命令行參數(shù)等。對(duì)于不同類型的標(biāo)記使用不同的聯(lián)合(union),比如表示內(nèi)存時(shí)使用tag_mem32,表示命令行時(shí)使用 tag_cmdline。數(shù)據(jù)結(jié)構(gòu)tag和tag_header定義在Linux內(nèi)核源碼的include/asm/setup.h頭文件中:
1
2
3
4
5
6
7
8
|
struct tag_header
{ u32 size; u32 tag; }; <br> struct tag
{ struct tag_header
hdr; union {
struct tag_corecore;
struct tag_mem32mem;
struct tag_videotextvideotext; struct tag_ramdiskramdisk;
struct tag_initrdinitrd;
struct tag_serialnrserialnr;
struct tag_revisionrevision;
struct tag_videolfbvideolfb;
struct tag_cmdlinecmdline;
<br> /*
* Acorn specific
*/ struct tag_acornacorn;
<br> /*
* DC21285 specific */ struct tag_memclkmemclk;
} u; }; |
下面以設(shè)置內(nèi)存標(biāo)記、命令行標(biāo)記為例說明參數(shù)的傳遞:
(1)設(shè)置標(biāo)記 ATAG_CORE。
標(biāo)記列表以標(biāo)記 ATAG_CORE開始,假設(shè)Bootloader與內(nèi)核約定的參數(shù)存放地址為0x30000100,則可以以如下代碼設(shè)置標(biāo)記 ATAG_CORE:
params = (struct tag *) 0x30000100;
<br>params->hdr.tag = ATAG_CORE; params->hdr.size =
tag_size (tag_core); <br>params->u.core.flags = 0;
params->u.core.pagesize = 0; params->u.core.rootdev = 0;
<br>params = tag_next (params);
其中,tag_next定義如下,它指向當(dāng)前標(biāo)記的末尾:
#define tag_next(t)((struct tag *)((u32 *)(t) + (t)->hdr.size))
(2)設(shè)置內(nèi)存標(biāo)記。
假設(shè)開發(fā)板使用的內(nèi)存起始地址為0x30000000,大小為0x4000000,則內(nèi)存標(biāo)記可以如下設(shè)置:
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = 0x30000000;
params->u.mem.size = 0x4000000;
params = tag_next (params);
(3)設(shè)置命令行標(biāo)記。
命令行就是一個(gè)字符串,它被用來控制內(nèi)核的一些行為。比如"root=/dev /mtdblock2 init=/linuxrc console=ttySAC0"表示根文件系統(tǒng)在MTD2分區(qū)上,系統(tǒng)啟動(dòng)后執(zhí)行的第一個(gè)程序?yàn)?linuxrc,控制臺(tái)為ttySAC0(即第一個(gè)串口)。
命令行可以在Bootloader中通過命令設(shè)置好,然后如下構(gòu)造標(biāo)記傳給內(nèi)核:
char *p = "root=/dev/mtdblock2 init=/linuxrc console=ttySAC0";
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
(4)設(shè)置標(biāo)記ATAG_NONE。
標(biāo)記列表以標(biāo)記ATAG_NONE結(jié)束,如下設(shè)置:
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
常用Bootloader介紹
現(xiàn)在Bootloader種類繁多,比如x86上有LILO、GRUB等。對(duì)于ARM架構(gòu)的CPU,有U-Boot、Vivi等。它們各有特點(diǎn),下面列出Linux的開放源代碼的Bootloader及其支持的體系架構(gòu),如表15.1所示。
開放源碼的Linux引導(dǎo)程序
<table fck__showtableborders?="">
Bootloader
Monitor
描述
X86
ARM
PowerPC
LILO
否
Linux磁盤引導(dǎo)程序
是
否
否
GRUB
否
GNU的LILO替代程序
是
否
否
Loadlin
否
從DOS引導(dǎo)Linux
是
否
否
ROLO
否
從ROM引導(dǎo)Linux而不需要BIOS
是
否
否
Etherboot
否
通過以太網(wǎng)卡啟動(dòng)Linux系統(tǒng)的固件
是
否
否
LinuxBIOS
否
完全替代BUIS的Linux引導(dǎo)程序
是
否
否
BLOB
是
LART等硬件平臺(tái)的引導(dǎo)程序
否
是
否
U-Boot
是
通用引導(dǎo)程序
是
是
是
RedBoot
是
基于eCos的引導(dǎo)程序
是
是
是
Vivi
是
Mizi公司針對(duì)SAMSUNG的ARM CPU設(shè)計(jì)的引導(dǎo)程序
否
是
否
對(duì)于本書使用的S3C2410/S3C2440開發(fā)板,U-Boot和Vivi是兩個(gè)好選擇。Vivi是Mizi公司針對(duì)SAMSUNG的ARM架構(gòu)CPU專門設(shè)計(jì)的,基本上可以直接使用,命令簡(jiǎn)單方便。不過其初始版本只支持串口下載,速度較慢。在網(wǎng)上出現(xiàn)了各種改進(jìn)版本:支持網(wǎng)絡(luò)功能、USB功能、燒寫YAFFS文件系統(tǒng)映像等。U-Boot則支持大多CPU,可以燒寫EXT2、JFFS2文件系統(tǒng)映像,支持串口下載、網(wǎng)絡(luò)下載,并提供了大量的命令。相對(duì)于Vivi,它的使用更復(fù)雜,但是可以用來更方便地調(diào)試程序。
2 U-Boot分析與移植
2.1 U-Boot工程簡(jiǎn)介
U-Boot,全稱為Universal Boot Loader,即通用Bootloader,是遵循GPL條款的開放源代碼項(xiàng)目。其前身是由德國(guó)DENX軟件工程中心的Wolfgang Denk基于8xxROM的源碼創(chuàng)建的PPCBOOT工程。后來整理代碼結(jié)構(gòu)使得非常容易增加其他類型的開發(fā)板、其他架構(gòu)的CPU(原來只支持 PowerPC);增加更多的功能,比如啟動(dòng)Linux、下載S-Record格式的文件、通過網(wǎng)絡(luò)啟動(dòng)、通過PCMCIA/CompactFLash /ATA disk/SCSI等方式啟動(dòng)。增加ARM架構(gòu)CPU及其他更多CPU的支持后,改名為U-Boot。
它的名字“通用”有兩層含義:可以引導(dǎo)多種操作系統(tǒng)、支持多種架構(gòu)的CPU。它支持如下操作 系統(tǒng):Linux、NetBSD、 VxWorks、QNX、RTEMS、ARTOS、LynxOS等,
支持如下架構(gòu)的CPU:PowerPC、MIPS、x86、ARM、NIOS、 XScale等。
U-Boot有如下特性:
可以從http://sourceforge.net/projects/u-boot獲得U-Boot的最新版本,如果使用過程中碰到問題或是發(fā)現(xiàn)Bug,可以通過郵件列表網(wǎng)站http://lists.sourceforge.net/lists/listinfo/u-boot-users/獲得幫助。
最新的更新代碼地址http://www.denx.de/wiki/U-Boot/WebHome
2.2 U-Boot源碼結(jié)構(gòu)
本書在u-boot-1.1.6的基礎(chǔ)上進(jìn)行分析和移植,從sourceforge網(wǎng)站下載u-boot-1.1.6.tar.bz2后解壓即得到全部源碼。U-Boot源碼目錄結(jié)構(gòu)比較簡(jiǎn)單、獨(dú)立,目錄結(jié)構(gòu)也比較淺,很容易全部掌握。
u-boot-1.1.6根目錄下共有26個(gè)子目錄,可以分為4類:
(1)平臺(tái)相關(guān)的或開發(fā)板相關(guān)的。
(2)通用的函數(shù)。
(3)通用的設(shè)備驅(qū)動(dòng)程序。
(4)U-Boot工具、示例程序、文檔。
先將這26個(gè)目錄的功能與作用如表15.2所示。
表2 U-Boot頂層目錄說明
<table fck__showtableborders?="">
目錄
特性
解釋說明
board
開發(fā)板相關(guān)
對(duì)應(yīng)不同配置的電路板(即使CPU相同),比如smdk2410、sbc2410x
cpu
平臺(tái)相關(guān)
對(duì)應(yīng)不同的CPU,比如arm920t、arm925t、i386等;在它們的子目錄下仍可以進(jìn)一步細(xì)分,比如arm920t下就有at91rm9200、s3c24x0
lib_i386類似
某一架構(gòu)下通用的文件
include
通用的函數(shù)
頭文件和開發(fā)板配置文件,開發(fā)板的配置文件都放在include/configs目錄下,U-Boot沒有make menuconfig類似的萊單來進(jìn)行可視化配置,需要手動(dòng)地修改配置文件中的宏定義
lib_generic
通用的庫(kù)函數(shù),比如printf等
common
通用的函數(shù),多是對(duì)下一層驅(qū)動(dòng)程序的進(jìn)一步封裝
disk
通用的設(shè)備驅(qū)動(dòng)程序
硬盤接口程序
drivers
各類具體設(shè)備的驅(qū)動(dòng)程序,基本上可以通用,它們通過宏從外面引入平臺(tái)/開發(fā)板相關(guān)的函數(shù)
dtt
數(shù)字溫度測(cè)量器或者傳感器的驅(qū)動(dòng)
fs
文件系統(tǒng)
nand_spl
U-Boot一般從ROM、NOR Flash等設(shè)備啟動(dòng),現(xiàn)在開始支持從NAND Flash啟動(dòng),但是支持的CPU種類還不多
net
各種網(wǎng)絡(luò)協(xié)議
post
上電自檢程序
rtc
實(shí)時(shí)時(shí)鐘的驅(qū)動(dòng)
doc
文檔
開發(fā)、使用文檔
examples
示例程序
一些測(cè)試程序,可以使用U-Boot下載后運(yùn)行
tools
工具
制作S-Record、U-Boot格式映像的工具,比如mkimage
U-Boot中各目錄間也是有層次結(jié)構(gòu)的,雖然這種分法不是絕對(duì)的,但是在移植過程中可以提供一些指導(dǎo)意義,如圖2所示。
2 U-Boot頂層目錄的層次結(jié)構(gòu)
比如common/cmd_nand.c文件提供了操作NAND
Flash的各種命令,這些命令通過調(diào)用drivers/nand/nand_base.c中的擦除、讀寫函數(shù)來實(shí)現(xiàn)。這些函數(shù)針對(duì)NAND
Flash的共性作了一些封裝,將平臺(tái)/開發(fā)板相關(guān)的代碼用宏或外部函數(shù)來代替。而這些宏與外部函數(shù),如果與平臺(tái)相關(guān),就要在下一層次的cpu
/xxx(xxx表示某型號(hào)的CPU)中實(shí)現(xiàn);如果與開發(fā)板相關(guān),就要在下一層次的board/xxx目錄(xxx表示某款開發(fā)板)中實(shí)現(xiàn)。本書移植的
U-Boot,就是在cpu/arm920t/s3c24x0目錄下增加了一個(gè)nand_flash.c文件來實(shí)現(xiàn)這些函數(shù)。
以增加燒寫yaffs文件系統(tǒng)映像的功能為例──就是在common目錄下的
cmd_nand.c中增加命令,比如nand
write.yaffs:這個(gè)命令要調(diào)用drivers/nand/nand_util.c中的相應(yīng)函數(shù),針對(duì)yaffs文件系統(tǒng)的特點(diǎn)依次調(diào)用擦除、燒
寫函數(shù)。而這些函數(shù)依賴于drivers/nand/nand_base.c、cpu/arm920t/s3c24x0/nand_flash.c文件中
的相關(guān)函數(shù)。
目前u-boot-1.1.6支持10種架構(gòu)──根目錄下有10個(gè)類似lib_i386的目
錄、31個(gè)型號(hào)(類型)的CPU──cpu目錄下有31個(gè)子目錄,214種開發(fā)板──board目錄下有214個(gè)子目錄,很容易從中找到與自己的板子相似
的配置,在上面稍作修改即可使用。
2.3 U-Boot的配置、編譯、連接過程
1. U-Boot初體驗(yàn)
u-boot-1.1.6中有幾千個(gè)文件,要想了解對(duì)于某款開發(fā)板,使用哪些文件、哪個(gè)文件首先執(zhí)行、可執(zhí)行文件占用內(nèi)存的情況,最好的方法就是閱讀它的Makefile。
根據(jù)頂層Readme文件的說明,可以知道如果要使用開發(fā)板board/<board_name>,就先執(zhí)行“make <board_name>_config”命令進(jìn)行配置,然后執(zhí)行“make all”,就可以生成如下3個(gè)文件:
對(duì)于S3C2410的開發(fā)板,執(zhí)行“make smdk2410_config”、“make all”后生成的u-boot.bin可以燒入NOR Flash中運(yùn)行。啟動(dòng)后可以看到串口輸出一些信息后進(jìn)入控制界面,等待用戶的輸入。
對(duì)于S3C2440的開發(fā)板,燒入上面生成的u-boot.bin,串口無輸出,需要修改代碼。
在修改代碼之前,先看看上面兩個(gè)命令“make smdk2410_config”、“make all”做了什么事情,以了解程序的流程,知道要修改哪些文件。
另外,編譯U-Boot成功后,還會(huì)在它的tools子目錄下生成一些工具,比如mkimage等。將它們復(fù)制到/usr/local/bin目錄下,以后就可以直接使用它們了,比如編譯內(nèi)核時(shí),會(huì)使用mkimage來生成U-Boot格式的內(nèi)核映像文件uImage。
2. U-Boot的配置過程
在頂層Makefile中可以看到如下代碼:
SRCTREE:= $(CURDIR)
……
MKCONFIG:= $(SRCTREE)/mkconfig
……
smdk2410_config:unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
假定我們?cè)趗-boot-1.1.6的根目錄下編譯,則其中的MKCONFIG就是根目錄下的mkconfig文件。$(@:_config=)的結(jié)果就是將“smdk2410_config”中的“_config”去掉,結(jié)果為
“smdk2410”。所以“make smdk2410_config”實(shí)際上就是執(zhí)行如下命令:./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0
再來看看mkconfig的作用,在mkconfig文件開頭第6行給出了它的用法:
06 # Parameters: Target Architecture CPU Board [VENDOR] [SOC]
這里解釋一下概念,對(duì)于S3C2410、S3C2440,它們被稱為SoC(System on Chip),上面除CPU外,還集成了包括UART、USB控制器、NAND Flash控制器等等設(shè)備(稱為片內(nèi)外設(shè))。S3C2410/S3C2440中的CPU為arm920t。
以下,分步驟分析mkconfig的作用:
(1)確定開發(fā)板名稱BOARD_NAME。
11
APPEND=no# Default: Create new config
file 12
BOARD_NAME= "" #
Name to print in make output 13 14
while [
$# -gt 0 ] ; do 15
case "$1" in 16
--) shift ; break ;; 17
-a) shift ; APPEND=yes ;; 18
-n) shift ; BOARD_NAME= "${1%%_config}" ;
shift ;; 19
*) break ;; 20
esac 21
done 22 23
[ "${BOARD_NAME}" ]
|| BOARD_NAME= "$1"
|
對(duì)于“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令,其中沒有“--”、“-a”、“-n”等符號(hào),所以第14~22行沒做任何事情。第11、12行兩個(gè)變量仍維持原來的值。
執(zhí)行完第23行后,BOARD_NAME的值等于第1個(gè)參數(shù),即“smdk2410”。
(2)創(chuàng)建到平臺(tái)/開發(fā)板相關(guān)的頭文件的鏈接。
略過mkconfig文件中的一些沒有起作用的行:
|
30
# 31
# Create link to architecture specific headers 32
# 33
if [
"$SRCTREE" !=
"$OBJTREE" ]
; then ……
45
else 46
cd ./include 47
rm -f asm 48
ln -s asm-$2 asm 49
fi 50 |
第33行判斷源代碼目錄和目標(biāo)文件目錄是否一樣,可以選擇在其他目錄下編譯U-Boot,這可以令源代碼目錄保持干凈,可以同時(shí)使用不同的配置進(jìn)行編譯。不過本書直接在源代碼目錄下編譯的,第33行的條件不滿足,將執(zhí)行else分支的代碼。
第46~48行進(jìn)入include目錄,刪除asm文件(這是上一次配置時(shí)建立的鏈接文件),然后再次建立asm文件,并令它鏈接向asm-$2目錄,即asm-arm。
繼續(xù)往下看代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
51
rm -f asm-$2/arch 52
53
if [
-z "$6" -o
"$6" =
"NULL" ]
; then 54
ln -s ${LNPREFIX}arch-$3 asm-$2/arch 55
else 56
ln -s ${LNPREFIX}arch-$6 asm-$2/arch 57
fi 58
59
if [
"$2" =
"arm" ]
; then 60
rm -f asm-$2/proc 61
ln -s ${LNPREFIX}proc-armv asm-$2/proc 62
fi 63 |
第51行刪除asm-$2/arch目錄,即asm-arm/arch。
對(duì)于“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令,$6為“s3c24x0”,不為空,也不是“NULL”,所以第53行的條件不滿足,將執(zhí)行else分支。
第56行中,LNPREFIX為空,所以這個(gè)命令實(shí)際上就是:ln -s arch-$6 asm-$2/arch,即:ln -s arch-s3c24x0 asm-arm/arch。
第60、61行重新建立asm-arm/proc文件,并讓它鏈接向proc-armv目錄。
(3)創(chuàng)建頂層Makefile包含的文件include/config.mk。
對(duì)于“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令,上面幾行代碼創(chuàng)建的config.mk文件內(nèi)容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
64
# 65
# Create include file for Make
66
# 67
echo "ARCH
= $2" >
config.mk 68
echo "CPU
= $3" >>
config.mk 69
echo "BOARD
= $4" >>
config.mk 70
71
[ "$5" ]
&& [ "$5" !=
"NULL" ]
&& echo "VENDOR
= $5" >>
config.mk 72
73
[ "$6" ]
&& [ "$6" !=
"NULL" ]
&& echo "SOC
= $6" >>
config.mk 74 |
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC = s3c24x0
(4)創(chuàng)建開發(fā)板相關(guān)的頭文件include/config.h。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
75
# 76
# Create board specific header file 77
# 78
if [
"$APPEND" =
"yes" ]#
Append to existing config file 79
then 80
echo >> config.h 81
else 82
> config.h# Create new config
file 83
fi 84
echo "/*
Automatically generated - do not edit */" >>config.h
85
echo "#include
<configs/$1.h>" >>config.h
86 |
前面說過,APPEND維持原值“no”,所以config.h被重新建立,它的內(nèi)容如下:
/* Automatically generated - do not edit */
#include <configs/smdk2410.h>"
現(xiàn)在總結(jié)一下,配置命令“make
smdk2410_config”,實(shí)際的作用就是執(zhí)行“./mkconfig smdk2410 arm arm920t smdk2410
NULL s3c24x0”命令。假設(shè)執(zhí)行“./mkconfig $1 $2 $3 $4 $5 $6”命令,則將產(chǎn)生如下結(jié)果:
(1)開發(fā)板名稱BOARD_NAME等于$1;
(2)創(chuàng)建到平臺(tái)/開發(fā)板相關(guān)的頭文件的鏈接:
ln -s asm-$2 asm
ln -s arch-$6 asm-$2/arch
ln -s proc-armv asm-$2/proc# 如果$2不是arm的話,此行沒有
(3) 創(chuàng)建頂層Makefile包含的文件include/config.mk。
ARCH = $2
CPU = $3
BOARD = $4
VENDOR = $5# $5為空,或者是NULL的話,此行沒有
SOC = $6# $6為空,或者是NULL的話,此行沒有
(4)創(chuàng)建開發(fā)板相關(guān)的頭文件include/config.h。
/* Automatically generated - do not edit */
#include <configs/$1.h>"
從這4個(gè)結(jié)果可以知道,如果要在board目錄下新建一個(gè)開發(fā)
板<board_name>的目錄,則在include/config目錄下也要建立一個(gè)文件<board_name>.h,里
面存放的就是開發(fā)板<board_name>的配置信息。
U-Boot還沒有類似Linux一樣的可視化配置界面(比如使用make menuconfig來配置),要手動(dòng)修改配置文件include/config/<board_name>.h來裁減、設(shè)置U-Boot。
配置文件中有兩類宏:
(1)一類是選項(xiàng)(Options),前綴為“CONFIG_”,它們用于選擇CPU、SOC、開發(fā)板類型,設(shè)置系統(tǒng)時(shí)鐘、選擇設(shè)備驅(qū)動(dòng)等。比如:
#define CONFIG_ARM920T1/* This is an ARM920T Core*/
#defineCONFIG_S3C24101/* in a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK24101/* on a SAMSUNG SMDK2410 Board */
#define CONFIG_SYS_CLK_FREQ12000000/* the SMDK2410 has 12MHz input clock */
#define CONFIG_DRIVER_CS89001/* we have a CS8900 on-board */
(2)另一類是參數(shù)(Setting),前綴為“CFG_”,它們用于設(shè)置malloc緩沖池的大小、U-Boot的提示符、U-Boot下載文件時(shí)的默認(rèn)加載地址、Flash的起始地址等。比如:
#define CFG_MALLOC_LEN(CFG_ENV_SIZE + 128*1024)
#defineCFG_PROMPT"100ASK> "/* Monitor Command Prompt*/
#defineCFG_LOAD_ADDR0x33000000/* default load address*/
#define PHYS_FLASH_10x00000000 /* Flash Bank #1 */
從下面的編譯、連接過程可知,U-Boot中幾乎每個(gè)文件都被編譯和連接,但是這些文件是否包含有效的代碼,則由宏開關(guān)來設(shè)置。比如對(duì)于網(wǎng)卡驅(qū)動(dòng)drivers/cs8900.c,它的格式為:
#include <common.h>/* 將包含配置文件include/config/<board_name>.h */
……
#ifdef CONFIG_DRIVER_CS8900
/* 實(shí)際的代碼 */
……
#endif/* CONFIG_DRIVER_CS8900 */
如果定義了宏CONFIG_DRIVER_CS8900,則文件中包含有效的代碼;否則,文件被注釋為空。
可以這樣粗糙地認(rèn)為,“CONFIG_”除了設(shè)置一些參數(shù)外,主要用來設(shè)置U-Boot的功能、選擇使用文件中的哪一部分;而“CFG_”用來設(shè)置更細(xì)節(jié)的參數(shù)。
3. U-Boot的編譯、連接過程
配置完后,執(zhí)行“make all”即可編譯,從Makefile中可以了解U-Boot使用了哪些文件、哪個(gè)文件首先執(zhí)行、可執(zhí)行文件占用內(nèi)存的情況。
先確定用到哪些文件,下面只摘取Makefile中與arm相關(guān)的部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
117
include $(OBJTREE)/include/config.mk 118
exportARCH CPU BOARD VENDOR SOC 119
……
127
ifeq ($(ARCH),arm) 128
CROSS_COMPILE = arm-linux- 129
endif ……
163
# load other configuration 164
include $(TOPDIR)/config.mk 165 |
第117、164行用于包含其他的config.mk文件,第117行所要包含文件的就是在
上面的配置過程中制作出來的include/config.mk文件,其中定義了ARCH、CPU、BOARD、SOC等4個(gè)變量的值為arm、
arm920t、smdk2410、s3c24x0。
第164行包含頂層目錄的config.mk文件,它根據(jù)上面4個(gè)變量的值確定了編譯器、編譯選項(xiàng)等。其中對(duì)我們理解編譯過程有幫助的是BOARDDIR、LDFLAGS的值,config.mk中:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
88
BOARDDIR = $(BOARD) ……
91
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk# include board specific rules ……
143
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds ……
189
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS) |
在board/smdk2410/config.mk中,定義了“TEXT_BASE =
0x33F80000”。所以,最終結(jié)果如下:BOARDDIR為smdk2410;LDFLAGS中有“-T
board/smdk2410/u-boot.lds -Ttext 0x33F80000”字樣。
繼續(xù)往下看Makefile:
166 #########################################################################
167 # U-Boot objects....order is important (i.e. start must be first)
168
169 OBJS = cpu/$(CPU)/start.o
……
193 LIBS = lib_generic/libgeneric.a
194 LIBS += board/$(BOARDDIR)/lib$(BOARD).a
195 LIBS += cpu/$(CPU)/lib$(CPU).a
……
199 LIBS += lib_$(ARCH)/lib$(ARCH).a
200 LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
201 fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
202 LIBS += net/libnet.a
……
212 LIBS += $(BOARDLIBS)
213
……
從第169行得知,OBJS的第一個(gè)值為“cpu/$(CPU)/start.o”,即“cpu/arm920t/start.o”。
第193~213行指定了LIBS變量就是平臺(tái)/開發(fā)板相關(guān)的各個(gè)目錄、通用目錄下相應(yīng)的
庫(kù),比如:lib_generic/libgeneric.a、board/smdk2410/libsmdk2410.a、cpu/arm920t
/libarm920t.a、lib_arm/libarm.a、fs/cramfs/libcramfs.a fs/fat/libfat.a等。
OBJS、LIBS所代表的.o、.a文件就是U-Boot的構(gòu)成,它們通過如下命令由相應(yīng)的源文件(或相應(yīng)子目錄下的文件)編譯得到。
268 $(OBJS):
269 $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
270
271 $(LIBS):
272 $(MAKE) -C $(dir $(subst $(obj),,$@))
273
274 $(SUBDIRS):
275 $(MAKE) -C $@ all
276
第268、269兩行的規(guī)則表示,對(duì)于OBJS中的每個(gè)成員,都將進(jìn)入cpu/$(CPU)目錄(即cpu/arm920t)編譯它們,F(xiàn)在OBJS為cpu/arm920t/start.o,它將由cpu/arm920t/start.S編譯得到。
第271、272兩行的規(guī)則表示,對(duì)于LIBS中的每個(gè)成員,都將進(jìn)入相應(yīng)的子目錄執(zhí)行“make”命令。這些子目錄中的Makefile,結(jié)構(gòu)相似,它們將Makefle中指定的文件編譯、連接成一個(gè)庫(kù)文件。
當(dāng)所有的OBJS、LIBS所表示的.o和.a文件都生成后,就剩最后的連接了,這對(duì)應(yīng)Makefile中如下幾行:
246 $(obj)u-boot.srec:$(obj)u-boot
247 $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
248
249 $(obj)u-boot.bin:$(obj)u-boot
250 $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
251
……
262 $(obj)u-boot:depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
263 UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
264 cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
265 --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
266 -Map u-boot.map -o u-boot
267
先使用第262~266的規(guī)則連接得到ELF格式的u-boot,最后轉(zhuǎn)換為二進(jìn)制格式u-
boot.bin、S-Record格式u-boot.srec。LDFLAGS確定了連接方式,其中的“-T
board/smdk2410/u-boot.lds -Ttext
0x33F80000”字樣指定了程序的布局、地址。board/smdk2410/u-boot.lds文件如下:
28 SECTIONS
29 {
30 . = 0x00000000;
31
32 . = ALIGN(4);
33 .text :
34 {
35 cpu/arm920t/start.o(.text)
36 *(.text)
37 }
38
39 . = ALIGN(4);
40 .rodata : { *(.rodata) }
41
42 . = ALIGN(4);
43 .data : { *(.data) }
44
45 . = ALIGN(4);
46 .got : { *(.got) }
47
48 . = .;
49 __u_boot_cmd_start = .;
50 .u_boot_cmd : { *(.u_boot_cmd) }
51 __u_boot_cmd_end = .;
52
53 . = ALIGN(4);
54 __bss_start = .;
55 .bss : { *(.bss) }
56 _end = .;
57 }
從第35行可知,cpu/arm920t/start.o被放在程序的最前面,所以U-Boot的入口點(diǎn)在cpu/arm920t/start.S中。
現(xiàn)在來總結(jié)一下U-Boot的編譯流程:
(1)首先編譯cpu/$(CPU)/start.S,對(duì)于不同的CPU,還可能編譯cpu/$(CPU)下的其他文件。
(2)然后,對(duì)于平臺(tái)/開發(fā)板相關(guān)的每個(gè)目錄、每個(gè)通用目錄,都使用它們各自的Makefile生成相應(yīng)的庫(kù)。
(3)將1、2步驟生成的.o、.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代碼段起始地址、board/$(BOARDDIR)/u-boot.lds連接腳本進(jìn)行連接。
(4)第3步得到的是ELF格式的U-Boot,后面Makefile還會(huì)將它轉(zhuǎn)換為二進(jìn)制格式、S-Record格式。
2.4 U-Boot的啟動(dòng)過程源碼分析
首先強(qiáng)調(diào),本書使用的U-Boot從NOR Flash啟動(dòng),下面以開發(fā)板smdk2410的U-Boot為例。
U-Boot屬于兩階段的Bootloader,第一階段的文件為cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S,前者是平臺(tái)相關(guān),后者是開發(fā)板相關(guān)。
U-Boot第一階段代碼分析
它與1.2節(jié)中描述的Bootloader第一階段所完成的功能可以一一對(duì)應(yīng):
(1)硬件設(shè)備初始化。
依次完成如下設(shè)置:將CPU的工作模式設(shè)為管理模式(svc),關(guān)閉WATCHDOG,設(shè)置FCLK、HCLK、PCLK的比例(即設(shè)置CLKDIVN寄存器),關(guān)閉MMU、CACHE。
代碼都在cpu/arm920t/start.S中,注釋也比較完善,讀者有不明白的地方可以參考前面硬件實(shí)驗(yàn)的相關(guān)章節(jié)。
(2)為加載Bootloader的第二階段代碼準(zhǔn)備RAM空間。
所謂準(zhǔn)備RAM空間,就是初始化內(nèi)存芯片,使它可用。對(duì)于S3C2410/S3C2440,
通過在start.S中調(diào)用lowlevel_init函數(shù)來設(shè)置存儲(chǔ)控制器,使得外接的SDRAM可用。代碼在board/smdk2410
/lowlevel_init.S中。
注意:lowlevel_init.S文件是開發(fā)板相關(guān)的,這表示如果外接的設(shè)備不一樣,可以修改lowlevel_init.S文件中的相關(guān)宏。
lowlevel_init函數(shù)并不復(fù)雜,只是要注意這時(shí)的代碼、數(shù)據(jù)都只保存在NOR Flash上,內(nèi)存中還沒有,所以讀取數(shù)據(jù)時(shí)要變換地址。代碼如下:
129 _TEXT_BASE:
130 .wordTEXT_BASE
131
132 .globl lowlevel_init
133 lowlevel_init:
134 /* memory control configuration */
135 /* make r0 relative the current location so that it */
136 /* reads SMRDATA out of FLASH rather than memory ! */
137 ldr r0, =SMRDATA
138 ldrr1, _TEXT_BASE
139 subr0, r0, r1
140 ldrr1, =BWSCON/* Bus Width Status Controller */
141 add r2, r0, #13*4
142 0:
143 ldr r3, [r0], #4
144 str r3, [r1], #4
145 cmp r2, r0
146 bne 0b
147
148 /* everything is fine now */
149 movpc, lr
150
151 .ltorg
152 /* the literal pools origin */
153
154 SMRDATA:/* 13個(gè)寄存器的值 */
155 .word ……
156 .word ……
第137~139行進(jìn)行地址變換,因?yàn)檫@時(shí)候內(nèi)存中還沒有數(shù)據(jù),不能使用連接程序時(shí)確定的地址來讀取數(shù)據(jù):
第137行中SMRDATA 表示這13個(gè)寄存器的值存放的開始地址(連接地址),值為0x33F8xxxx,處于內(nèi)存中。
第138行獲得代碼段的起始地址,它就是第130行中的“TEXT_BASE”,其值在board/smdk2410/config.mk中定義:“TEXT_BASE = 0x33F80000”。
第139行將0x33F8xxxx與0x33F80000相減,這就是13個(gè)寄存器值在NOR Flash上存放的開始地址。
(3)拷貝Bootloader的第二階段代碼到 RAM 空間中。
這里將整個(gè)U-Boot的代碼(包括第一、第二階段)都復(fù)制到SDRAM中,這在cpu/arm920t/start.S中實(shí)現(xiàn):
164 relocate:/* 將U-Boot復(fù)制到RAM中 */
165 adrr0, _start/* r0 = 當(dāng)前代碼的開始地址 */
166 ldrr1, _TEXT_BASE/* r1 = 代碼段的連接地址 */
167 cmp r0, r1 /* 測(cè)試現(xiàn)在是在Flash中還是在RAM中 */
168 beq stack_setup/* 如果已經(jīng)在RAM中(這通常是調(diào)試時(shí),直接下載到RAM中),
* 則不需要復(fù)制
*/
169
170 ldrr2, _armboot_start/* _armboot_start在前面定義,是第一條指令的運(yùn)行地址 */
171 ldrr3, _bss_start/* 在連接腳本u-boot.lds中定義,是代碼段的結(jié)束地址 */
172 subr2, r3, r2/* r2 = 代碼段長(zhǎng)度 */
173 addr2, r0, r2/* r2 = NOR Flash上代碼段的結(jié)束地址 */
174
175 copy_loop:
176 ldmiar0!, {r3-r10}/* 從地址[r0]處獲得數(shù)據(jù) */
177 stmiar1!, {r3-r10}/* 復(fù)制到地址[r1]處 */
178 cmpr0, r2/* 判斷是否復(fù)制完畢 */
179 blecopy_loop/* 沒復(fù)制完,則繼續(xù) */
(4)設(shè)置好棧。
棧的設(shè)置靈活性很大,只要讓sp寄存器指向一段沒有使用的內(nèi)存即可。
182 /* Set up the stack */
183 stack_setup:
184 ldr r0, _TEXT_BASE /* _TEXT_BASE為代碼段的開始地址,值為0x33F80000 */
185 sub r0, r0, #CFG_MALLOC_LEN /* 代碼段下面,留出一段內(nèi)存以實(shí)現(xiàn)malloc */
186 sub r0, r0, #CFG_GBL_DATA_SIZE /* 再留出一段內(nèi)存,存一些全局參數(shù) */
187 #ifdef CONFIG_USE_IRQ
188 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) /* IRQ、FIQ模式的棧 */
189 #endif
190 sub sp, r0, #12 /* 最后,留出12字節(jié)的內(nèi)存給abort異常,
* 往下的內(nèi)存就都是棧了
*/
191
到了這一步,讀者可以知道內(nèi)存的使用情況了,如下圖所示(圖中與上面的劃分稍有不同,這是因?yàn)樵赾pu/arm920t/cpu.c中的cpu_init函數(shù)中才真正為IRQ、FIQ模式劃分了棧): [[Image:]]
圖3 U-Boot內(nèi)存使用情況
(5)跳轉(zhuǎn)到第二階段代碼的C入口點(diǎn)。
在跳轉(zhuǎn)之前,還要清除BSS段(初始值為0、無初始值的全局變量、靜態(tài)變量放在BSS段),代碼如下:
192 clear_bss:
193 ldrr0, _bss_start/* BSS段的開始地址,它的值在連接腳本u-boot.lds中確定 */
194 ldrr1, _bss_end/* BSS段的結(jié)束地址,它的值在連接腳本u-boot.lds中確定 */
195 mov r2, #0x00000000
196
197 clbss_l:strr2, [r0]/* 往BSS段中寫入0值 */
198 addr0, r0, #4
199 cmpr0, r1
200 bleclbss_l
201
現(xiàn)在,C函數(shù)的運(yùn)行環(huán)境已經(jīng)完全準(zhǔn)備好,通過如下命令直接跳轉(zhuǎn)(這之后,程序才在內(nèi)存中執(zhí)行),它將調(diào)用lib_arm/board.c中的start_armboot函數(shù),這是第二階段的入口點(diǎn):
223 ldrpc, _start_armboot
224
225 _start_armboot:.word start_armboot
226
U-Boot第二階段代碼分析
它與15.1.2節(jié)中描述的Bootloader第二階段所完成的功能基本上一致,不過順序有點(diǎn)小差別。另外,U-Boot在啟動(dòng)內(nèi)核之前可以讓用戶決定是否進(jìn)入下載模式,即進(jìn)入U(xiǎn)-Boot的控制界面。
第二階段從lib_arm/board.c中的start_armboot函數(shù)開始,先看從這個(gè)函數(shù)開始的程序流程圖。
圖3 U-Boot第二階段流程圖
移植U-Boot的主要工作在于對(duì)硬件的初始化、驅(qū)動(dòng),所以下面講解時(shí)將重點(diǎn)放在硬件的操作上。
(1)初始化本階段要使用到的硬件設(shè)備。:最主要的是設(shè)置系統(tǒng)時(shí)鐘、初始化串口,只要這兩個(gè)設(shè)置好了,就可以從串口看到打印信息。
board_init函數(shù)設(shè)置MPLL、改變系統(tǒng)時(shí)鐘,它是開發(fā)板相關(guān)的函數(shù),在board/smdk2410/smdk2410.c中實(shí)現(xiàn)。值得注意的是,board_init函數(shù)中還保存了機(jī)器類型ID,這將在調(diào)用內(nèi)核時(shí)傳給內(nèi)核,代碼如下:
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* 值為193 */
串口的初始化函數(shù)主要是serial_init,它設(shè)置UART控制器,是CPU相關(guān)的函數(shù),在cpu/arm920t/s3c24x0/serial.c中實(shí)現(xiàn)。
(2)檢測(cè)系統(tǒng)內(nèi)存映射(memory
map)。對(duì)于特定的開發(fā)板,其內(nèi)存的分布是明確的,所以可以直接設(shè)置。board/smdk2410/smdk2410.c中的dram_init函數(shù)
指定了本開發(fā)板的內(nèi)存起始地址為0x30000000,大小為0x4000000。代碼如下:
int dram_init (void)
{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;/* 即0x300000000 */
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;/* 即0x4000000 */
return 0;
}
這些設(shè)置的參數(shù),將在后面向內(nèi)核傳遞參數(shù)時(shí)用到。
(3)U-Boot命令的格式。
從圖3可以知道,即使是內(nèi)核的啟動(dòng),也是通過U-Boot命令來實(shí)現(xiàn)的。U-Boot中每個(gè)命令都通過U_BOOT_CMD宏來定義,格式如下:
U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")
各項(xiàng)參數(shù)的意義為:
① name:命令的名字,注意,它不是一個(gè)字符串(不要用雙引號(hào)括起來)。
② maxargs:最大的參數(shù)個(gè)數(shù)
③ repeatable:命令是否可重復(fù),可重復(fù)是指運(yùn)行一個(gè)命令后,下次敲回車即可再次運(yùn)行。
④ command:對(duì)應(yīng)的函數(shù)指針,類型為(*cmd)(struct cmd_tbl_s *, int, int, char *[])。
⑤ usage:簡(jiǎn)短的使用說明,這是個(gè)字符串。
⑥ help:較詳細(xì)的使用說明,這是個(gè)字符串。
宏U_BOOT_CMD在include/command.h中定義:
#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}
Struct_Section也是在include/command.h中定義:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
比如對(duì)于bootm命令,它如此定義:
U_BOOT_CMD(
bootm,CFG_MAXARGS,1,do_bootm,
“string1”,
“string2”
);
宏U_BOOT_CMD擴(kuò)展開后就是:
cmd_tbl_t __u_boot_cmd_bootm __attribute__
((unused,section (".u_boot_cmd"))) = {“bootm”, CFG_MAXARGS, 1,
do_bootm, “string1”, “string2”};
對(duì)于每個(gè)使用U_BOOT_CMD宏來定義的命令,其實(shí)都是在".u_boot_cmd"段中定義一個(gè)cmd_tbl_t結(jié)構(gòu)。連接腳本u-boot.lds中有這么一段:
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
程序中就是根據(jù)命令的名字在內(nèi)存段__u_boot_cmd_start~__u_boot_cmd_end找到它的cmd_tbl_t結(jié)構(gòu),然后調(diào)用它的函數(shù)(請(qǐng)參考common/command.c中的find_cmd函數(shù))。
內(nèi)核的復(fù)制和啟動(dòng),可以通過如下命令來完成:bootm從內(nèi)存、ROM、NOR
Flash中啟動(dòng)內(nèi)核,bootp則通過網(wǎng)絡(luò)來啟動(dòng),而nboot從NAND
Flash啟動(dòng)內(nèi)核。它們都是先將內(nèi)核映像從各種媒介中讀出,存放在指定的位置;然后設(shè)置標(biāo)記列表以給內(nèi)核傳遞參數(shù);最后跳到內(nèi)核的入口點(diǎn)去執(zhí)行。具體實(shí)
現(xiàn)的細(xì)節(jié)不再描述,有興趣的讀者可以閱讀common/cmd_boot.c、common/cmd_net.c、common/cmd_nand.c來
了解它們的實(shí)現(xiàn)。
(4)為內(nèi)核設(shè)置啟動(dòng)參數(shù)。
與15.1.2小節(jié)中《Bootloader與內(nèi)核的交互》所描述的一樣,U-Boot也是
通過標(biāo)記列表向內(nèi)核傳遞參數(shù)。并且,15.1.2小節(jié)中內(nèi)存標(biāo)記、命令行標(biāo)記的示例代碼就是取自U-Boot中的setup_memory_tags、
setup_commandline_tag函數(shù),它們都是在lib_arm/armlinux.c中定義。一般而言,設(shè)置這兩個(gè)標(biāo)記就可以了,在配置文
件include/configs/smdk2410.h中增加如下兩個(gè)配置項(xiàng)即可:
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_CMDLINE_TAG 1
對(duì)于ARM架構(gòu)的CPU,都是通過lib_arm/armlinux.c中的
do_bootm_linux函數(shù)來啟動(dòng)內(nèi)核。這個(gè)函數(shù)中,設(shè)置標(biāo)記列表,最后通過“theKernel (0,
bd->bi_arch_number,
bd->bi_boot_params)”調(diào)用內(nèi)核。其中,theKernel指向內(nèi)核存放的地址(對(duì)于ARM架構(gòu)的CPU,通常是
0x30008000),bd->bi_arch_number就是前面board_init函數(shù)設(shè)置的機(jī)器類型ID,而
bd->bi_boot_params就是標(biāo)記列表的開始地址。
2.5 U-Boot的移植
開發(fā)板smdk2410的配置適用于大多數(shù)S3C2410單板,或是只需要極少的修改即可使用。但是目前U-Boot中沒有對(duì)S3C2440的支持,需要我們自己移植。
本書基于的S3C2410、S3C2440兩款開發(fā)板,它們的外接硬件相同:
對(duì)于NOR Flash和NAND
Flash,如圖15.4所示劃分它們的使用區(qū)域。由于NAND
Flash的“位反轉(zhuǎn)”現(xiàn)象比較常見,為保證數(shù)據(jù)的正確,在讀寫數(shù)據(jù)時(shí)需要使用ECC較驗(yàn)。另外,NAND
Flash在使用過程中、運(yùn)輸過程中還有可能出現(xiàn)壞塊。所以本書選擇在NOR Flash中保存U-Boot,在NAND
Flash中保存內(nèi)核和文件系統(tǒng),并在使用U-Boot燒寫內(nèi)核、文件系統(tǒng)時(shí),進(jìn)行壞塊檢查、ECC較驗(yàn)。這樣,即使NAND
Flash出現(xiàn)壞塊導(dǎo)致內(nèi)核或文件系統(tǒng)不能使用,也可以通過NOR Flash中的U-Boot來重新燒寫。 [[Image:]]
圖15.4 開發(fā)板固態(tài)存儲(chǔ)器分區(qū)劃分
smdk2410開發(fā)板已經(jīng)支持NOR
Flash芯片AM29LV800,U-Boot本身也已經(jīng)支持jffs2文件系統(tǒng)映像的燒寫。下面一步一步移植U-Boot(所有的修改都在補(bǔ)丁文件
u-boot-1.1.6_100ask24x0.patch里,讀者可以直接打補(bǔ)丁),增加如下新功能:
1. 同時(shí)支持S3C2410和S3C2440
我們將在開發(fā)板smdk2410的基礎(chǔ)上進(jìn)行移植。
(1)新建一個(gè)開發(fā)板的相應(yīng)目錄和文件。
為了不破壞原來的代碼,在board目錄下將smdk2410復(fù)制為100ask24x0目錄,并將board/100ask24x0/smdk2410.c改名為100ask24x0.c。
根據(jù)前面描述的配置過程可知,還要在include/configs目錄下建立一個(gè)配置文件100ask24x0.h,可以將include/configs/smdk2410.h直接復(fù)制為100ask24x0.h。
還要修改兩個(gè)Makefile,首先在頂層Makefile中增加如下兩行:
100ask24x0_config:unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
然后在board/100ask24x0/Makefile中,如下修改(因?yàn)榍懊鎸mdk2410.c文件改名為100ask24x0.c了):
COBJS:= smdk2410.o flash.o
改為:
COBJS:= 100ask24x0.o flash.o
(2)修改SDRAM的配置。
SDRAM的初始化在U-Boot的第一階段完成,就是在board/100ask24x0/lowlevel_init.S文件中設(shè)置存儲(chǔ)控制器。
檢查一下BANK6的設(shè)置:位寬為32──宏B6_BWSCON剛好為DW32(表示32位),無需改變;另外還要根據(jù)HCLK設(shè)置SDRAM的刷新參數(shù),主要是REFCNT寄存器。
本書所用開發(fā)板的HCLK都設(shè)為100MHz,需要根據(jù)SDRAM芯片的具體參數(shù)重新計(jì)算REFCNT寄存器的值(請(qǐng)參考第6章)。代碼修改如下:
126 #define REFCNT 1113/* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
改為
126 #define REFCNT 0x4f4/* period=7.8125us, HCLK=100Mhz, (2048+1-7.8125*100) */
對(duì)于其他BANK,比如網(wǎng)卡芯片CS8900所在的BANK2,原來的設(shè)置剛好匹配,無需更改;而對(duì)于BANK1、2、4、5、7,在U-Boot中并沒有使用到它們外接的設(shè)備,也不需要理會(huì)。
(3)增加對(duì)S3C2440的支持。
S3C2440是S3C2410的改進(jìn)版,它們的操作基本相似。不過在系統(tǒng)時(shí)鐘的設(shè)置、
NAND
Flash控制器的操作等方面,有一些小差別。它們的MPLL、UPLL計(jì)算公式不一樣,F(xiàn)CLK、HCLK和PCLK的分頻化設(shè)置也不一樣,這在下面的
代碼中可以看到。NAND Flash控制器的差別在增加對(duì)NAND Flash的支持時(shí)講述。
本章的目標(biāo)是令同一個(gè)U-Boot二進(jìn)制代碼既可以在S3C2410上運(yùn)行,也可以在
S3C2440上運(yùn)行。首先需要在代碼中自動(dòng)識(shí)別是S3C2410還是S3C2440,這可以通過讀取GSTATUS1寄存器的值來分
辨:0x32410000表示S3C2410,0x32410002表示S3C2410A,0x32440000表示
S3C2440,0x32440001表示S3C2440A。S3C2410和S3C2410A、S3C2440和S3C2440A,對(duì)本書來說沒有區(qū)
別。
對(duì)于S3C2410開發(fā)板,將FCLK設(shè)為200MHz,分頻比為
FCLK:HCLK:PCLK=1:2:4;對(duì)于S3C2440開發(fā)板,將FCLK設(shè)為400MHz,分頻比為
FCLK:HCLK:PCLK=1:4:8。還將UPLL設(shè)為48MHz,即UCLK為48MHz,以在內(nèi)核中支持USB控制器。
首先修改board/100ask24x0/100ask24x0.c中的board_init函數(shù),下面是修改后的代碼:
33 /* S3C2440: MPLL = (2*m * Fin) / (p * 2^s), UPLL = (m * Fin) / (p * 2^s)
34 * m = M (the value for divider M)+ 8, p = P (the value for divider P) + 2
35 */
36 #define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
37 #define S3C2440_UPLL_48MHZ ((0x38<<12)|(0x02<<4)|(0x02))
38 #define S3C2440_CLKDIV 0x05 /* FCLK:HCLK:PCLK = 1:4:8, UCLK = UPLL */
39
40 /* S3C2410: Mpll,Upll = (m * Fin) / (p * 2^s)
41 * m = M (the value for divider M)+ 8, p = P (the value for divider P) + 2
42 */
43 #define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))
44 #define S3C2410_UPLL_48MHZ ((0x28<<12)|(0x01<<4)|(0x02))
45 #define S3C2410_CLKDIV 0x03 /* FCLK:HCLK:PCLK = 1:2:4 */
46
上面幾行針對(duì)S3C2410、S3C2440分別定義了MPLL、UPLL寄存器的值。開發(fā)
板輸入時(shí)鐘為12MHz(這在include/configs/100ask24x0.h中的宏CONFIG_SYS_CLK_FREQ中定義),讀者可
以根據(jù)代碼中的計(jì)算公式針對(duì)自己的開發(fā)板修改系統(tǒng)時(shí)鐘。
下面是針對(duì)S3C2410、S3C2440,分別使用不同的宏設(shè)置系統(tǒng)時(shí)鐘:
58 int board_init (void)
59 {
60 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
61 S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
62
63 /* 設(shè)置GPIO */
64 gpio->GPACON = 0x007FFFFF;
65 gpio->GPBCON = 0x00044555;
66 gpio->GPBUP = 0x000007FF;
67 gpio->GPCCON = 0xAAAAAAAA;
68 gpio->GPCUP = 0x0000FFFF;
69 gpio->GPDCON = 0xAAAAAAAA;
70 gpio->GPDUP = 0x0000FFFF;
71 gpio->GPECON = 0xAAAAAAAA;
72 gpio->GPEUP = 0x0000FFFF;
73 gpio->GPFCON = 0x000055AA;
74 gpio->GPFUP = 0x000000FF;
75 gpio->GPGCON = 0xFF95FFBA;
76 gpio->GPGUP = 0x0000FFFF;
77 gpio->GPHCON = 0x002AFAAA;
78 gpio->GPHUP = 0x000007FF;
79
80 /* 同時(shí)支持S3C2410和S3C2440, www.100ask.net */
81 if ((gpio->GSTATUS1 == 0x32410000) || (gpio->GSTATUS1 == 0x32410002))
82 {
83 /* FCLK:HCLK:PCLK = 1:2:4 */
84 clk_power->CLKDIVN = S3C2410_CLKDIV;
85
86 /* 修改為異步總線模式 */
87 __asm__( "mrc p15, 0, r1, c1, c0, 0\n" /* read ctrl register */
88 "orr r1, r1, #0xc0000000\n" /* Asynchronous */
89 "mcr p15, 0, r1, c1, c0, 0\n" /* write ctrl register */
90 :::"r1"
91 );
92
93 /* 設(shè)置PLL鎖定時(shí)間 */
94 clk_power->LOCKTIME = 0xFFFFFF;
95
96 /* 配置MPLL */
97 clk_power->MPLLCON = S3C2410_MPLL_200MHZ;
98
99 /* 配置MPLL后,要延時(shí)一段時(shí)間再配置UPLL */
100 delay (4000);
101
102 /* 配置UPLL */
103 clk_power->UPLLCON = S3C2410_UPLL_48MHZ;
104
105 /* 再延時(shí)一會(huì) */
106 delay (8000);
107
108 /* 機(jī)器類型ID,這在調(diào)用Linux內(nèi)核時(shí)用到 */
109 gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
110 }
111 else
112 {
113 /* FCLK:HCLK:PCLK = 1:4:8 */
114 clk_power->CLKDIVN = S3C2440_CLKDIV;
115
116 /* 修改為異步總線模式 */
117 __asm__( "mrc p15, 0, r1, c1, c0, 0\n" /* read ctrl register */
118 "orr r1, r1, #0xc0000000\n" /* Asynchronous */
119 "mcr p15, 0, r1, c1, c0, 0\n" /* write ctrl register */
120 :::"r1"
121 );
122
123 /* 設(shè)置PLL鎖定時(shí)間 */
124 clk_power->LOCKTIME = 0xFFFFFF;
125
126 /* 配置MPLL */
127 clk_power->MPLLCON = S3C2440_MPLL_400MHZ;
128
129 /* 配置MPLL后,要延時(shí)一段時(shí)間再配置UPLL */
130 delay (4000);
131
132 /* 配置UPLL */
133 clk_power->UPLLCON = S3C2440_UPLL_48MHZ;
134
135 /* 再延時(shí)一會(huì) */
136 delay (8000);
137
138 /* 機(jī)器類型ID,這在調(diào)用Linux內(nèi)核時(shí)用到,這個(gè)值要與內(nèi)核相對(duì)應(yīng) */
139 gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
140 }
141
142 /* 啟動(dòng)內(nèi)核時(shí),參數(shù)存放位置。這個(gè)值在構(gòu)造標(biāo)記列表時(shí)用到 */
143 gd->bd->bi_boot_params = 0x30000100;
144
145 icache_enable();
146 dcache_enable();
147
148 return 0;
149 }
150
最后一步:獲取系統(tǒng)時(shí)鐘的函數(shù)需要針對(duì)S3C2410、S3C2440的不同進(jìn)行修改。
在后面設(shè)置串口波特率時(shí)需要獲得系統(tǒng)時(shí)鐘,就是在U-Boot的第二階
段,lib_arm/board.c中start_armboot函數(shù)調(diào)用serial_init函數(shù)初始化串口時(shí),會(huì)調(diào)用get_PCLK函數(shù)。它在
cpu/arm920t/s3c24x0/speed.c中定義,與它相關(guān)的還有g(shù)et_HCLK、get_PLLCLK等函數(shù)。
前面的board_init函數(shù)在識(shí)別出S3C2410或S3C2440后,設(shè)置了機(jī)器類型
ID:gd->bd->bi_arch_number,后面的函數(shù)可以通過它來分辨是S3C2410還是S3C2440。首先要在程序的開頭
增加如下一行,這樣才可以使用gd變量:
DECLARE_GLOBAL_DATA_PTR;
S3C2410和S3C2440的MPLL、UPLL計(jì)算公式不一樣,所以get_PLLCLK函數(shù)也需要修改:
56 static ulong get_PLLCLK(int pllreg)
57 {
58 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
59 ulong r, m, p, s;
60
61 if (pllreg == MPLL)
62 r = clk_power->MPLLCON;
63 else if (pllreg == UPLL)
64 r = clk_power->UPLLCON;
65 else
66 hang();
67
68 m = ((r & 0xFF000) >> 12) + 8;
69 p = ((r & 0x003F0) >> 4) + 2;
70 s = r & 0x3;
71
72 /* 同時(shí)支持S3C2410和S3C2440, by www.100ask.net */
73 if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
74 return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
75 else
76 return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); /* S3C2440 */
77 }
78
由于分頻系數(shù)的設(shè)置方法也不一樣,get_HCLK、get_PCLK也需要修改。對(duì)于S3C2410,沿用原來的計(jì)算方法,else分支中是S3C2440的代碼:
85 /* for s3c2440 */
86 #define S3C2440_CLKDIVN_PDIVN (1<<0)
87 #define S3C2440_CLKDIVN_HDIVN_MASK (3<<1)
88 #define S3C2440_CLKDIVN_HDIVN_1 (0<<1)
89 #define S3C2440_CLKDIVN_HDIVN_2 (1<<1)
90 #define S3C2440_CLKDIVN_HDIVN_4_8 (2<<1)
91 #define S3C2440_CLKDIVN_HDIVN_3_6 (3<<1)
92 #define S3C2440_CLKDIVN_UCLK (1<<3)
93
94 #define S3C2440_CAMDIVN_CAMCLK_MASK (0xf<<0)
95 #define S3C2440_CAMDIVN_CAMCLK_SEL (1<<4)
96 #define S3C2440_CAMDIVN_HCLK3_HALF (1<<8)
97 #define S3C2440_CAMDIVN_HCLK4_HALF (1<<9)
98 #define S3C2440_CAMDIVN_DVSEN (1<<12)
99
100 /* return HCLK frequency */
101 ulong get_HCLK(void)
102 {
103 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
104 unsigned long clkdiv;
105 unsigned long camdiv;
106 int hdiv = 1;
107
108 /* 同時(shí)支持S3C2410和S3C2440, by www.100ask.net */
109 if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
110 return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
111 else
112 {
113 clkdiv = clk_power->CLKDIVN;
114 camdiv = clk_power->CAMDIVN;
115
116 /* 計(jì)算分頻比 */
117
118 switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
119 case S3C2440_CLKDIVN_HDIVN_1:
120 hdiv = 1;
121 break;
122
123 case S3C2440_CLKDIVN_HDIVN_2:
124 hdiv = 2;
125 break;
126
127 case S3C2440_CLKDIVN_HDIVN_4_8:
128 hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
129 break;
130
131 case S3C2440_CLKDIVN_HDIVN_3_6:
132 hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
133 break;
134 }
135
136 return get_FCLK() / hdiv;
137 }
138 }
139
140 /* return PCLK frequency */
141 ulong get_PCLK(void)
142 {
143 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
144 unsigned long clkdiv;
145 unsigned long camdiv;
146 int hdiv = 1;
147
148 /* 同時(shí)支持S3C2410和S3C2440, by www.100ask.net */
149 if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
150 return((clk_power->CLKDIVN & 0x1) ? get_HCLK()/2 : get_HCLK());
151 else
152 {
153 clkdiv = clk_power->CLKDIVN;
154 camdiv = clk_power->CAMDIVN;
155
156 /* 計(jì)算分頻比 */
157
158 switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
159 case S3C2440_CLKDIVN_HDIVN_1:
160 hdiv = 1;
161 break;
162
163 case S3C2440_CLKDIVN_HDIVN_2:
164 hdiv = 2;
165 break;
166
167 case S3C2440_CLKDIVN_HDIVN_4_8:
168 hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
169 break;
170
171 case S3C2440_CLKDIVN_HDIVN_3_6:
172 hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
173 break;
174 }
175
176 return get_FCLK() / hdiv / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1);
177 }
178 }
179
現(xiàn)在重新執(zhí)行“make 100ask24x0_config”和“make
all”生成的u-boot.bin文件既可以運(yùn)行于S3C2410開發(fā)板,也可以運(yùn)行于S3C2440開發(fā)板。將它燒入NOR
Flash后啟動(dòng),就可以在串口工具(設(shè)置為115200,8N1)中看到提示信息,可以輸入各種命令操作U-Boot了。
(4)選擇NOR Flash的型號(hào)。
但是,現(xiàn)在還無法通過U-Boot命令燒寫NOR Flash。本書所用開發(fā)板中的NOR Flash型號(hào)為AM29LV800,而配置文件include/configs/100ask24x0.h中的默認(rèn)型號(hào)為AM29LV400。修改如下:
#define CONFIG_AMD_LV4001/* uncomment this if you have a LV400 flash */
#if 0
#define CONFIG_AMD_LV8001/* uncomment this if you have a LV800 flash */
#endif
改為:
#if 0
#define CONFIG_AMD_LV4001/* uncomment this if you have a LV400 flash */
#endif
#define CONFIG_AMD_LV8001/* uncomment this if you have a LV800 flash */
本例中NOR
Flash的操作函數(shù)在board/100ask24x0/flash.c中實(shí)現(xiàn),它支持AM29LV400y和AM29LV800。對(duì)于其他型號(hào)的
NOR
Flash,如果符合CFI接口標(biāo)準(zhǔn),則可以在使用drivers/cfi_flash.c中的接口函數(shù);否則,只好自己編寫了。如果要使用
cfi_flash.c,如下修改兩個(gè)文件:
在include/configs/100ask24x0.h中增加以下一行:
#define CFG_FLASH_CFI_DRIVER 1
在board/100ask24x0/Makefile中去掉flash.o:
COBJS:= 100ask24x0.o flash.o
改為:
COBJS:= 100ask24x0.o
修改好對(duì)NOR Flash的支持后,重新編譯U-Boot:make clean、make all。運(yùn)行后可以在串口中看到如下字樣:
Flash: 1 MB
現(xiàn)在可以使用loadb、loady等命令通過串口下載文件,然后使用erase、cp命令分別擦除、燒寫NOR Flash了,它們的效率比JTAG快上好幾倍。
2. 支持串口xmodem協(xié)議
上面的loadb命令需要配合Linux下的kermit工具來使用,loady命令通過串
口ymodem協(xié)議來傳輸文件。Windows下的超級(jí)終端雖然支持ymodem,但是它的使用界面實(shí)在不友好。而本書推薦使用的Windows工具
SecureCRT只支持xmodem和zmodem。為了方便在Windows下開發(fā),現(xiàn)在修改代碼增加對(duì)xmodem的支持,即增加一個(gè)命令
loadx。
依照loady的實(shí)現(xiàn)來編寫代碼,首先使用U_BOOT_CMD宏來增加loadx命令:
/* 支持xmodem, www.100ask.net */
U_BOOT_CMD(
loadx, 3, 0,do_load_serial_bin,
"loadx - load binary file over serial line (xmodem mode)\n",
"[ off ] [ baud ]\n"
" - load binary file over serial line"
" with offset 'off' and baudrate 'baud'\n"
);
其次,在do_load_serial_bin函數(shù)中增加對(duì)loadx命令的處理分支。也是依照loady來實(shí)現(xiàn):
481 /* 支持xmodem, www.100ask.net */
482 if (strcmp(argv[0],"loadx")==0) {
483 printf ("## Ready for binary (xmodem) download "
484 "to 0x%08lX at %d bps...\n",
485 offset,
486 load_baudrate);
487
488 addr = load_serial_xmodem (offset);
489
490 } else if (strcmp(argv[0],"loady")==0) {
491 printf ("## Ready for binary (ymodem) download "
492 "to 0x%08lX at %d bps...\n",
……
第481~490行就是為loadx命令增加的代碼。
在第288行調(diào)用load_serial_xmodem函數(shù),它是依照load_serial_ymodem實(shí)現(xiàn)的一個(gè)新函數(shù):
36 #if (CONFIG_COMMANDS & CFG_CMD_LOADB)
37 /* 支持xmodem, www.100ask.net */
38 static ulong load_serial_xmodem (ulong offset);
39 static ulong load_serial_ymodem (ulong offset);
40 #endif
……
995 /* 支持xmodem, www.100ask.net */
996 static ulong load_serial_xmodem (ulong offset)
997 {
……
1003 char xmodemBuf[1024];/* 原來是ymodemBuf,這只是為了與函數(shù)名稱一致 */
……
1008 info.mode = xyzModem_xmodem;/* 原來是xyzModem_ymodem,對(duì)應(yīng)ymodem */
……
首先在文件開頭增加load_serial_xmodem函數(shù)的聲明,然后復(fù)制load_serial_ymodem函數(shù)為load_serial_xmodem,稍作修改:
① 將局部數(shù)組ymodemBuf改名為xmodemBuf,并在后面使用到的地方統(tǒng)一修改。這只是為了與函數(shù)名稱一致。
② info.mode的值從xyzModem_ymodem改為xyzModem_xmodem。
重新編譯、燒寫u-boot.bin后,就可以使用loadx命令下載文件了。
3. 支持網(wǎng)卡芯片CS8900
使用串口來傳輸文件的速率太低,現(xiàn)在增加對(duì)網(wǎng)卡芯片CS8900的支持。
本書使用開發(fā)板的網(wǎng)卡芯片CS8900的連接方式與smdk2410完全一樣,所以現(xiàn)在的
U-Boot中已經(jīng)支持CS8900了,它的驅(qū)動(dòng)程序?yàn)閐rivers/cs8900.c。只要在U-Boot控制界面中稍加配置就可以使用網(wǎng)絡(luò)功能。使
用網(wǎng)絡(luò)之前,先設(shè)置開發(fā)板IP地址、MAC地址,服務(wù)器IP地址,比如可以在U-Boot中執(zhí)行以下命令:
setenv ipaddr 192.168.1.17
setenv ethaddr 08:00:3e:26:0a:5b
setenv serverip 192.168.1.11
saveenv
然后就可以使用tftp或nfs命令下載文件了,注意:服務(wù)器上要開啟tftp或nfs服務(wù)。比如可以使用如下命令將u-boot.bin文件下載到內(nèi)存0x30000000中:
tftp 0x30000000 u-boot.bin
或
nfs 0x30000000 192.168.1.57:/work/nfs_root/u-boot.bin
可以修改配置文件,讓網(wǎng)卡的各個(gè)默認(rèn)值就是上面設(shè)置的值。在此之前,先了解網(wǎng)卡的相關(guān)文件,這有助于移植代碼以支持其他連接方式的CS8900。
首先,CS8900接在S3C2410、S3C2440的BANK3,位寬為16,使用WAIT、nBE信號(hào)。在設(shè)置存儲(chǔ)控制器時(shí)要設(shè)置好BANK3。代碼在board/100ask24x0/lowlevel_init.S中:
#define B3_BWSCON (DW16 + WAIT + UBLB)
……
/* 時(shí)序參數(shù) */
#define B3_Tacs 0x0/* 0clk */
#define B3_Tcos 0x3/* 4clk */
#define B3_Tacc 0x7/* 14clk */
#define B3_Tcoh 0x1/* 1clk */
#define B3_Tah 0x0/* 0clk */
#define B3_Tacp 0x3 /* 6clk */
#define B3_PMC 0x0/* normal */
接下來,還要確定CS8900的基地址。這在配置文件include/configs/100ask24x0.h中定義:
#define CONFIG_DRIVER_CS89001/* 使用CS8900 */
#define CS8900_BASE0x19000300/* 基地址 */
#define CS8900_BUS161 /* 位寬為16 */
從第6章可以知道網(wǎng)卡CS8900的訪問基址為0x19000000,之所以再偏移0x300是由它的特性決定的。
最后,還是在配置文件include/configs/100ask24x0.h中定義CS8900的各個(gè)默認(rèn)地址:
#define CONFIG_ETHADDR08:00:3e:26:0a:5b
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR192.168.1.17
#define CONFIG_SERVERIP192.168.1.11
額外的,如果要增加ping命令,還可以在配置文件include/configs/100ask24x0.h的宏CONFIG_COMMANDS中增加CFG_CMD_PING,如下:
#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \
CFG_CMD_CACHE | \
CFG_CMD_PING | \
……
4. 支持NAND Flash
U-Boot 1.1.6中對(duì)NAND
Flash的支持有新舊兩套代碼,新代碼在drivers/nand目錄下,舊代碼在drivers/nand_legacy目錄下。文檔doc
/README.nand對(duì)這兩套代碼有所說明:使用舊代碼需要定義更多的宏,而新代碼移植自Linux內(nèi)核2.6.12,它更加智能,可以自動(dòng)識(shí)別更多
型號(hào)的NAND
Flash。目前之所以還保留舊的代碼,是因?yàn)閮蓚(gè)目標(biāo)板NETTA、NETTA_ISDN使用JFFS文件系統(tǒng),它們還依賴于舊代碼。當(dāng)相關(guān)功能移植到
新代碼之后,舊的代碼將從U-Boot中去除。
要讓U-Boot支持NAND Flash,首先在配置文件include/configs/100ask24x0.h的宏CONFIG_COMMANDS中增加CFG_CMD_NAND,如下:
#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \
CFG_CMD_CACHE | \
CFG_CMD_PING | \
CFG_CMD_NAND| \
……
然后選擇使用哪套代碼:在配置文件中定義宏CFG_NAND_LEGACY則使用舊代碼,否則使用新代碼。
使用舊代碼時(shí),需要實(shí)現(xiàn)drivers/nand_legacy/nand_legacy.c中使用到的各種宏,比如:
#define NAND_WAIT_READY(nand)/* 等待Nand Flash的狀態(tài)為“就緒”,代碼依賴于具體的開發(fā)板 */
#define WRITE_NAND_COMMAND(d, adr)/* 寫NAND Flash命令,代碼依賴于具體的開發(fā)板 */
本書使用新代碼,下面講述移植過程。
代碼的移植沒有現(xiàn)成的文檔,可以在配置文件include/configs/100ask24x0.h的宏CONFIG_COMMANDS中增加CFG_CMD_NAND后,就編譯代碼,然后一個(gè)一個(gè)地解決出現(xiàn)的錯(cuò)誤。編譯結(jié)果中出現(xiàn)的錯(cuò)誤和警告如下:
nand.h:412: error: `NAND_MAX_CHIPS' undeclared here (not in a function)
nand.c:35: error: `CFG_MAX_NAND_DEVICE' undeclared here (not in a function)
nand.c:38: error: `CFG_NAND_BASE' undeclared here (not in a function)
nand.c:35: error: storage size of `nand_info' isn't known
nand.c:37: error: storage size of `nand_chip' isn't known
nand.c:38: error: storage size of `base_address' isn't known
nand.c:37: warning: 'nand_chip' defined but not used
nand.c:38: warning: 'base_address' defined but not used
在配置文件include/configs/100ask24x0.h中增加如下3個(gè)宏就可
以解決上述錯(cuò)誤。在Flash的驅(qū)動(dòng)程序中,設(shè)備是邏輯上的概念,表示一組相同結(jié)構(gòu)、訪問函數(shù)相同的Flash芯片。在本書所用開發(fā)板中,只有一個(gè)
NAND Flash芯片,所以設(shè)備數(shù)為1,芯片數(shù)也為1。
#define CFG_NAND_BASE 0/* 無實(shí)際意義:基地址,這在board_nand_init中重新指定 */
#define CFG_MAX_NAND_DEVICE 1/* NAND Flash“設(shè)備”的數(shù)目為1 */
#define NAND_MAX_CHIPS 1/* 每個(gè)NAND Flash“設(shè)備”由1個(gè)NAND Flash“芯片”組成 */
修改配置文件后再次編譯,現(xiàn)在只有一個(gè)錯(cuò)誤了,“board_nand_init函數(shù)未定義”:
nand.c:50: undefined reference to `board_nand_init'
調(diào)用board_nand_init函數(shù)的過程為:NAND
Flash的初始化入口函數(shù)是nand_init,它在lib_arm/board.c的start_armboot函數(shù)中被調(diào)用;nand_init函
數(shù)在drivers/nand/nand.c中實(shí)現(xiàn),它調(diào)用相同文件中的nand_init_chip函數(shù);nand_init_chip函數(shù)首先調(diào)用
board_nand_init函數(shù)來初始化NAND Flash設(shè)備,最后才是統(tǒng)一的識(shí)別過程。
從board_nand_init函數(shù)的名稱就可以知道它是平臺(tái)/開發(fā)板相關(guān)的函數(shù),需要自
己編寫。本書在cpu/arm920t/s3c24x0目錄下新建一個(gè)文件nand_flash.c,在里面針對(duì)S3C2410、S3C2440實(shí)現(xiàn)了統(tǒng)
一的board_nand_init函數(shù)。
在編寫board_nand_init函數(shù)的之前,需要針對(duì)S3C2410、S3C2440 NAND Flash控制器的不同定義一些數(shù)據(jù)結(jié)構(gòu)和函數(shù):
(1)在include/s3c24x0.h文件中增加S3C2440_NAND數(shù)據(jù)結(jié)構(gòu)。
/* NAND FLASH (see S3C2440 manual chapter 6, www.100ask.net) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECCD0;
S3C24X0_REG32 NFMECCD1;
S3C24X0_REG32 NFSECCD;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSBLK;
S3C24X0_REG32 NFEBLK;
} /*__attribute__((__packed__))*/ S3C2440_NAND;
(2)在include/s3c2410.h文件中仿照S3C2410_GetBase_NAND函數(shù)定義S3C2440_GetBase_NAND函數(shù)。
/* for s3c2440, www.100ask.net */
static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)
{
return (S3C2440_NAND * const)S3C2410_NAND_BASE;
}
既然新的NAND
Flash代碼是從Linux內(nèi)核2.6.12中移植來的,那么cpu/arm920t/s3c24x0/nand_flash.c文件也可以仿照內(nèi)核
中,對(duì)S3C2410、S3C2440的NAND
Flash進(jìn)行初始化的drivers/mtd/nand/s3c2410.c文件來編寫。為了方便閱讀,先把cpu/arm920t/s3c24x0
/nand_flash.c文件的代碼全部列出來,再講解:
01 /*
02 * s3c2410/s3c2440的NAND Flash控制器接口, www.100ask.net
03 * 修改自Linux內(nèi)核2.6.13文件drivers/mtd/nand/s3c2410.c
04 */
05
06 #include <common.h>
07
08 #if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
09 #include <s3c2410.h>
10 #include <nand.h>
11
12 DECLARE_GLOBAL_DATA_PTR;
13
14 #define S3C2410_NFSTAT_READY (1<<0)
15 #define S3C2410_NFCONF_nFCE (1<<11)
16
17 #define S3C2440_NFSTAT_READY (1<<0)
18 #define S3C2440_NFCONT_nFCE (1<<1)
19
20
21 /* S3C2410:NAND Flash的片選函數(shù) */
22 static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
23 {
24 S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
25
26 if (chip == -1) {
27 s3c2410nand->NFCONF |= S3C2410_NFCONF_nFCE;/* 禁止片選信號(hào) */
28 } else {
29 s3c2410nand->NFCONF &= ~S3C2410_NFCONF_nFCE;/* 使能片選信號(hào) */
30 }
31 }
32
33 /* S3C2410:命令和控制函數(shù)
34 *
35 * 注意,這個(gè)函數(shù)僅僅根據(jù)各種命令來修改“寫地址”IO_ADDR_W 的值(這稱為tglx方法),
36 * 這種方法使得平臺(tái)/開發(fā)板相關(guān)的代碼很簡(jiǎn)單。
37 * 真正發(fā)出命令是在上一層NAND Flash的統(tǒng)一的驅(qū)動(dòng)中實(shí)現(xiàn),
38 * 它首先調(diào)用這個(gè)函數(shù)修改“寫地址”,然后才分別發(fā)出控制、地址、數(shù)據(jù)序列。
39 */
40 static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
41 {
42 S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
43 struct nand_chip *chip = mtd->priv;
44
45 switch (cmd) {
46 case NAND_CTL_SETNCE:
47 case NAND_CTL_CLRNCE:
48 printf("%s: called for NCE\n", __FUNCTION__);
49 break;
50
51 case NAND_CTL_SETCLE:
52 chip->IO_ADDR_W = (void *)&s3c2410nand->NFCMD;
53 break;
54
55 case NAND_CTL_SETALE:
56 chip->IO_ADDR_W = (void *)&s3c2410nand->NFADDR;
57 break;
58
59 /* NAND_CTL_CLRCLE: */
60 /* NAND_CTL_CLRALE: */
61 default:
62 chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
63 break;
64 }
65 }
66
67 /* S3C2410:查詢NAND Flash狀態(tài)
68 *
69 * 返回值:0 – 忙, 1 – 就緒
70 */
71 static int s3c2410_nand_devready(struct mtd_info *mtd)
72 {
73 S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
74
75 return (s3c2410nand->NFSTAT & S3C2410_NFSTAT_READY);
76 }
77
78
79 /* S3C2440:NAND Flash的片選函數(shù) */
80 static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chip)
81 {
82 S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
83
84 if (chip == -1) {
85 s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;/* 禁止片選信號(hào) */
86 } else {
87 s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;/* 使能片選信號(hào) */
88 }
89 }
90
91 /* S3C2440:命令和控制函數(shù),與s3c2410_nand_hwcontrol函數(shù)類似 */
92 static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
93 {
94 S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
95 struct nand_chip *chip = mtd->priv;
96
97 switch (cmd) {
98 case NAND_CTL_SETNCE:
99 case NAND_CTL_CLRNCE:
100 printf("%s: called for NCE\n", __FUNCTION__);
101 break;
102
103 case NAND_CTL_SETCLE:
104 chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD;
105 break;
106
107 case NAND_CTL_SETALE:
108 chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR;
109 break;
110
111 /* NAND_CTL_CLRCLE: */
112 /* NAND_CTL_CLRALE: */
113 default:
114 chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
115 break;
116 }
117 }
118
119 /* S3C2440:查詢NAND Flash狀態(tài)
120 *
121 * 返回值:0 – 忙, 1 – 就緒
122 */
123 static int s3c2440_nand_devready(struct mtd_info *mtd)
124 {
125 S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
126
127 return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
128 }
129
130 /*
131 * Nand flash硬件初始化:
132 * 設(shè)置NAND Flash的時(shí)序, 使能NAND Flash控制器
133 */
134 static void s3c24x0_nand_inithw(void)
135 {
136 S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
137 S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
138
139 #define TACLS 0
140 #define TWRPH0 4
141 #define TWRPH1 2
142
143 if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
144 {
145 /* 使能NAND Flash控制器,初始化ECC,使能片選信號(hào),設(shè)置時(shí)序 */
146 s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
147 }
148 else
149 {
150 /* 設(shè)置時(shí)序 */
151 s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
152 /* 初始化ECC,使能NAND Flash控制器,使能片選信號(hào) */
153 s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);
154 }
155 }
156
157 /*
158 * 被drivers/nand/nand.c調(diào)用, 初始化NAND Flash硬件,初始化訪問接口函數(shù)
159 */
160 void board_nand_init(struct nand_chip *chip)
161 {
162 S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
163 S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
164
165 s3c24x0_nand_inithw();/* Nand flash硬件初始化 */
166
167 if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410) {
168 chip->IO_ADDR_R = (void *)&s3c2410nand->NFDATA;
169 chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
170 chip->hwcontrol = s3c2410_nand_hwcontrol;
171 chip->dev_ready = s3c2410_nand_devready;
172 chip->select_chip = s3c2410_nand_select_chip;
173 chip->options = 0;/* 設(shè)置位寬等,位寬為8 */
174 } else {
175 chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;
176 chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
177 chip->hwcontrol = s3c2440_nand_hwcontrol;
178 chip->dev_ready = s3c2440_nand_devready;
179 chip->select_chip = s3c2440_nand_select_chip;
180 chip->options = 0;/* 設(shè)置位寬等,位寬為8 */
181 }
182
183 chip->eccmode = NAND_ECC_SOFT;/* ECC較驗(yàn)方式:軟件ECC */
184 }
185
186 #endif
文件中分別針對(duì)S3C2410、S3C2440實(shí)現(xiàn)了NAND
Flash最底層訪問函數(shù),并進(jìn)行了一些硬件的設(shè)置(比如時(shí)序、使能NAND Flash控制器等)。新的代碼對(duì)NAND
Flash的封裝做得很好,只要向上提供底層初始化函數(shù)board_nand_init來設(shè)置好平臺(tái)/開發(fā)板相關(guān)的初始化、提供底層接口即可。
最后,只要將新建的nand_flash.c文件編入U(xiǎn)-Boot中就可以擦除、讀寫NAND Flash了。如下修改cpu/arm920t/s3c24x0/Makefile文件即可:
COBJS = i2c.o interrupts.o serial.o speed.o \
usb_ohci.o
改為:
COBJS = i2c.o interrupts.o serial.o speed.o \
usb_ohci.o nand_flash.o
現(xiàn)在,可以使用新編譯的u-boot.bin燒寫內(nèi)核映像到NAND Flash去了,請(qǐng)參考15.2.6。
5. 支持燒寫yaffs文件系統(tǒng)映像
在實(shí)際生產(chǎn)中,可以通過燒片器等手段將內(nèi)核、文件系統(tǒng)映像燒入固態(tài)存儲(chǔ)設(shè)備中,Bootloader不需要具備燒寫功能。但為了方便開發(fā),通常在Bootloader中增加燒寫內(nèi)核、文件系統(tǒng)映像文件的功能。
增加了NAND Flash功能的U-Boot 1.1.6已經(jīng)可以通過“nand
write ……”、“nand write.jffs2 ……”等命令來燒寫內(nèi)核,cramfs、jffs2文件系統(tǒng)映像文件。但是在NAND
Flash上,yaffs文件系統(tǒng)的性能更佳,下面增加“nand write.yaffs ……”命令以燒寫yaffs文件系統(tǒng)映像文件。
“nand write.yaffs ……”字樣的命令中,“nand”是具體命令,“write.yaffs ……”是參數(shù)。nand命令在common/cmd_nand.c中實(shí)現(xiàn):
U_BOOT_CMD(nand, 5, 1, do_nand,
"nand - NAND sub-system\n",
"info - show available NAND devices\n"
"nand device [dev] - show or set current device\n"
"nand read[.jffs2] - addr off|partition size\n"
"nand write[.jffs2] - addr off|partiton size - read/write `size' bytes starting\n"
" at offset `off' to/from memory address `addr'\n"
……
先在其中增加“nand write.yaffs ……”的使用說明:
U_BOOT_CMD(nand, 5, 1, do_nand,
"nand - NAND sub-system\n",
"info - show available NAND devices\n"
"nand device [dev] - show or set current device\n"
"nand read[.jffs2] - addr off|partition size\n"
"nand write[.jffs2] - addr off|partiton size - read/write `size' bytes starting\n"
" at offset `off' to/from memory address `addr'\n"
"nand read.yaffs addr off size - read the `size' byte yaffs image starting\n"
" at offset `off' to memory address `addr'\n"
"nand write.yaffs addr off size - write the `size' byte yaffs image starting\n"
" at offset `off' from memory address `addr'\n"
……
然后,在nand命令的處理函數(shù)do_nand中增加對(duì)“write.yaffs ……”的支持。do_nand函數(shù)仍在common/cmd_nand.c中實(shí)現(xiàn),代碼修改如下:
331 (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) {
……
354 }else if ( s != NULL && !strcmp(s, ".yaffs")){
355 if (read) {
356 /* read */
357 nand_read_options_t opts;
358 memset(&opts, 0, sizeof(opts));
359 opts.buffer = (u_char*) addr;
360 opts.length = size;
361 opts.offset = off;
362 opts.readoob = 1;
363 opts.quiet = quiet;
364 ret = nand_read_opts(nand, &opts);
365 } else {
366 /* write */
367 nand_write_options_t opts;
368 memset(&opts, 0, sizeof(opts));
369 opts.buffer = (u_char*) addr;/* yaffs文件系統(tǒng)映像存放的地址 */
370 opts.length = size;/* 長(zhǎng)度 */
371 opts.offset = off;/* 要燒寫到的NAND Flash的偏移地址 */
372 /* opts.forceyaffs = 1; *//* 計(jì)算ECC碼的方法,沒有使用 */
373 opts.noecc = 1; /* 不需要計(jì)算ECC,yaffs映像中有OOB數(shù)據(jù) */
374 opts.writeoob = 1;/* 寫OOB區(qū) */
375 opts.blockalign = 1;/* 每個(gè)“邏輯上的塊”大小為1個(gè)“物理塊” */
376 opts.quiet = quiet;/* 是否打印提示信息 */
377 opts.skipfirstblk = 1;/* 跳過第一個(gè)可用塊 */
378 ret = nand_write_opts(nand, &opts);
379 }
380 } else {
……
385 }
386
第354~379行就是針對(duì)命令“nand read.yaffs ……”、“nand
write.yaffs ……”增加的代碼。有興趣的讀者可以自己分析“if (read)”分支的代碼,下面只講解“else”分支,即“nand
write.yaffs ……”命令的實(shí)現(xiàn)。
NAND Flash每一頁大小為(512+16)字節(jié)(還有其他格式的NAND
Flash,比如每頁大小為(256+8)、(2048+64)等),其中的512字節(jié)就是一般存儲(chǔ)數(shù)據(jù)的區(qū)域,16字節(jié)稱為OOB(Out Of
Band)區(qū)。通常在OOB區(qū)存放壞塊標(biāo)記、前面512字節(jié)的ECC較驗(yàn)碼等。
cramfs、jffs2文件系統(tǒng)映像文件中并沒有OOB區(qū)的內(nèi)容,如果將它們燒入NOR
Flash中,則是簡(jiǎn)單的“平鋪”關(guān)系;如果將它們燒入NAND Flash中,則NAND
Flash的驅(qū)動(dòng)程序首先根據(jù)OOB的標(biāo)記略過壞塊,然后將一頁數(shù)據(jù)(512字節(jié))寫入后,還會(huì)計(jì)算這512字節(jié)的ECC較驗(yàn)碼,最后將它寫入OOB區(qū),
如此循環(huán)。cramfs、jffs2文件系統(tǒng)映像文件的大小通常是512的整數(shù)倍。
而yaffs文件系統(tǒng)映像文件的格式則跟它們不同,文件本身就包含了OOB區(qū)的數(shù)據(jù)(里面有
壞塊標(biāo)記、ECC較驗(yàn)碼、其他yaffs相關(guān)的信息)。所以燒寫時(shí),不需要再計(jì)算ECC值,首先檢查是否壞塊(是則跳過),然后寫入512字節(jié)的數(shù)據(jù),最
后寫入16字節(jié)的OOB數(shù)據(jù),如此循環(huán)。yaffs文件系統(tǒng)映像文件的大小是(512+16)的整數(shù)倍。
注意:燒寫yaffs文件系統(tǒng)映像時(shí),分區(qū)上第一個(gè)可用的(不是壞塊)塊也要跳過。
下面分析上面的代碼。
第369~371行設(shè)置源地址、目的地址、長(zhǎng)度。燒寫yaffs文件系統(tǒng)映像前,一般通過網(wǎng)
絡(luò)將它下載到內(nèi)存某個(gè)地址處(比如0x30000000),然后通過類似“nand write.yaffs 0x30000000
0x00A00000 $(filesize)”的命令燒到NAND
Flash的偏移地址0x00A00000處。對(duì)于這個(gè)命令,第369行中opts.buffer等于0x30000000,第370行中
opts.length等于$(filesize)的值,就是前面下載的文件的大小,第371行中的opts.offset等于0x00A00000。
這里列出不使用的第372行,是因?yàn)閛pts.forceyaffs這個(gè)名字很有欺騙性,它其實(shí)是指計(jì)算ECC較驗(yàn)碼的一種方法。燒寫yaffs文件系統(tǒng)映像時(shí),不需要計(jì)算ECC較驗(yàn)碼。
第373、374行指定燒寫數(shù)據(jù)時(shí)不計(jì)算ECC較驗(yàn)碼、而是燒入文件中的OOB數(shù)據(jù)。
第375行指定“邏輯塊”的大小,“邏輯塊”可以由多個(gè)“物理塊”組成,在yaffs文件系統(tǒng)映像中,它們是1:1的關(guān)系。
第377行的opts.skipfirstblk是新加的項(xiàng),nand_write_options_t結(jié)構(gòu)中沒有skipfirstblk成員。它表示燒寫時(shí)跳過第一個(gè)可用的邏輯塊──這是由yaffs文件系統(tǒng)的特性決定的。
既然skipfirstblk是在nand_write_options_t結(jié)構(gòu)中新加的項(xiàng),那么就要重新定義nand_write_options_t結(jié)構(gòu),并在下面調(diào)用的nand_write_opts函數(shù)中對(duì)它進(jìn)行處理。
首先在include/nand.h中如下修改,增加skipfirstblk成員:
struct nand_write_options {
u_char *buffer;/* memory block containing image to write */
ulong length;/* number of bytes to write */
ulong offset;/* start address in NAND */
int quiet;/* don't display progress messages */
int autoplace;/* if true use auto oob layout */
int forcejffs2;/* force jffs2 oob layout */
int forceyaffs;/* force yaffs oob layout */
int noecc;/* write without ecc */
int writeoob;/* image contains oob data */
int pad;/* pad to page size */
int blockalign;/* 1|2|4 set multiple of eraseblocks to align to */
int skipfirstblk; /* 新加,燒寫時(shí)跳過第一個(gè)可用的邏輯塊 */
};
typedef struct nand_write_options nand_write_options_t;
然后,修改nand_write_opts函數(shù)增加對(duì)skipfirstblk成員的支持。它在drivers/nand/nand_util.c文件中,下面的第301、第430~435行是新加的:
285 int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
286 {
……
300 int result;
301 int skipfirstblk = opts->skipfirstblk;
……
430 /* skip the first good block when wirte yaffs image, by www.100ask.net */
431 if (skipfirstblk) {
432 mtdoffset += erasesize_blockalign;
433 skipfirstblk = 0;
434 continue;
435 }
……
進(jìn)行了上面的移植后,U-Boot已經(jīng)可以燒yaffs文件系統(tǒng)映像了。由于前面設(shè)置“opts.noecc = 1”不使用ECC較驗(yàn)碼,在燒寫過程中會(huì)出現(xiàn)很多的提示信息:
Writing data without ECC to NAND-FLASH is not recommended
可以修改drivers/nand/nand_base.c文件的nand_write_page函數(shù)將它去掉:
917 case NAND_ECC_NONE:
918 printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
改為:
917 case NAND_ECC_NONE:
918 //printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
6. 修改默認(rèn)配置參數(shù)以方便使用
前面移植網(wǎng)卡芯片CS8900時(shí),已經(jīng)設(shè)置過默認(rèn)IP地址等。為了使用U-Boot時(shí)減少一些設(shè)置,現(xiàn)在修改配置文件include/configs/100ask24x0.h增加默認(rèn)配置參數(shù),其中一些在移植過程中已經(jīng)增加的選項(xiàng)這里也再次說明。
(1)Linux啟動(dòng)參數(shù)。
增加如下3個(gè)宏:
#define CONFIG_SETUP_MEMORY_TAGS 1/* 向內(nèi)核傳遞內(nèi)存分布信息 */
#define CONFIG_CMDLINE_TAG 1/* 向內(nèi)核傳遞命令行參數(shù) */
/* 默認(rèn)命令行參數(shù) */
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0"
(2)自動(dòng)啟動(dòng)命令。
增加如下2個(gè)宏:
/* 自動(dòng)啟動(dòng)前延時(shí)3秒 */
#define CONFIG_BOOTDELAY3
/* 自動(dòng)啟動(dòng)的命令 */
#define CONFIG_BOOTCOMMAND “nboot 0x32000000 0 0; bootm 0x32000000”
自動(dòng)啟動(dòng)時(shí)(開機(jī)3秒內(nèi)無輸入),首先執(zhí)行“nboot 0x32000000 0 0”命令將第0個(gè)NAND Flash偏移地址0上的映像文件復(fù)制到內(nèi)存0x32000000中;然后執(zhí)行“bootm 0x32000000”命令啟動(dòng)內(nèi)存中的映像。
(3)默認(rèn)網(wǎng)絡(luò)設(shè)置。
根據(jù)具體網(wǎng)絡(luò)環(huán)境增加、修改下面4個(gè)宏:
#define CONFIG_ETHADDR08:00:3e:26:0a:5b
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR192.168.1.17
#define CONFIG_SERVERIP192.168.1.11
2.6 U-Boot的常用命令
1. U-Boot的常用命令的用法
進(jìn)入U(xiǎn)-Boot控制界面后,可以運(yùn)行各種命令,比如下載文件到內(nèi)存,擦除、讀寫Flash,運(yùn)行內(nèi)存、NOR Flash、NAND Flash中的程序,查看、修改、比較內(nèi)存中的數(shù)據(jù)等。
使用各種命令時(shí),可以使用其開頭的若干個(gè)字母代替它。比如tftpboot命令,可以使用t、tf、tft、tftp等字母代替,只要其他命令不以這些字母開頭即可。
當(dāng)運(yùn)行一個(gè)命令之后,如果它是可重復(fù)執(zhí)行的(代碼中使用U_BOOT_CMD定義這個(gè)命令時(shí),第3個(gè)參數(shù)是1),若想再次運(yùn)行可以直接輸入回車。
U-Boot接受的數(shù)據(jù)都是16進(jìn)制,輸入時(shí)可以省略前綴0x、0X。
下面介紹常用的命令:
(1)幫助命令help。
運(yùn)行help命令可以看到U-Boot中所有命令的作用,如果要查看某個(gè)命令的使用方法,運(yùn)行“help 命令名”,比如“help bootm”。
可以使用“?”來代替“help”,比如直接輸入“?”、“? bootm”。
(2)下載命令。
U-Boot支持串口下載、網(wǎng)絡(luò)下載,相關(guān)命令有:loadb、loads、loadx、loady和tftpboot、nfs。
前幾個(gè)串口下載命令使用方法相似,以loadx命令為例,它的用法為“loadx [
off ] [ baud
]”。中括號(hào)“[]”表示里面的參數(shù)可以省略,off表示文件下載后存放的內(nèi)存地址,baud表示使用的波特率。如果baud參數(shù)省略,則使用當(dāng)前的波特
率;如果off參數(shù)省略,存放的地址為配置文件中定義的宏CFG_LOAD_ADDR。
tftpboot命令使用TFTP協(xié)議從服務(wù)器下載文件,服務(wù)器的IP地址為環(huán)境變量
serverip。用法為“tftpboot [loadAddress]
[bootfilename]”,loadAddress表示文件下載后存放的內(nèi)存地址,bootfilename表示要下載的文件的名稱。如果
loadAddress省略,存放的地址為配置文件中定義的宏CFG_LOAD_ADDR;如果bootfilename省略,則使用單板的IP地址構(gòu)造
一個(gè)文件名,比如單板IP為192.168.1.17,則缺省的文件名為C0A80711.img。
nfs命令使用NFS協(xié)議下載文件,用法為“nfs [loadAddress]
[host ip
addr:bootfilename]”。loadAddress、bootfilename的意義與tftpboot命令一樣,host ip
addr表示服務(wù)器的IP地址,默認(rèn)為環(huán)境變量serverip。
下載文件成功后,U-Boot會(huì)自動(dòng)創(chuàng)建或更新環(huán)境變量filesize,它表示下載的文件的長(zhǎng)度,可以在后續(xù)命令中使用“$(filesize)”來引用它。
(3)內(nèi)存操作命令。
常用的命令有:查看內(nèi)存命令md、修改內(nèi)存命令md、填充內(nèi)存命令mw、拷貝命令cp。這些
命令都可以帶上后綴“.b”、“.w”或“.l”,表示以字節(jié)、字(2個(gè)字節(jié))、雙字(4個(gè)字節(jié))為單位進(jìn)行操作。比如“cp.l 30000000
31000000 2”將從開始地址0x30000000處,拷貝2個(gè)雙字到開始地址為0x31000000的地方。
md命令用法為“md[.b, .w, .l] address [count]”,表示以字節(jié)、字或雙字(默認(rèn)為雙字)為單位,顯示從地址address開始的內(nèi)存數(shù)據(jù),顯示的數(shù)據(jù)個(gè)數(shù)為count。
mm命令用法為“mm[.b, .w, .l] address”,表示以字節(jié)、字或雙字(默認(rèn)為雙字)為單位,從地址address開始修改內(nèi)存數(shù)據(jù)。執(zhí)行mm命令后,輸入新數(shù)據(jù)后回車,地址會(huì)自動(dòng)增加,Ctrl+C退出。
mw命令用法為“mw[.b, .w, .l] address value [count]”,表示以字節(jié)、字或雙字(默認(rèn)為雙字)為單位,往開始地址為address的內(nèi)存中填充count個(gè)數(shù)據(jù),數(shù)據(jù)值為value。
cp命令用法為“cp[.b, .w, .l] source target count”,表示以字節(jié)、字或雙字(默認(rèn)為雙字)為單位,從源地址source的內(nèi)存拷貝count個(gè)數(shù)據(jù)到目的地址的內(nèi)存。
(4)NOR Flash操作命令。
常用的命令有查看Flash信息的flinfo命令、加/解寫保護(hù)命令protect、擦除
命令erase。由于NOR Flash的接口與一般內(nèi)存相似,所以一些內(nèi)存命令可以在NOR Flash上使用,比如讀NOR
Flash時(shí)可以使用md、cp命令,寫NOR Flash時(shí)可以使用cp命令(cp根據(jù)地址分辨出是NOR Flash,從而調(diào)用NOR
Flash驅(qū)動(dòng)完成寫操作)。
直接運(yùn)行“flinfo”即可看到NOR Flash的信息,有NOR Flash的型號(hào)、容量、各扇區(qū)的開始地址、是否只讀等信息。比如對(duì)于本書基于的開發(fā)板,flinfo命令的結(jié)果如下:
Bank # 1: AMD: 1x Amd29LV800BB (8Mbit)
Size: 1 MB in 19 Sectors
Sector Start Addresses:
00000000 (RO) 00004000 (RO) 00006000 (RO) 00008000 (RO) 00010000 (RO)
00020000 (RO) 00030000 00040000 00050000 00060000
00070000 00080000 00090000 000A0000 000B0000
000C0000 000D0000 000E0000 000F0000 (RO)
其中的RO表示該扇區(qū)處于寫保護(hù)狀態(tài),只讀。
對(duì)于只讀的扇區(qū),在擦除、燒寫它之前,要先解除寫保護(hù)。最簡(jiǎn)單的命令為“protect off all”,解除所有NOR Flash的寫保護(hù)。
erase命令常用的格式為“erase start
end”──擦除的地址范圍為start至end、“erase start +len”──擦除的地址范圍為start至(start + len
– 1),“erase all”──表示擦除所有NOR Flash。
注意:其中的地址范圍,剛好是一個(gè)扇區(qū)的開始地址到另一個(gè)(或同一個(gè))扇區(qū)的結(jié)束地址。比如要擦除Amd29LV800BB的前5個(gè)扇區(qū),執(zhí)行的命令為“erase 0 0x2ffff”,而非“erase 0 0x30000”。
(5)NAND Flash操作命令。
NAND Flash操作命令只有一個(gè):nand,它根據(jù)不同的參數(shù)進(jìn)行不同操作,比如擦除、讀取、燒寫等。
“nand info”查看NAND Flash信息。
“nand erase [clean] [off size]”擦除NAND
Flash。加上“clean”時(shí),表示在每個(gè)塊的第一個(gè)扇區(qū)的OOB區(qū)加寫入清除標(biāo)記;off、size表示要擦除的開始偏移地址和長(zhǎng)度,如果省略
off和size,表示要擦除整個(gè)NAND Flash。
“nand read[.jffs2] addr off size”從NAND Flash偏移地址off處讀出size個(gè)字節(jié)的數(shù)據(jù),存放到開始地址為addr的內(nèi)存中。是否加后綴“.jffs”的差別只是讀操作時(shí)的ECC較驗(yàn)方法不同。
“nand write[.jffs2] addr off size”把開始地址為addr的內(nèi)存中的size個(gè)字節(jié)數(shù)據(jù),寫到NAND Flash的偏移地址off處。是否加后綴“.jffs”的差別只是寫操作時(shí)的ECC較驗(yàn)方法不同。
“nand read.yaffs addr off size”從NAND Flash偏移地址off處讀出size個(gè)字節(jié)的數(shù)據(jù)(包括OOB區(qū)域),存放到開始地址為addr的內(nèi)存中。
“nand write.yaffs addr off size”把開始地址為addr的內(nèi)存中的size個(gè)字節(jié)數(shù)據(jù)(其中有要寫入OOB區(qū)域的數(shù)據(jù)),寫到NAND Flash的偏移地址off處。
“nand dump off”,將NAND Flash偏移地址off的一個(gè)扇區(qū)的數(shù)據(jù)打印出來,包括OOB數(shù)據(jù)。
(6)環(huán)境變量命令。
“printenv”命令打印全部環(huán)境變量,“printenv name1 name2 ...”打印名字為name1、name2、……”的環(huán)境變量。
“setenv name value”設(shè)置名字為name的環(huán)境變量的值為value。
“setenv name”刪除名字為name的環(huán)境變量。
上面的設(shè)置、刪除操作只是在內(nèi)存中進(jìn)行,“saveenv”將更改后的所有環(huán)境變量寫入NOR Flash中。
(7)啟動(dòng)命令。
不帶參數(shù)的“boot”、“bootm”命令都是執(zhí)行環(huán)境變量bootcmd所指定的命令。
“bootm [addr [arg
...]]”命令啟動(dòng)存放在地址addr處的U-Boot格式的映像文件(使用U-Boot目錄tools下的mkimage工具制作得到),[arg
...]表示參數(shù)。如果addr參數(shù)省略,映像文件所在地址為配置文件中定義的宏CFG_LOAD_ADDR。
“go addr [arg ...]”與bootm命令類似,啟動(dòng)存放在地址addr處的二進(jìn)制文件, [arg ...]表示參數(shù)。
“nboot [[[loadAddr] dev] offset]”命令將NAND
Flash設(shè)備dev上偏移地址off處的映像文件復(fù)制到內(nèi)存loadAddr處,然后,如果環(huán)境變量autostart的值為“yes”,就啟動(dòng)這個(gè)映
像。如果loadAddr參數(shù)省略,存放地址為配置文件中定義的宏CFG_LOAD_ADDR;如果dev參數(shù)省略,則它的取值為環(huán)境變量
bootdevice的值;如果offset參數(shù)省略,則默認(rèn)為0。
2. U-Boot命令使用實(shí)例
下面通過一個(gè)例子來演示如何使用各種命令燒寫內(nèi)核映像文件、yaffs映像文件,并啟動(dòng)系統(tǒng)。
(1)制作內(nèi)核映像文件。
對(duì)于本書使用的Linux 2.6.22.6版本,編譯內(nèi)核時(shí)可以直接生成U-Boot格式的映像文件uImage。
對(duì)于不能直接生成uImage的內(nèi)核,制作方法在U-Boot根目錄下的README文件中
有說明,假設(shè)已經(jīng)編譯好的內(nèi)核文件為vmlinux,它是ELF格式的。mkimage是U-Boot目錄tools下的工具,它在編譯U-Boot時(shí)自
動(dòng)生成。執(zhí)行以下3個(gè)命令將內(nèi)核文件vmlinux制作為U-Boot格式的映像文件uImage,它們首先將vmlinux轉(zhuǎn)換為二進(jìn)制格式,然后壓
縮,最后構(gòu)造頭部信息(里面包含有文件名稱、大小、類型、CRC較驗(yàn)碼等):
① arm-linux-objcopy -O binary -R .note -R .comment -S vmlinux linux.bin
② gzip -9 linux.bin
③ mkimage -A arm -O linux -T kernel -C gzip -a 0x30008000 -e 0x30008000 -n "Linux Kernel Image" -d linux.bin.gz uImage
(2)燒寫內(nèi)核映像文件uImage。
首先將uImage放在主機(jī)上的tftp或nfs目錄下,確保已經(jīng)開啟tftp或nfs服務(wù)。
然后運(yùn)行如下命令下載文件,擦除、燒寫NAND Flash:
① tftp 0x30000000 uImage 或 nfs 0x30000000 192.168.1.57:/work/nfs_root/uImage
② nand erase 0x0 0x00200000
③ nand write.jffs2 0x30000000 0x0 $(filesize)
第3條命令之所以使用“nand write.jffs2”而不是“nand
write”,是因?yàn)榍罢卟灰笪募拈L(zhǎng)度是頁對(duì)齊的(512字節(jié)對(duì)齊)。也可以使用“nand
write”,但是需要將命令中的長(zhǎng)度參數(shù)改為$(filesize)向上進(jìn)行512取整后的值。比如uImage的大小為1540883,向上進(jìn)行
512取整后為1541120(即0x178400),可以使用命令“nand write 0x30000000 0x0
0x178400”進(jìn)行燒寫。
(3)燒寫yaffs文件系統(tǒng)映像。
假設(shè)yaffs文件系統(tǒng)映像的文件名為yaffs.img,首先將它放在主機(jī)上的tftp或nfs目錄下,確保已經(jīng)開啟tftp或nfs服務(wù);然后執(zhí)行如下命令下載、擦除、燒寫:
① tftp 0x30000000 yaffs.img 或 nfs 0x30000000 192.168.1.57:/work/nfs_root/yaffs.img
② nand erase 0xA00000 0x3600000
③ nand write.yaffs 0x30000000 0xA00000 $(filesize)
這時(shí),重啟系統(tǒng),在U-Boot倒數(shù)3秒之后,就會(huì)自動(dòng)啟動(dòng)Linux系統(tǒng)。
(4)燒寫jffs2文件系統(tǒng)映像。
假設(shè)jffs2文件系統(tǒng)映像的文件名為jffs2.img,首先將它放在主機(jī)上的tftp或nfs目錄下,確保已經(jīng)開啟tftp或nfs服務(wù);然后執(zhí)行如下命令下載、擦除、燒寫:
① tftp 0x30000000 jffs2.img 或 nfs 0x30000000 192.168.1.57:/work/nfs_root/jffs2.img
② nand erase 0x200000 0x800000
③ nand write.jffs2 0x30000000 0x200000 $(filesize)
系統(tǒng)啟動(dòng)后,就可以使用“mount -t jffs2 /dev/mtdblock1 /mnt”掛接jffs2文件系統(tǒng)。
2.7 使用U-Boot來執(zhí)行程序
在前面的硬件實(shí)驗(yàn)中使用JTAG燒寫程序到NAND
Flash,燒寫過程十分緩慢。如果使用U-Boot來燒寫NAND Flash,效率會(huì)高很多。燒寫二進(jìn)制文件到NAND
Flash中所使用的命令與上面燒寫內(nèi)核映像文件uImage的過程類似,只是不需要將二進(jìn)制文件制作成U-Boot格式。
另外,可以將程序下載到內(nèi)存中,然后使用go命令執(zhí)行它。假設(shè)有一個(gè)程序的二進(jìn)制可執(zhí)行文件
test.bin,連接地址為0x30000000。首先將它放在主機(jī)上的tftp或nfs目錄下,確保已經(jīng)開啟tftp或nfs服務(wù);然后將它下載到內(nèi)
存0x30000000處,最后使用go命令執(zhí)行它:
① tftp 0x30000000 test.bin 或 nfs 0x30000000 192.168.1.57:/work/nfs_root/test.bin
② go 0x30000000
下一篇:嵌入式系統(tǒng)UBOOT
掃碼關(guān)注我們
傳真:0755-82591176
郵箱:vicky@yingtexin.net
地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤(rùn)豐創(chuàng)業(yè)園A棟2樓A08