怎么实现nand flash的整块读写
凡尘老蜀黍
2024-12-02 16:27:59
最佳回答
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 reg**ter. 翻成中文为列地址,地址的低8位page address :页地址block address :块地址对于nand flash来讲,地址和命令只能在i/o[7:0]上传递,数据宽度是8位。二、nand flash地址的表示512byte需要9bit来表示,对于528byte系列的nand,这512byte被分成1st half page reg**ter和2nd half page reg**ter,各自的访问由地址指针命令来选择,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[]传到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驱动解读以前由于做移植多一些,那些工作很简单(现在看来),从来都不用去关心驱动里面到底怎么实现的,这几次面试才发现真的是学的太浅了,似乎我还在学习仰泳而那些牛人基本都属于潜水级的了,潜的不知有多深。我对照着开发板所带的nand flash驱动和k9f1208的芯片资料把这些代码通读了一遍,终于明白了nand flash的读写过程是如何实现的了。我所参考的驱动是mizi公司为芯片所写的,我看看了,大概和官方2.4.18内核的nand.c差不多。在处理器中有专门的nand flash**,他们位于sfr区,具体可以参看用户手册。以下的这些代码均可以在vivi或者kernel里面找到,文中会标明程序出自何处。在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 void (*hwcontrol)(int cmd); void (*write_cmd)( val); void (*write_addr)( val); (*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 ……#ifdef config_mtd_nand_ecc u_char ecc_code_buf[6]; u_char reserved[2];#endif#endif };纵观对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() th**->hwcontrol(nand_ctl_setnce); \ nand_command(mtd, nand_cmd_reset, -1, -1); \ udelay (10);hwcontrol(nand_ctl_setnce)的作用是设置2410的nand flash configuration (nfconf) reg**ter的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 0 读操作nand_cmd_read1 1 读操作nand_cmd_pageprog 0x10 页编程操作nand_cmd_readoob 0x50 读写oobnand_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 oxff 复位操作按照程序的注释,可以将该函数的实现分为如下几步:1、begin command latch cycle实现代码:th**->hwcontrol(nand_ctl_setcle);th**->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 reg**ter. when active high, commands are latched into the command reg**ter through the i/o ports on the r**ing edge of the nwe signal. 看了这段英文相信对第一条语句的作用已经十分清楚了,他就是用来控制向命令寄存(command set (nfcmd) reg**ter)发送命令的。2、 write out the command to the device这部分对于不同的命令来说,操作的步骤也不太相同,如果为写操作,那么还有根据flash不同的容量决定操作步骤,具体可以参看代码。如果为其他命令,那么就是简单的一行:th**->write_cmd (command);将命令直接想到命令寄存器(nfcmd[7:0])中。3、 set ale and clear cle to start address cycle & serially input address1中已经提到了ale和cle的作用,现在开始发送地址。实现代码:th**->hwcontrol(nand_ctl_clrcle); // clear the command latch enableth**->hwcontrol(nand_ctl_setale); // set the address latch enable然后按位操作,是用函数write_addr()将地址写到nand flash address set (nfaddr) reg**ter中。4、 latch in address实现代码:th**->hwcontrol(nand_ctl_clrale);th**->hwcontrol(nand_ctl_dat_in);地址发送完毕,清楚ale。5、 pause for 15us我使用的vivi中,使用udelay (15)延时15us,但这个时间会因nand flash的不同而不同。三、operation根据函数的不同,操作部分会不一样,但是主要的是对nand flash data (nfdata) reg**ter的操作,或写(编程)或者读。通过读或写函数的参数来返回或传递读出的值或写入的值。写得操作通常比较麻烦,他要将写到flash的内容重新读出后进行ecc校验,如果数据正确则在重新真正的写(编程),如果错误,则将数据写入flash的另一个块。读和写都是以页为单位进行操作。而擦除则以块为单位,三个周期发送完地址。擦除完毕后同样需要进行检察以确定是否擦除成功。四、de-select the nand device实现代码:#define nand_deselect() th**->hwcontrol(nand_ctl_clrnce);反选flash吧,不知这样叫正确与否,跟select the nand device相反,亦即使用完后将使能flash位清0,代码是nfconf位于0x4e00_0000的位置(nfconf |= nfconf_nfce_high;),有兴趣的可以读读代码,看看这是怎么实现的,我的感觉就是关于寄存器的清置读起来都比较晕。基于三星k8f2g08u0m存储芯片的读写操作,如命令、地址、数据的读写时序,读芯片id、页读、页写、随机读、随机写,坏块的检测、标记及处理。(1)写命令子函数 void write_command(unsigned char com) { flash_cle = 1; flash_ale = 0; flash_we = 0; xbyte[xp] = com; flash_we = 1; }(2)写地址子函数 void write_address(unsigned char addr) { flash_cle = 0; flash_ale = 1; flash_we = 0; xbyte[xp] = addr; flash_we = 1; }(3)写数据子函数 void write_data(unsigned char dat) { flash_cle = 0; flash_ale = 0; flash_re = 1; flash_we = 0; xbyte[xp] = dat; flash_we = 1; }(4)读数据子函数 unsigned char read_data(void) { unsigned char dat; flash_cle = 0; flash_ale = 0; flash_we = 1; flash_re = 0; dat = xbyte[xp]; flash_re = 1; return dat; }(5) 读id函数 flash_cen = 0; //读id write_command(0x90); write_address(0x00); aa1 = read_data(); //0xec aa2 = read_data(); //0xda aa3 = read_data(); //无所谓 aa4 = read_data(); flash_cen = 1; 20210311