新一篇: U-BOOT下使用bootm引導內核方法

前幾天大致分析了u-boot針對smdk2410的源碼,了解了啟動的流程,但是對板上許多硬件的驅動過程還不太清楚。smdk2410源碼中有針對Nor Flash的初始化和讀取,但源碼中沒有對Nand Flash的操作,雖然針對其他型號的板子應該有Nand的源碼,但方便起見,我查閱了vivi的源碼,它支持從Nand Flash啟動,自然有我需要的東西。下面我就自己的分析和總結列出來,中間當然也從google上得到不少前人留下的寶貴資料:)。

我先摘錄一段對NorNand flash區別的幾條總結:
    NOR flash位讀寫,因為它具有sram的接口,有足夠的引腳來尋址,可以很容易的存取其內部的每一個字節
    NAND flash使用復雜的I/O口來串行地存取數據。8個引腳用來傳送控制、地址和數據信息(復用)。NAND的讀和寫單位為512Byte的頁,擦寫單位為32頁的塊
    ● NOR的讀速度比NAND稍快一些。
    ● NAND的寫入速度比NOR快很多。 
    ● NAND的4ms擦除速度遠比NOR的5s快。
    ● 大多數寫入操作需要先進行擦除操作。
    ● NAND的擦除單元更小,相應的擦除電路更少。
    在NOR器件上運行代碼不需要任何的軟件支持,在NAND器件上進行同樣操作時,通常需要驅動程序,也就是內存技術驅動程序(MTD),NAND和NOR器件在進行寫入和擦除操作時都需要MTD。

再來看看Nand flash自身的特點(部分摘自August0703的文章):
     Nand Flash
的數據是以bit的方式保存在memory cell中,一般來說,一個cell 中只能存儲一個bit。這些cell 8個或者16個為單位,連成bit line,形成所謂的byte(x8)/word(x16)這就是NAND Device的位寬
    
多個line(多個位寬大小的數據)會再組成Page。我使用的Nand flash是三星的K9F1208U0M,從datesheet上得知,此flash每頁528Bytes512byteMain Area + 16byteSpare Area),每32page形成一個Block(32*528B)。具體一片flash上有多少個Block視需要所定。我所使用的k9f1208U0M具有4096block,故總容量為4096*32*528B=66MB,但是其中的2MBSpaer Area)是用來保存ECC校驗碼等額外數據的,故實際中可使用的為64MB
    Nand flash
以頁(512Byte)為單位讀寫數據,而以塊(16KB)為單位擦除數據。按照這樣的組織方式可以形成所謂的三類地址: 
    ● Column Address
:列地址,地址的低8
    ● Page Address 
:頁地址
    ● Block Address 
:塊地址
    
對於NAND Flash來講,地址和命令只能在I/O[7:0]上傳遞,數據寬度也是8位,這導致在讀寫指定地址的數據時,地址是分4次傳遞的(3次右移),見后文。
    s3c2410
這個處理器之所以可以直接從Nand flash啟動,是因為CPU內置了4KB的片內SRAM,手冊上稱作“Steppingstone”。板子上電復位之后,CPU會自動將Nand flash的前4KB代碼拷貝到片內SRAM中去執行(此過程是靠硬件實現的,見datasheetFigure 6-1. NAND Flash Controller Block Diagram),這也是導致從NorNand啟動后的內存映射不同的原因。所以,vivistage1代碼head.S必須要小於4KB,其中實現基本的CPU初始化等工作,並且要實現把自身拷貝到SDRAM中,之后的stage2就實現復雜功能。
    
關於SDRAM的初始化,之前分析u-boot時雖然涉及,但那篇沒有仔細分析,我將另外歸納一篇。

下面具體看一下如何讀寫這塊Nand flash
    
s3c2410datasheet上得知,Nand flash的操作通過NFCONFNFCMDNFADDRNFDATANFSTATNFECC這六個寄存器來完成,並且列出操作Nand flash4個步驟:

NAND FLASH MODE CONFIGURATION
1. Set NAND flash configuration by NFCONF register.
2. Write NAND flash command onto NFCMD register.
3. Write NAND flash address onto NFADDR register.
4. Read/Write data while checking NAND flash status by NFSTAT register. R/nB signal should be checked before read operation or after program operation.

    下面結合vivi源碼來詳細的分析具體如何操作這6個寄存器來完成以上4個步驟來完成讀過程:
先要初始化Nand flash,緊接著復位一下:

void reset_nand()
{
    int i=0;
    NFCONF &= ~0x800;    /* 現在真正使用Nand flash,bit[11]要置0,與初始化時相反 */
        for(; i<10; i++);
    NFCMD = 0xff;    //reset command

/* 復位命令。NFCMD寄存器只用到低8位(bit[7:0])。
K9F1208U0M手冊中列出了針對此塊flash的各種命令,見Table 1. Command Sets。vivi/include/mtd/nand.h更直觀的列出了各種命令 */

    wait_idle();
}

