前幾天大致分析了u-boot針對smdk2410的源碼,了解了啟動的流程,但是對板上許多硬件的驅動過程還不太清楚。smdk2410源碼中有針對Nor Flash的初始化和讀取,但源碼中沒有對Nand Flash的操作,雖然針對其他型號的板子應該有Nand的源碼,但方便起見,我查閱了vivi的源碼,它支持從Nand Flash啟動,自然有我需要的東西。下面我就自己的分析和總結列出來,中間當然也從google上得到不少前人留下的寶貴資料:)。
我先摘錄一段對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個步驟:
|
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之后,就可以把stage2的main函數代碼拷貝到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 = (i >> 9) & 0xff; NFADDR = (i >> 17) & 0xff; NFADDR = (i >> 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讀操作的分析。總體來看,關鍵在於根據CPU和Flash的datasheet配置各寄存器和按照規定順序進行操作。具體的配置過程是比較繁雜的,可參照u-boot或vivi中對各種硬件支持的源碼來配置,可省不少事。以后若自己嘗試寫bootloader,再實踐一下作為練習吧。
請先 登入 以發表留言。