68精品久久久久久欧美,最近中文字幕完整在线看一,久久亚洲男人天堂,最近中文字幕完整视频高清1

您好,歡迎進(jìn)入深圳市穎特新科技有限公司官方網(wǎng)站!

您現(xiàn)在的位置:首頁 新聞資訊 >> 新聞?lì)^條 >> Bootloader之uBoot簡(jiǎn)介(轉(zhuǎn))
新聞資訊
NEWS INFORMATION

Bootloader之uBoot簡(jiǎn)介(轉(zhuǎn))

發(fā)布時(shí)間:2019-05-22

來自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)過程

  • 1. 概述

在移植之前先了解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)用程序。

  • 2. Bootloader的兩個(gè)階段Bootloader的啟動(dòng)過程啟動(dòng)過程可以分為單階段(Single Stage)、多階段(Multi-Stage)兩種。通常多階段的Bootloader能提供更為復(fù)雜的功能,以及更好的可移植性。從固態(tài)存儲(chǔ)設(shè)備上啟 動(dòng)的Bootloader大多都是 2 階段的啟動(dòng)過程。這從前面的硬件實(shí)驗(yàn)可以很好地理解這點(diǎn):第一階段使用匯編來實(shí)現(xiàn),它完成一些依賴于 CPU 體系結(jié)構(gòu)的初始化,并調(diào)用第二階段的代碼。第二階段則通常使用C語言來實(shí)現(xiàn),這樣可以實(shí)現(xiàn)更復(fù)雜的功能,而且代碼會(huì)有更好的可讀性和可移植性。

一般而言,這兩個(gè)階段完成的功能可以如下分類,但這不是絕對(duì)的:

(1)Bootloader第一階段的功能。

    • 硬件設(shè)備初始化。
    • 為加載Bootloader的第二階段代碼準(zhǔn)備RAM空間。
    • 拷貝Bootloader的第二階段代碼到 RAM 空間中。
    • 設(shè)置好棧。
    • 跳轉(zhuǎn)到第二階段代碼的C入口點(diǎn)。

在第一階段進(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第二階段的功能。

    • 初始化本階段要使用到的硬件設(shè)備。
    • 檢測(cè)系統(tǒng)內(nèi)存映射(memory map)。
    • 將內(nèi)核映像和根文件系統(tǒng)映像從Flash上讀到RAM空間中。
    • 為內(nèi)核設(shè)置啟動(dòng)參數(shù)。
    • 調(diào)用內(nèi)核。

為了方便開發(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è)置。

    • R0=0
    • R1=機(jī)器類型ID;對(duì)于ARM結(jié)構(gòu)的CPU,其機(jī)器類型ID可以參見 linux/arch/arm/tools/mach-types。
    • R2=啟動(dòng)參數(shù)標(biāo)記列表在 RAM 中起始基地址

(2)CPU工作模式。

    • 必須禁止中斷(IRQs和FIQs)
    • CPU 必須 SVC 模式

(3)Cache 和 MMU 的設(shè)置。

    • MMU 必須關(guān)閉
    • 指令 Cache 可以打開也可以關(guān)閉
    • 數(shù)據(jù) Cache 必須關(guān)閉

如果用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);

  • 3. Bootloader與內(nèi)核的交互

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有如下特性:

  • 開放源碼;
  • 支持多種嵌入式操作系統(tǒng)內(nèi)核,如Linux、NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS;
  • 支持多個(gè)處理器系列,如PowerPC、ARM、x86、MIPS、XScale;
  • 較高的可靠性和穩(wěn)定性;
  • 高度靈活的功能設(shè)置,適合U-Boot調(diào)試、操作系統(tǒng)不同引導(dǎo)要求、產(chǎn)品發(fā)布等;
  • 豐富的設(shè)備驅(qū)動(dòng)源碼,如串口、以太網(wǎng)、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、鍵盤等;
  • 較為豐富的開發(fā)調(diào)試文檔與強(qiáng)大的網(wǎng)絡(luò)技術(shù)支持;
  • 支持NFS掛載、RAMDISK(壓縮或非壓縮)形式的根文件系統(tǒng)
  • 支持NFS掛載、從FLASH中引導(dǎo)壓縮或非壓縮系統(tǒng)內(nèi)核;
  • 可靈活設(shè)置、傳遞多個(gè)關(guān)鍵參數(shù)給操作系統(tǒng),適合系統(tǒng)在不同開發(fā)階段的調(diào)試要求與產(chǎn)品發(fā)布,尤對(duì)Linux支持最為強(qiáng)勁;
  • 支持目標(biāo)板環(huán)境變量多種存儲(chǔ)方式,如FLASH、NVRAM、EEPROM;
  • CRC32校驗(yàn),可校驗(yàn)FLASH中內(nèi)核、RAMDISK鏡像文件是否完好;
  • 上電自檢功能:SDRAM、FLASH大小自動(dòng)檢測(cè);SDRAM故障檢測(cè);CPU型號(hào);
  • 特殊功能:XIP內(nèi)核引導(dǎo);

可以從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è)文件:

  • u-boot.bin:二進(jìn)制可執(zhí)行文件,它就是可以直接燒入ROM、NOR Flash的文件。
  • u-boot:ELF格式的可執(zhí)行文件
  • u-boot.srec:Motorola S-Record格式的可執(zhí)行文件

對(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ā)板,它們的外接硬件相同:

  • BANK0外接容量為1MB,位寬為8的NOR Flash芯片AM29LV800
  • BANK3外接10M網(wǎng)卡芯片CS8900,位寬為16
  • BANK6外接兩片容量為32MB、位寬為16的SDRAM芯片K4S561632,組成容量為64MB、位寬為32的內(nèi)存
  • 通過NAND Flash控制器外接容量為64MB,位寬為8的NAND Flash芯片K9S1208

對(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ǔ)丁),增加如下新功能:

  • 同時(shí)支持本書使用的S3C2410和S3C2440開發(fā)板
  • 支持串口xmodem協(xié)議
  • 支持網(wǎng)卡芯片CS8900
  • 支持NAND Flash讀寫
  • 支持燒寫yaffs文件系統(tǒng)映像

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

聯(lián)系方式0755-82591179

傳真:0755-82591176

郵箱:vicky@yingtexin.net

地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤(rùn)豐創(chuàng)業(yè)園A棟2樓A08

明光市| 和田县| 平安县| 柞水县| 泾源县| 霍林郭勒市| 苏尼特左旗| 彭阳县| 长岭县| 监利县| 石屏县| 巴里| 宝山区| 南昌县| 衡东县| 红河县| 米脂县| 江达县| 清远市| 三门县| 大化| 井冈山市| 乐亭县| 南乐县| 元朗区| 鞍山市| 汝南县| 博客| 富顺县| 安塞县| 历史| 辽中县| 宁津县| 兴文县| 正安县| 余庆县| 香格里拉县| 敖汉旗| 津南区| 台中市| 鹤峰县|