/* 初始化NAND Flash */
/* NFCONF設定為0xf830,作用是使能Nand flash控制器、初始化ECC、Nand flash片選信號nFCE=1(inactive,真正使用時再讓它等於0)、設置TACLS、TWRPH0、TWRPH1。
TACLS、 TWRPH0、TWRPH1這三個參數是控制Nand flash信號線CLE/ALE和寫控制信號nWE的時序關系的,要參照具體的flash芯片手冊來設置。我這個是K9F1208U0M ,在表“AC Timing Characteristics for Command / Address / Data Input”中可以看到: 
CLE setup Time = 0 ns,CLE Hold Time = 10 ns, 
ALE setup Time = 0 ns,ALE Hold Time = 10 ns, 
WE Pulse Width = 25 ns 
可 以計算,即使在HCLK=100MHz的情況下,TACLS+TWRPH0+TWRPH1=6/100 uS=60 ns,也是可以滿足NAND Flash K9F1208U0M的時序要求的。(此句摘自thisway.diy@163.com的文章。關於如何配置時序,以后我得好好學習一下,還自詡電子出 身,丟人啊。。。)*/
 
void init_nand()
{
    NFCONF = 0xf830;
    reset_nand();
}

    初始化Nand flash之后,就可以把stage2main函數代碼拷貝到SDRAM中去執行,當然之前已經配置好了SDRAM。以上工作都是在stage1階段(片內SRAM中)完成的,之后就可以讀寫Nand flash了。
    
下面分析讀操作的實現,貼上vivi/s3c2410/nand_read.c源碼:

#include <config.h>

#define __REGb(x)    (*(volatile unsigned char *)(x))
#define __REGi(x)    (*(volatile unsigned int *)(x))
#define NF_BASE        0x4e000000
#define NFCONF        __REGi(NF_BASE + 0x0)
#define NFCMD        __REGb(NF_BASE + 0x4)
#define NFADDR        __REGb(NF_BASE + 0x8)
#define NFDATA        __REGb(NF_BASE + 0xc)
#define NFSTAT        __REGb(NF_BASE + 0x10)

#define BUSY 1
inline void wait_idle(void) {
    int i;

/* NFSTAT:只用到位0,0-busy,1-ready */
    while(!(NFSTAT & BUSY))
      for(i=0; i<10; i++);
}

#define NAND_SECTOR_SIZE    512            /* Nand flash是以512Byte為單位來讀寫的 */
#define NAND_BLOCK_MASK        (NAND_SECTOR_SIZE - 1)

/* low level nand read function */
/* 下面的讀過程嚴格按照2410手冊上的順序 */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
    int i, j;

    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
        return -1;    /* invalid alignment */
    }

/* chip Enable */
/* 對應第一條:1. Set NAND flash configuration by NFCONF register. */
    NFCONF &= ~0x800;
    for(i=0; i<10; i++);

    for(i=start_addr; i < (start_addr + size);) {
      /* READ0 */
/* 對應第二條:2. Write NAND flash command onto NFCMD register. */
      NFCMD = 0;

      /* Write Address */
/* 對應第三條:3. Write NAND flash address onto NFADDR register. 
*NFADDR寄存器也只用到低八位來傳輸,所以需要分4次來寫入一個完整的32位地址,需要注意后3次的移位操作
*/

      NFADDR = i & 0xff;
      NFADDR = (>> 9) & 0xff;
      NFADDR = (>> 17) & 0xff;
      NFADDR = (>> 25) & 0xff;

/* 對應第四條:4. Read/Write data while checking NAND flash status by NFSTAT register. 
一個地址對應512個字節數據。所以,由於8bit位寬的限制,每次讀取8位(1個字節),共讀512次得到1頁512Byte數據
*/

      wait_idle();
      for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
        *buf = (NFDATA & 0xff);
        buf++;
      }
    }

/* chip Disable */
/* 讀寫完畢需要禁止Nand flash ,與開始相對應*/
    NFCONF |= 0x800;    /* chip disable */

    return 0;
}


    以上是對
Nand flash讀操作的分析。總體來看,關鍵在於根據CPUFlashdatasheet配置各寄存器和按照規定順序進行操作。具體的配置過程是比較繁雜的,可參照u-bootvivi中對各種硬件支持的源碼來配置,可省不少事。以后若自己嘗試寫bootloader,再實踐一下作為練習吧。

創作者介紹
創作者 轉貼部落格 的頭像
丘猴子

轉貼部落格

丘猴子 發表在 痞客邦 留言(0) 人氣( 5330 )