今天在利用ARM7上的nandflash控制器驅(qū)動(dòng),ID已讀取成功,擦寫,讀取等尚未完成,晚上就在網(wǎng)上查查相關(guān)的知識(shí),覺得有一個(gè)不錯(cuò),轉(zhuǎn)貼如下:
NAND Flash 的數(shù)據(jù)是以bit的方式保存在memory cell,一般來說,一個(gè)cell 中只能存儲(chǔ)一個(gè)bit。這些cell 以8 個(gè)或者16 個(gè)為單位,連成bitline,形成所謂的byte(x8)/word(x16),這就是NAND Device 的位寬。這些Line 會(huì)再組成Page.
(NandFlash 有多種結(jié)構(gòu),我使用的Nand Flash 是K9F1208,下面內(nèi)容針對(duì)三星的K9F1208U0M),每頁528Byte,每32個(gè)page 形成一個(gè)Block, Sizeof(block)=16kByte 。
1block="16kbyte",512Mbit=64Mbyte,Numberof(block)=4096 1block=32page,1page=528byte=512byte(Main Area)+16byte(Spare Area)
Nand flash以頁為單位讀寫數(shù)據(jù),而以塊為單位擦除數(shù)據(jù)。
按照這樣的組織方式可以形成所謂的三類地址:
--BlockAddress -- Page Address --Column Address
對(duì)于NAND Flash來講,地址和命令只能在I/O[7:0]上傳遞,數(shù)據(jù)寬度是8 位。
512byte需要9bit來表示,對(duì)于528byte系列的NAND,這512byte被分成1st half和2nd half,各自的訪問由地址指針命令來選擇,A[7:0]就是所謂的column address。
32個(gè)page 需要5bit 來表示,占用A[13:9],即該page 在塊內(nèi)的相對(duì)地址。Block的地址是由A14 以上的bit來表示,例如512Mb 的NAND,共4096block,因此,需要12 個(gè)bit 來表示,即A[25:14],如果是1Gbit的528byte/page的NAND Flash,則block address用A[26:24]表示。而page address就是blcokaddress|page address in block
NAND Flash 的地址表示為:
Block Address|Page Address in block|halfpagepointer|Column Address
地址傳送順序是Column Address,PageAddress,Block Address。
由于地址只能在I/O[7:0]上傳遞,因此,必須采用移位的方式進(jìn)行。例如,對(duì)于512Mbit x8 的NANDflash,地址范圍是0~0x3FF_FFFF,只要是這個(gè)范圍內(nèi)的數(shù)值表示的地址都是有效的。以NAND_ADDR 為例: 第1步是傳遞column address,就是NAND_ADDR[7:0],不需移位即可傳遞到I/O[7:0]上,而halfpage pointer即bit8 是由操作指令決定的,即指令決定在哪個(gè)halfpage 上進(jìn)行讀寫。而真正的bit8 的值是don't care 的。 第2步就是將NAND_ADDR 右移9 位,將NAND_ADDR[16:9]傳到I/O[7:0]上 第3步將NAND_ADDR[24:17]放到I/O 上 第4 步需要將NAND_ADDR[25]放到I/O 上 因此,整個(gè)地址傳遞過程需要4步才能完成,即4-step addressing。 如果NAND Flash 的容量是256Mbit 以下,那么,block adress最高位只到bit24,因此尋址 只需要3 步。 下面,就x16 的NAND flash 器件稍微進(jìn)行一下說明。 由于一個(gè)page 的mainarea 的容量為256word,仍相當(dāng)于512byte。但是,這個(gè)時(shí)候沒有所謂 的1st halfpage 和2nd halfpage之分了,所以,bit8就變得沒有意義了,也就是這個(gè)時(shí)候 bit8 完全不用管,地址傳遞仍然和x8 器件相同。除了,這一點(diǎn)之外,x16的NAND使用方法和 x8 的使用方法完全相同。
正如硬盤的盤片被分為磁道,每個(gè)磁道又分為若干扇區(qū),一塊nandflash也分為若干block,每個(gè)block分為如干page。一般而言,block、page之間的關(guān)系隨著芯片的不同而不同,典型的分配是這樣的:
1block = 32page
1page = 512bytes(datafield) + 16bytes(oob)
需要注意的是,對(duì)于flash的讀寫都是以一個(gè)page開始的,但是在讀寫之前必須進(jìn)行flash的擦寫,而擦寫則是以一個(gè)block為單位的。同時(shí)必須提醒的是,512bytes理論上被分為1st half 和2sdhalf,每個(gè)half各占256個(gè)字節(jié)。
我們討論的K9F1208U0B總共有4096個(gè)Blocks,故我們可以知道這塊flash的容量為4096 *(32 *528)= 69206016 Bytes = 66 MB 但事實(shí)上每個(gè)Page上的最后16Bytes是用于存貯檢驗(yàn)碼和其他信息用的,并不能存放實(shí)際的數(shù)據(jù),所以實(shí)際上我們可以操作的芯片容量為4096 *(32 *512) = 67108864 Bytes = 64 MB由上圖所示,1個(gè)Page總共由528 Bytes組成,這528個(gè)字節(jié)按順序由上而下以列為單位進(jìn)行排列(1列代表一個(gè)Byte。第0行為第0 Byte,第1行為第1Byte,以此類推,每個(gè)行又由8個(gè)位組成,每個(gè)位表示1個(gè)Byte里面的1bit)。這528Bytes按功能分為兩大部分,分別是DataField和Spare Field,其中SpareField占528Bytes里的16Bytes,這16Bytes是用于在讀寫操作的時(shí)候存放校驗(yàn)碼用的,一般不用做普通數(shù)據(jù)的存儲(chǔ)區(qū),除去這16Bytes,剩下的512Bytes便是我們用于存放數(shù)據(jù)用的DataField,所以一個(gè)Page上雖然有528個(gè)Bytes,但我們只按512Bytes進(jìn)行容量的計(jì)算。
讀命令有兩個(gè),分別是 Read1,Read2其中Read1用于讀取Data Field的數(shù)據(jù),而Read2則是用于讀取SpareField的數(shù)據(jù)。對(duì)于NandFlash來說,讀操作的最小操作單位為Page,也就是說當(dāng)我們給定了讀取的起始位置后,讀操作將從該位置開始,連續(xù)讀取到本Page的最后一個(gè)Byte為止(可以包括Spare Field)
Nand Flash的尋址
Nand Flash的地址寄存器把一個(gè)完整的Nand Flash地址分解成Column Address與Page Address.進(jìn)行尋址。
ColumnAddress: 列地址。Column Address其實(shí)就是指定Page上的某個(gè)Byte,指定這個(gè)Byte其實(shí)也就是指定此頁的讀寫起始地址。
PaageAddress:頁地址。由于頁地址總是以512Bytes對(duì)齊的,所以它的低9位總是0。確定讀寫操作是在Flash上的哪個(gè)頁進(jìn)行的。
Read1命令
當(dāng)我們得到一個(gè)Nand Flash地址src_addr時(shí)我們可以這樣分解出Column Address和PageAddress
column_addr=src_addr%512; // column address
page_address=(src_addr>>9); // page address
也可以這么認(rèn)為,一個(gè)NandFlash地址的A0~A7是它的column_addr,A9~A25是它的PageAddress。(注意地址位A8并沒有出現(xiàn),也就是A8被忽略,在下面你將了解到這是什么原因)
Read1命令的操作分為4個(gè)Cycle,發(fā)送完讀命令00h或01h(00h與01h的區(qū)別請(qǐng)見下文描述)之后將分4個(gè)Cycle發(fā)送參數(shù),1st.Cycle是發(fā)送Column Address。2nd.Cycle ,3rd.Cycle和4th.Cycle則是指定PageAddress(每次向地址寄存器發(fā)送的數(shù)據(jù)只能是8位,所以17位的Page Address必須分成3次進(jìn)行發(fā)送
Read1的命令里面出現(xiàn)了兩個(gè)命令選項(xiàng),分別是00h和01h。這里出現(xiàn)了兩個(gè)讀命是否令你意識(shí)到什么呢?是的,00h是用于讀寫1sthalf的命令,而01h是用于讀取2ndhalf的命令?,F(xiàn)在我可以結(jié)合上圖給你說明為什么K9F1208U0B的DataField被分為2個(gè)half了。
如上文我所提及的,Read1的1st.Cycle是發(fā)送Column Address,假設(shè)我現(xiàn)在指定的ColumnAddress是0,那么讀操作將從此頁的第0號(hào)Byte開始一直讀取到此頁的最后一個(gè)Byte(包括SpareField),如果我指定的Column Address是127,情況也與前面一樣,但不知道你發(fā)現(xiàn)沒有,用于傳遞ColumnAddress的數(shù)據(jù)線有8條(I/O0~I/O7,對(duì)應(yīng)A0~A7,這也是A8為什么不出現(xiàn)在我們傳遞的地址位中),也就是說我們能夠指定的ColumnAddress范圍為0~255,但不要忘了,1個(gè)Page的DataField是由512個(gè)Byte組成的,假設(shè)現(xiàn)在我要指定讀命令從第256個(gè)字節(jié)處開始讀取此頁,那將會(huì)發(fā)生什么情景?我必須把Column Address設(shè)置為256,但ColumnAddress最大只能是255,這就造成數(shù)據(jù)溢出。。。正是因?yàn)檫@個(gè)原因我們才把Data Field分為兩個(gè)半?yún)^(qū),當(dāng)要讀取的起始地址(ColumnAddress)在0~255內(nèi)時(shí)我們用00h命令,當(dāng)讀取的起始地址是在256~511時(shí),則使用01h命令.假設(shè)現(xiàn)在我要指定從第256個(gè)byte開始讀取此頁,那么我將這樣發(fā)送命令串
column_addr=256;
NF_CMD=0x01; ? 從2nd half開始讀取
NF_ADDR=column_addr&0xff; 1st Cycle
NF_ADDR=page_address&0xff; 2nd.Cycle
NF_ADDR=(page_address>>8)&0xff; 3rd.Cycle
NF_ADDR=(page_address>>16)&0xff; 4th.Cycle
其中NF_CMD和NF_ADDR分別是NandFlash的命令寄存器和地址寄存器的地址解引用,我一般這樣定義它們,
#define rNFCMD (*(volatile unsigned char*)0x4e000004) //NADD Flash command
#define rNFADDR (*(volatile unsigned char *)0x4e000008) //NAND Flash address
事實(shí)上,當(dāng)NF_CMD=0x01時(shí),地址寄存器中的第8位(A8)將被設(shè)置為1(如上文分析,A8位不在我們傳遞的地址中,這個(gè)位其實(shí)就是硬件電路根據(jù)01h或是00h這兩個(gè)命令來置高位或是置低位),這樣我們傳遞column_addr的值256隨然由于數(shù)據(jù)溢出變?yōu)?,但A8位已經(jīng)由于NF_CMD=0x01的關(guān)系被置為1了,所以我們傳到地址寄存器里的值變成了
A0 A1 A2 A3 A4 A5 A6 A7 A8
1 0 0 0 0 0 0 0 1
這8個(gè)位所表示的正好是256,這樣讀操作將從此頁的第256號(hào)byte(2nd half的第0號(hào)byte)開始讀取數(shù)據(jù)。nand_flash.c中包含3個(gè)函數(shù)
void nf_reset(void);
voidnf_init(void);
void nf_read(unsigned intsrc_addr,unsigned char *desc_addr,int size);
nf_reset()將被nf_init()調(diào)用。nf_init()是nand_flash的初始化函數(shù),在對(duì)nandflash進(jìn)行任何操作之前,nf_init()必須被調(diào)用。
nf_read(unsigned intsrc_addr,unsigned char *desc_addr,int size);為讀函數(shù),src_addr是nandflash上的地址,desc_addr是內(nèi)存地址,size是讀取文件的長(zhǎng)度。
在nf_reset和nf_read函數(shù)中存在兩個(gè)宏
NF_nFCE_L();
NF_nFCE_H();
你可以看到當(dāng)每次對(duì)NandFlash進(jìn)行操作之前NF_nFCE_L()必定被調(diào)用,操作結(jié)束之時(shí)NF_nFCE_H()必定被調(diào)用。這兩個(gè)宏用于啟動(dòng)和關(guān)閉Flash芯片的工作(片選/取消片選)。至于nf_reset()中的
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
這一行代碼是對(duì)NandFlash的控制寄存器進(jìn)行初始化配置,rNFCONF是NandFlash的配置寄存器,各個(gè)位的具體功能請(qǐng)參閱s3c2410數(shù)據(jù)手冊(cè)。
現(xiàn)在舉一個(gè)例子,假設(shè)我要從NandFlash中的第5000字節(jié)處開始讀取1024個(gè)字節(jié)到內(nèi)存的0x30000000處,我們這樣調(diào)用read函數(shù)
nf_read(5000,0x30000000,1024);
我們來分析5000這個(gè)src_addr.
根據(jù)
column_addr=src_addr%512;
page_address=(src_addr>>9);
我們可得出column_addr=5000%512=392
page_address=(5000>>9)=9
于是我們可以知道5000這個(gè)地址是在第9頁的第392個(gè)字節(jié)處,于是我們的nf_read函數(shù)將這樣發(fā)送命令和參數(shù)
column_addr=5000%512;
>page_address=(5000>>9);
NF_CMD=0x01; 從2nd half開始讀取
NF_ADDR= column_addr&0xff; 1st Cycle
NF_ADDR=page_address&0xff; 2nd.Cycle
NF_ADDR=(page_address>>8)&0xff; 3rd.Cycle
NF_ADDR=(page_address>>16)&0xff; 4th.Cycle
向NandFlash的命令寄存器和地址寄存器發(fā)送完以上命令和參數(shù)之后,我們就可以從rNFDATA寄存器(NandFlash數(shù)據(jù)寄存器)讀取數(shù)據(jù)了.
我用下面的代碼進(jìn)行數(shù)據(jù)的讀取.
for(i=column_addr;i<512;i++)
{
*buf++=NF_RDDATA();
}
每當(dāng)讀取完一個(gè)Page之后,數(shù)據(jù)指針會(huì)落在下一個(gè)Page的0號(hào)Column(0號(hào)Byte).
下面是源代碼:
/*
www.another-prj.com
author: caiyuqing
本代碼只屬于交流學(xué)習(xí),不得用于商業(yè)開發(fā)
*/
#include"s3c2410.h"
#include "nand_flash.h"
static unsigned charseBuf[16]={0xff};
//--------------------------------------------------------------------------------------
unsignedshort nf_checkId(void)
{
int i;
unsigned short id;
NF_nFCE_L(); //chip enable
NF_CMD(0x90); //Read ID
NF_ADDR(0x0);
for(i=0;i<10;i++); //waittWB(100ns)
id="NF"_RDDATA()<<8; // Makercode(K9S1208V:0xec)
id|=NF_RDDATA(); // Devidecode(K9S1208V:0x76)
NF_nFCE_H(); //chip enable
return id;
}
//--------------------------------------------------------------------------------------
staticvoid nf_reset(void)
{
int i;
NF_nFCE_L(); //chip enable
NF_CMD(0xFF); //reset command
for(i=0;i<10;i++); //tWB = 100ns.
NF_WAITRB(); //wait 200~500us;
NF_nFCE_H(); //chip disable
}
//--------------------------------------------------------------------------------------
voidnf_init(void)
{
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
// 1 1 1 1 1 xxx rxxx, r xxx
// En r r ECCR nFCE="H" tACLS tWRPH0 tWRPH1
nf_reset();
}
//--------------------------------------------------------------------------------------
voidnf_read(unsigned int src_addr,unsigned char *desc_addr,int size)
{
int i;
unsigned int column_addr = src_addr % 512; //column address
unsigned int page_address = (src_addr >>9); // page addrress
unsigned char *buf = desc_addr;
while((unsigned int)buf < (unsigned int)(desc_addr) + size)
{
NF_nFCE_L(); // enable chip
/*NF_ADDR和NF_CMD為nand_flash的地址和命令寄存器的解引用*/
if(column_addr > 255) // 2end halft
NF_CMD(0x01); // Read2 command. cmd 0x01: Readcommand(start from 2end half page)
else
NF_CMD(0x00); // 1st halft?
NF_ADDR(column_addr & 0xff); // Column Address
NF_ADDR(page_address & 0xff); // Page Address
NF_ADDR((page_address >> 8) & 0xff); // ...
NF_ADDR((page_address >> 16) & 0xff); // ..
for(i = 0; i < 10; i++); // waittWB(100ns)/////??????
NF_WAITRB(); //Wait tR(max 12us)
// Read from main area
for(i = column_addr; i < 512; i++)
{
*buf++= NF_RDDATA();
}
NF_nFCE_H(); // disable chip
column_addr = 0;
page_address++;
}
return ;
}
聯(lián)系客服