天天看點

對Nand flash讀操作的分析

    前幾天大緻分析了u-boot針對smdk2410的源碼,了解了啟動的流程,但是對闆上許多硬體的驅動過程還不太清楚。smdk2410源碼中有針對Nor Flash的初始化和讀取,但源碼中沒有對Nand Flash的操作,雖然針對其他型号的闆子應該有Nand的源碼,但友善起見,我查閱了vivi的源碼,它支援從Nand Flash啟動,自然有我需要的東西。下面我就自己的分析和總結列出來,中間當然也從google上得到不少前人留下的寶貴資料:)。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

我先摘錄一段對Nor和Nand 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每頁528Bytes(512byte的Main Area + 16byte的Spare Area),每32個page形成一個Block(32*528B)。具體一片flash上有多少個Block視需要所定。我所使用的k9f1208U0M具有4096個block,故總容量為4096*(32*528B)=66MB,但是其中的2MB(Spaer 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中去執行(此過程是靠硬體實作的,見datasheet圖Figure 6-1. NAND Flash Controller Block Diagram),這也是導緻從Nor和Nand啟動後的記憶體映射不同的原因。是以,vivi的stage1代碼head.S必須要小于4KB,其中實作基本的CPU初始化等工作,并且要實作把自身拷貝到SDRAM中,之後的stage2就實作複雜功能。

    關于SDRAM的初始化,之前分析u-boot時雖然涉及,但那篇沒有仔細分析,我将另外歸納一篇。

下面具體看一下如何讀寫這塊Nand flash:

    從s3c2410的datasheet上得知,Nand flash的操作通過NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT和NFECC這六個寄存器來完成,并且列出操作Nand flash的4個步驟:

<code>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.</code>

    下面結合vivi源碼來詳細的分析具體如何操作這6個寄存器來完成以上4個步驟來完成讀過程:

先要初始化Nand flash,緊接着複位一下:

<code>void reset_nand() {     int i=0;     NFCONF &amp;= ~0x800;    /* 現在真正使用Nand flash,bit[11]要置0,與初始化時相反 */         for(; i10; 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的時序要求的。(此句摘自[email protected]的文章。關于如何配置時序,以後我得好好學習一下,還自诩電子出身,丢人啊。。。)*/ void init_nand() {     NFCONF = 0xf830;     reset_nand(); }</code>

    初始化Nand flash之後,就可以把stage2的main函數代碼拷貝到SDRAM中去執行,當然之前已經配置好了SDRAM。以上工作都是在stage1階段(片内SRAM中)完成的,之後就可以讀寫Nand flash了。

    下面分析讀操作的實作,貼上vivi/s3c2410/nand_read.c源碼:

<code>#include config.h&gt; #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 &amp; BUSY))       for(i=0; i10; 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 &amp; NAND_BLOCK_MASK) || (size &amp; NAND_BLOCK_MASK)) {         return -1;    /* invalid alignment */     } /* chip Enable */ /* 對應第一條:1. Set NAND flash configuration by NFCONF register. */     NFCONF &amp;= ~0x800;     for(i=0; i10; 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 &amp; 0xff;       NFADDR = (i &gt;&gt; 9) &amp; 0xff;       NFADDR = (i &gt;&gt; 17) &amp; 0xff;       NFADDR = (i &gt;&gt; 25) &amp; 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 &amp; 0xff);         buf++;       }     } /* chip Disable */ /* 讀寫完畢需要禁止Nand flash ,與開始相對應*/     NFCONF |= 0x800;    /* chip disable */     return 0; }</code>

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