加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

nand flash结构以及读写分析——写的比较经典

发布时间:2020-12-15 06:37:20 所属栏目:百科 来源:网络整理
导读:NAND Flash 的数据是以bit 的方式保存在memory cell,一般来说,一个cell 中只能存储一个bit。这些cell 以8 个或者16 个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device 的位宽。这些Line 会再组成Page. (Nand Flash 有多种结构,我

NAND Flash 的数据是以bit 的方式保存在memory cell,一般来说,一个cell 中只能存储一个bit。这些cell 以8 个或者16 个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device 的位宽。这些Line 会再组成Page.
(Nand Flash 有多种结构,我使用的Nand Flash 是K9F1208,下面内容针对三星的K9F1208U0M),每页528Byte,每32 个page 形成一个Block, Sizeof(block)=16kByte 。
1 block="16kbyte",512Mbit=64Mbyte,Numberof(block)=4096 1block=32page,1page=528byte=512byte(Main Area)+16byte(Spare Area)?
Nand flash 以页为单位读写数据,而以块为单位擦除数据。
按照这样的组织方式可以形成所谓的三类地址:?
--Block Address -- Page Address? --Column Address?
对于NAND Flash 来讲,地址和命令只能在I/O[7:0]上传递,数据宽度是8 位。?
512byte需要9bit来表示,对于528byte系列的NAND,这512byte被分成1st half和2nd half,各自的访问由地址指针命令来选择,A[7:0]就是所谓的column address。
32 个page 需要5bit 来表示,占用A[13:9],即该page 在块内的相对地址。Block的地址是由A14 以上的bit 来表示,例如512Mb 的NAND,共4096block,因此,需要12 个bit 来表示,即A[25:14],如果是1Gbit 的528byte/page的NAND Flash,则block address用A[26:14]表示。而page address就是blcok address|page address in block

NAND Flash 的地址表示为:?
?Block Address|Page Address in block|halfpage pointer|Column Address?
地址传送顺序是Column Address,Page Address,Block Address。?
由于地址只能在I/O[7:0]上传递,因此,必须采用移位的方式进行。 例如,对于512Mbit x8 的NAND flash,地址范围是0~0x3FF_FFFF,只要是这个范围内的数值表示的地址都是有效的。以NAND_ADDR 为例: 第1 步是传递column address,就是NAND_ADDR[7:0],不需移位即可传递到I/O[7:0]上,而halfpage pointer 即bit8 是由操作指令决定的,即指令决定在哪个halfpage 上进行读写。而真正的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 上 因此,整个地址传递过程需要4 步才能完成,即4-step addressing。 如果NAND Flash 的容量是256Mbit 以下,那么,block adress 最高位只到bit24,因此寻址 只需要3 步。 下面,就x16 的NAND flash 器件稍微进行一下说明。 由于一个page 的main area 的容量为256word,仍相当于512byte。但是,这个时候没有所谓 的1st halfpage 和2nd halfpage 之分了,所以,bit8就变得没有意义了,也就是这个时候 bit8 完全不用管,地址传递仍然和x8 器件相同。除了,这一点之外,x16 的NAND使用方法和 x8 的使用方法完全相同。

?

正如硬盘的盘片被分为磁道,每个磁道又分为若干扇区,一块nand flash也分为若干block,每个block分为如干page。一般而言,block、page之间的关系随着芯片的不同而不同,典型的分配是这样的:
1block = 32page
1page = 512bytes(datafield) + 16bytes(oob)

需要注意的是,对于flash的读写都是以一个page开始的,但是在读写之前必须进行flash的擦写,而擦写则是以一个block为单位的。同时必须提醒的是,512bytes理论上被分为1st half 和2sd half,每个half各占256个字节。

我们讨论的K9F1208U0B总共有4096 个Blocks,故我们可以知道这块flash的容量为4096 *(32 *528)= 69206016 Bytes = 66 MB? 但事实上每个Page上的最后16Bytes是用于存贮检验码和其他信息用的,并不能存放实际的数据,所以实际上我们可以操作的芯片容量为4096 *(32 *512) = 67108864 Bytes = 64 MB由 上图所示,1个Page总共由528 Bytes组成,这528个字节按顺序由上而下以列为单位进行排列(1列代表一个Byte。第0行为第0 Byte ,第1行为第1 Byte,以此类推,每个行又由8个位组成,每个位表示1个Byte里面的1bit)。这528Bytes按功能分为两大部分,分别是Data Field和Spare Field,其中Spare Field占528Bytes里的16Bytes,这16Bytes是用于在读写操作的时候存放校验码用的,一般不用做普通数据的存储区,除去这 16Bytes,剩下的512Bytes便是我们用于存放数据用的Data Field,所以一个Page上虽然有528个Bytes,但我们只按512Bytes进行容量的计算。

读 命令有两个,分别是 Read1,Read2其中Read1用于读取Data Field的数据,而Read2则是用于读取Spare Field的数据。对于Nand Flash来说,读操作的最小操作单位为Page,也就是说当我们给定了读取的起始位置后,读操作将从该位置开始,连续读取到本Page的最后一个 Byte为止(可以包括Spare Field)

Nand Flash的寻址
??? Nand Flash的地址寄存器把一个完整的Nand Flash地址分解成Column Address与Page Address.进行寻址。
Column Address: 列地址。Column Address其实就是指定Page上的某个Byte,指定这个Byte其实也就是指定此页的读写起始地址。
Page Address:页地址。由于页地址总是以512Bytes对齐的,所以它的低9位总是0。确定读写操作是在Flash上的哪个页进行的。
Read1命令
当我们得到一个Nand Flash地址src_addr时我们可以这样分解出Column Address和Page Address
column_addr=src_addr%512;? ?? ?? ?? ?? ?? ?? ?? ? // column address
page_address=(src_addr>>9);? ?? ?? ?? ?? ?? ?? ???// page address
也可以这么认为,一个Nand Flash地址的A0~A7是它的column_addr,A9~A25是它的Page Address。(注意地址位A8并没有出现,也就是A8被忽略,在下面你将了解到这是什么原因)
Read1 命令的操作分为4个Cycle,发送完读命令00h或01h(00h与01h的区别请见下文描述)之后将分4个Cycle发送参数,1st.Cycle是 发送Column Address。2nd.Cycle,3rd.Cycle和4th.Cycle则是指定Page Address(每次向地址寄存器发送的数据只能是8位,所以17位的Page Address必须分成3次进行发送
Read1的 命令里面出现了两个命令选项,分别是00h和01h。这里出现了两个读命是否令你意识到什么呢?是的,00h是用于读写1st half的命令,而01h是用于读取2nd half的命令。现在我可以结合上图给你说明为什么K9F1208U0B的DataField被分为2个half了。
如上文我所提及的,Read1的1st.Cycle是发送Column Address,假设我现在指定的Column Address是0,那么读操作将从此页的第0号Byte开始一直读取到此页的最后一个Byte(包括Spare Field),如果我指定的Column Address是127,情况也与前面一样,但不知道你发现没有,用于传递Column Address的数据线有8条(I/O0~I/O7,对应A0~A7,这也是A8为什么不出现在我们传递的地址位中),也就是说我们能够指定的 Column Address范围为0~255,但不要忘了,1个Page的DataField是由512个Byte组成的,假设现在我要指定读命令从第256个字节处 开始读取此页,那将会发生什么情景?我必须把Column Address设置为256,但Column Address最大只能是255,这就造成数据溢出。。。正是因为这个原因我们才把Data Field分为两个半区,当要读取的起始地址(Column Address)在0~255内时我们用00h命令,当读取的起始地址是在256~511时,则使用01h命令.假设现在我要指定从第256个byte开 始读取此页,那么我将这样发送命令串
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
事实上,当NF_CMD=0x01时,地址寄存器中的第8位(A8)将被设置为1(如上文分析,A8位不在我们传递的地址中,这个位其实就是硬件电路根据 01h或是00h这两个命令来置高位或是置低位),这样我们传递column_addr的值256随然由于数据溢出变为1,但A8位已经由于NF_CMD =0x01的关系被置为1了,所以我们传到地址寄存器里的值变成了

A0??A1??A2??A3??A4??A5??A6??A7??A8
1? ???0? ?? 0? ?? 0? ? ?0? ???0? ? 0? ? ?0? ?? 1?

这8个位所表示的正好是256,这样读操作将从此页的第256号byte(2nd half的第0号byte)开始读取数据。 nand_flash.c中包含3个函数
void nf_reset(void);
void nf_init(void);
void nf_read(unsigned int src_addr,unsigned??char *desc_addr,int size);

nf_reset()将被nf_init()调用。nf_init()是nand_flash的初始化函数,在对nand flash进行任何操作之前,nf_init()必须被调用。
nf_read(unsigned int src_addr,int size);为读函数,src_addr是nand flash上的地址,desc_addr是内存地址,size是读取文件的长度。
在nf_reset和nf_read函数中存在两个宏
NF_nFCE_L();
NF_nFCE_H();
你可以看到当每次对Nand Flash进行操作之前NF_nFCE_L()必定被调用,操作结束之时NF_nFCE_H()必定被调用。这两个宏用于启动和关闭Flash芯片的工作(片选/取消片选)。至于nf_reset()中的
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
这一行代码是对NandFlash的控制寄存器进行初始化配置,rNFCONF是Nand Flash的配置寄存器,各个位的具体功能请参阅s3c2410数据手册。
现在举一个例子,假设我要从Nand Flash中的第5000字节处开始读取1024个字节到内存的0x30000000处,我们这样调用read函数
nf_read(5000,0x30000000,1024);
我们来分析5000这个src_addr.
根据
column_addr=src_addr%512;? ?? ???
page_address=(src_addr>>9);? ?? ??
我们可得出column_addr=5000%512=392
page_address=(5000>>9)=9
于是我们可以知道5000这个地址是在第9页的第392个字节处,于是我们的nf_read函数将这样发送命令和参数
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的命令寄存器和地址寄存器发送完以上命令和参数之后,我们就可以从rNFDATA寄存器(NandFlash数据寄存器)读取数据了.
我用下面的代码进行数据的读取.
for(i=column_addr;i<512;i++)
{
? ?? ???*buf++=NF_RDDATA();
}
每当读取完一个Page之后,数据指针会落在下一个Page的0号Column(0号Byte).
下面是源代码:
/*
????http://www.another-prj.com/???? ??? author: caiyuqing???? ??? 本代码只属于交流学习,不得用于商业开发 */ #include "s3c2410.h" #include "nand_flash.h" static unsigned char seBuf[16]={0xff}; //-------------------------------------------------------------------------------------- unsigned short 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++);??? //wait tWB(100ns) ???? ??? id="NF"_RDDATA()<<8;??? // Maker code(K9S1208V:0xec) ??? id|=NF_RDDATA();??? // Devide code(K9S1208V:0x76) ???? ??? NF_nFCE_H();??? ??? //chip enable ??? return id; } //-------------------------------------------------------------------------------------- static void 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 } //-------------------------------------------------------------------------------------- void nf_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???? r xxx,????? r xxx???????? ??? //??? ???? En ??? r??? r?????? ECCR??? nFCE="H" tACLS?? tWRPH0????? tWRPH1 ??? nf_reset(); } //-------------------------------------------------------------------------------------- void nf_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: Read command(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++);??? ??? ??? ??? // wait tWB(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 ; }

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读