嵌入式软件工程师&&太极拳
全部博文(548)
分类: 嵌入式
2011-12-15 14:24:22
S3C2410处理器集成了8位NandFlash控制器。目前市场上常见的8位NandFlash有三星公司的k9f1208、k9f1g08、k9f2g08等。k9f1208、k9f1g08、k9f2g08的数据页大小分别为512Byte、2kByte、2kByte。它们在寻址方式上有一定差异,所以程序代码并不通用。本文以S3C2410处理器和k9f1208系统为例,讲述NandFlash的读写方法。
NandFlash的数据是以bit 的方式保存在memory cell里的,一般来说,一个cell 中只能存储一个bit,这些cell 以8 个或者16 个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device 的位宽。这些Line 组成Page, page 再组织形成一个Block。k9f1208的相关数据如下:
1block=32page;1page=528byte=512byte(Main Area)+16byte(Spare Area)。
总容量为=4096(block数量)*32(page/block)*512(byte/page)=64Mbyte
NandFlash以页为单位读写数据,而以块为单位擦除数据。按照k9f1208的组织方式可以分四类地址: Column Address、halfpage pointer、Page Address 、Block Address。
A[0:25]表示数据在64M空间中的地址。
Column Address表示数据在半页中的地址,大小范围0~255,用A[0:7]表示;
halfpage pointer表示半页在整页中的位置,即在0~255空间还是在256~511空间,用A[8]表示;
Page Address表示页在块中的地址,大小范围0~31,用A[9:13]表示;
Block Address表示块在flash中的位置,大小范围0~4095,A[14:25] 表示;
|
二、读操作过程
K9f1208的寻址分为4个cycle。分别是:A[0:7]、A[9:16]、A[17:24]、A[25]。
读操作的过程为:
1、发送读取指令;
2、发送第1个cycle地址;
3、发送第2个cycle地址;
4、发送第3个cycle地址;
5、发送第4个cycle地址;
6、读取数据至页末。
K9f1208提供了两个读指令,‘0x00’、‘0x01’。这两个指令区别在于‘0x00’可以将A[8]置为0,选中上半页;而‘0x01’可以将A[8]置为1,选中下半页。
虽然读写过程可以不从页边界开始,但在正式场合下还是建议从页边界开始读写至页结束。下面通过分析读取页的代码,阐述读过程。
static void ReadPage(U32 addr, U8 *buf) //addr表示flash中的第几页,即‘flash地址>>9’ { U16 i; NFChipEn(); //使能NandFlash WrNFCmd(READCMD0); //发送读指令‘0x00’,由于是整页读取,所以选用指令‘0x00’ WrNFAddr(0); //写地址的第1个cycle,即Column Address,由于是整页读取所以取0 WrNFAddr(addr); //写地址的第2个cycle,即A[9:16] WrNFAddr(addr>>8); //写地址的第3个cycle,即A[17:24] WrNFAddr(addr>>16); //写地址的第4个cycle,即A[25]。 WaitNFBusy(); //等待系统不忙 for(i=0; i<512; i++) buf[i] = RdNFDat(); //循环读出1页数据 NFChipDs(); //释放NandFlash }
|
三、写操作过程
写操作的过程为:
1、发送写开始指令;
2、发送第1个cycle地址;
3、发送第2个cycle地址;
4、发送第3个cycle地址;
5、发送第4个cycle地址;
6、写入数据至页末;
7、发送写结束指令
下面通过分析写入页的代码,阐述读写过程。
static void WritePage(U32 addr, U8 *buf) //addr表示flash中的第几页,即‘flash地址>>9’ { U32 i; NFChipEn(); //使能NandFlash WrNFCmd(PROGCMD0); //发送写开始指令’0x80’ WrNFAddr(0); //写地址的第1个cycle WrNFAddr(addr); //写地址的第2个cycle WrNFAddr(addr>>8); //写地址的第3个cycle WrNFAddr(addr>>16); 写地址的第4个cycle WaitNFBusy(); //等待系统不忙 for(i=0; i<512; i++) WrNFDat(buf[i]); //循环写入1页数据 WrNFCmd(PROGCMD1); //发送写结束指令’0x10’ NFChipDs(); //释放NandFlash }
|
四、总结
本文以S3C2410处理器和k9f1208系统为例讲述了nand flash的读写过程。在读写过程中没有考虑到坏块问题,有关ecc及坏块处理问题将在下个专题中讲述。
我的板子上使用的是SAMSUNG的K9F1208U0B,下面我将对此型号的NandFlash读取操作做一个讲解。
首先我们先从物理结构上来了解这颗芯片,结构图如下所示
正如硬盘的盘片被分为磁道,每个磁道又被分为若干扇区,一块Nand Flash被分为若干Block,每个Block又被分为若干Page。
由上图我们可以知道flash中Byte(字节),Page(页),Block(块)3个单位之间的关系为
1 Page =512 Bytes Data Field+ 16 Bytes Spare Field
1 Blcok="32" Pages
我们讨论的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
1. Spare Field占528Bytes里的16Bytes,这16Bytes是用于在读写操作的时候存放校验码用的,一般不用做普通数据的存储区
2. 512Bytes便是我们用于存放数据用的Data Field,(计算标准)
3. Data Field按位置关系又可分为两个部分,分别称为1st half与2nd half,每个half各占256个bytes。
对K9F1208U0B的操作是通过向Nand Flash命令寄存器(对于s3c2410来说此寄存器为NFCMD,内存映射地址为0x4e000004)发送命令队列进行的,为什么说是命令队列?就是因为要完成某个操作的时候发送的不是一条命令,而是连续几条命令或是一条命令加几个参数
下面是K9F1208U0B的操作命令集:
读命令有两个,分别是 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次进行发送)。
4个Cycle
Data Field被分为1st half 和2end half两个部分
00h是用于读写1st half的命令
01h是用于读取2nd 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)开始读取数据。
|
Read2
Read2则是指定读取Spare Field的内容
Read1和Read2都是读命令,区别相当于对一个读指针进行不同区域的定位。
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()调用。
1. nf_init()
为nand_flash的初始化函数,在对nand flash进行任何操作之前,nf_init()必须被调用。
2. nf_read(unsigned int src_addr, unsigned char *desc_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处
1. 调用nf_read函数:nf_read(5000, 0x30000000,1024);
2. 分析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函数将这样发送命令和参数
3. 操作方式:向NandFlash的命令寄存器和地址寄存器发送
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
4. 从rNFDATA寄存器(NandFlash数据寄存器)读取数据了.
我用下面的代码进行数据的读取.
for(i = column_addr; I < 512; i++)
{
*buf++=NF_RDDATA();
}
每当读取完一个Page之后, 数据指针会落在下一个Page的0号Column(0号Byte).
Fisrt part :
NAND flash和NOR 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 和 NOR flash的区别》
Second part:
NAND Flash结构与驱动分析
一、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),每页528Bytes(512byte(Main Area)+16byte(Spare Area)),每32个page形成一个Block(32*528B)。具体一片flash上有多少个Block视需要所定。我所使用的三星k9f1208U0M具有4096个block,故总容量为4096*(32*528B)=66MB,但是其中的2MB是用来保存ECC校验码等额外数据的,故实际中可使用的为64MB。
NAND flash以页为单位读写数据,而以块为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址:
Column Address:Starting Address of the Register. 翻成中文为列地址,地址的低8位
Page Address :页地址
Block Address :块地址
对于NAND Flash来讲,地址和命令只能在I/O[7:0]上传递,数据宽度是8位。
二、NAND Flash地址的表示
512byte需要9bit来表示,对于528byte系列的NAND,这512byte被分成1st half Page Register和2nd half Page Register,各自的访问由地址指针命令来选择,A[7:0]就是所谓的column address(列地址),在进行擦除操作时不需要它,why?因为以块为单位擦除。32个page需要5bit来表示,占用A[13:9],即该page在块内的相对地址。A8这一位地址被用来设置512byte的1st half page还是2nd half page,0表示1st,1表示2nd。Block的地址是由A14以上的bit来表示。
例如64MB(512Mb)的NAND flash(实际中由于存在spare area,故都大于这个值),共4096block,因此,需要12个bit来表示,即A[25:14],如果是128MB(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即A8 是由操作指令决定的,即指令决定在哪个halfpage 上进行读写,而真正的A8 的值是不需程序员关心的。
第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 的容量是32MB(256Mbit)以下,那么,block adress最高位只到bit24,因此寻址只需要3步。
下面,就x16 的NAND flash 器件稍微进行一下说明。 由于一个page 的main area 的容量为256word,仍相当于512byte。但是,这个时候没有所谓的1st halfpage 和2nd halfpage 之分了,所以,bit8就变得没有意义了,也就是这个时候 A8 完全不用管,地址传递仍然和x8 器件相同。除了,这一点之外,x16 的NAND使用方法和 x8 的使用方法完全相同。
三、NAND flash驱动解读
mizi公司为三星芯片所写的和官方2.4.18内核的nand.c差不多。
在s3c2410处理器中有专门的NAND flash控制器,他们位于SFR区,具体可以参看s3c2410用户手册。
在vivi中,有关NAND flash的驱动都在driver/mtd/nand/下,该目录中包含的源文件:smc_core.c是NAND flash的主要驱动。
NAND flash 芯片定义了一个很长的结构,这个结构中包含了操作NAND flash的函数和一些必要的变量(include/mtd/nand.h)。
struct nand_chip
{
#ifdef CONFIG_MTD_NANDY /* =y */
void (*hwcontrol)(int cmd);
void (*write_cmd)(u_char val);
void (*write_addr)(u_char val);
u_char (*read_data)(void);
void (*write_data)(u_char val);
void (*wait_for_ready)(void);
int page_shift;
u_char *data_buf;
u_char *data_cache;
int cache_page;
struct nand_smc_dev *dev;
u_char spare[SMC_OOB_SIZE];
#else /* CONFIG_MTD_NANDY */
……
#ifdef CONFIG_MTD_NAND_ECC
u_char ecc_code_buf[6];
u_char reserved[2];
#endif
#endif /* CONFIG_MTD_NANDY */
};
纵观对NAND flash的各种操作(read、write、erase),无外乎如下几种操作:
1.选择flash nand_select()
2.发送命令 nand_command()
3.进行相应操作 read,write……
4.反选NAND flash nand_deselect()
下面是以上四步的实现代码:
1、选择NAND flash
#define nand_select()
this->hwcontrol(NAND_CTL_SETNCE); \
nand_command(mtd, NAND_CMD_RESET, -1, -1); \
udelay (10);
hwcontrol(NAND_CTL_SETNCE)的作用是设置2410的NAND FLASH CONFIGURATION (NFCONF) REGISTER的NAND Flash Memory chip enable位为0,这位寄存器在自动重启后就被系统自动清零。如果要访问NAND flash的内存,这位必须置1。
nand_command(mtd, NAND_CMD_RESET, -1, -1);向flash发送命令,此命令为reset,即为重置NAND flash。
然后是10us的延迟,给flash个反应时间。
2、发送命令
Nand_command()同样在smc_core.c中实现。NAND flash的命令有如下几种:
命令 命令值 描述
NAND_CMD_READ0 0x0 读操作
NAND_CMD_READ1 0x1 读操作
NAND_CMD_PAGEPROG 0x10 页编程操作
NAND_CMD_READOOB 0x50 读写OOB
NAND_CMD_ERASE1 0x60 读写操作
NAND_CMD_STATUS 0x70 读取状态
NAND_CMD_STATUS_MULTI 0x71 读取状态
NAND_CMD_SEQIN 0x80 写操作
NAND_CMD_READID 0x90 读Flash ID号
NAND_CMD_ERASE2 0xd0 擦写操作
NAND_CMD_RESET 0xff 复位操作
按照程序的注释,可以将该函数的实现分为如下几步:
1、Begin command latch cycle
实现代码:
this->hwcontrol(NAND_CTL_SETCLE);
this->hwcontrol(NAND_CTL_DAT_OUT);
找到第二条语句的定义,发现什么都没做,不解!!希望达人解答。我猜想可能是一个数据读出的使能操作,允许数据读出。
Command Latch Enable(CLE) and Address Latch Enable(ALE) are used to multiplex command and address respectively, via the I/O pins. The CLE input controls the path activation for commands sent to the command register. When active high, commands are latched into the command register through the I/O ports on the rising edge of the nWE signal. 看了这段英文相信对第一条语句的作用已经十分清楚了,他就是用来控制向命令寄存(COMMAND SET (NFCMD) REGISTER)发送命令的。
2、 Write out the command to the device
这部分对于不同的命令来说,操作的步骤也不太相同,如果为写操作,那么还有根据flash不同的容量决定操作步骤,具体可以参看代码。如果为其他命令,那么就是简单的一行:
this->write_cmd (command);
将命令直接想到命令寄存器(NFCMD[7:0])中。
3、 Set ALE and clear CLE to start address cycle & Serially input address
1中已经提到了ALE和CLE的作用,现在开始发送地址。
实现代码:
this->hwcontrol(NAND_CTL_CLRCLE); // clear the command latch enable
this->hwcontrol(NAND_CTL_SETALE); // set the address latch enable
然后按位操作,是用函数write_addr()将地址写到NAND FLASH ADDRESS SET (NFADDR) REGISTER中。
4、 Latch in address
实现代码:
this->hwcontrol(NAND_CTL_CLRALE);
this->hwcontrol(NAND_CTL_DAT_IN);
地址发送完毕,清楚ALE。
5、 Pause for 15us
我使用的VIVI中,使用udelay (15)延时15us,但这个时间会因NAND Flash的不同而不同。
三、Operation
根据函数的不同,操作部分会不一样,但是主要的是对NAND FLASH DATA (NFDATA) REGISTER的操作,或写(编程)或者读。通过读或写函数的参数来返回或传递读出的值或写入的值。
写操作通常比较麻烦,他要将写到flash的内容重新读出后进行ECC校验,如果数据正确则在重新真正的写(编程),如果错误,则将数据写入flash的另一个块。读和写都是以页为单位进行操作。而擦除则以块为单位,三个周期发送完地址。擦除完毕后同样需要进行检察以确定是否擦除成功。
四、De-select the NAND device
实现代码:
#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);
反选flash吧,不知这样叫正确与否,跟select the NAND device相反,亦即使用完后将使能flash位清0,代码是NFCONF位于0x4e00_0000的位置(NFCONF |= NFCONF_nFCE_HIGH;),有兴趣的可以读读代码,看看这是怎么实现的,我的感觉就是关于寄存器的清置读起来都比较晕。
好了,到此flash操作的框架基本完成。
本文来自:我爱研发网(52RD.com) - R&D大本营
详细出处: