Intel于1988年首先开发出NOR Flash 技术,彻底改变了原先由EPROM(Erasable Programmable Read-Only-Memory电可编程序只读存储器)和EEPROM(电可擦只读存储器Electrically Erasable Programmable Read - Only Memory)一统天下的局面。紧接着,1989年,东芝公司发表了NAND Flash 结构,强调降低每比特的成本,有更高的性能,并且像磁盘一样可以通过接口轻松升级。NOR Flash 的特点是芯片内执行(XIP,eXecute In Place),这样应用程序可以直接在Flash闪存内运行,不必再把代码读到系统RAM中。
他们的区别如下。
NAND Flash | NOR Flash | |
---|---|---|
读速度 | ↓ | ↑ |
写速度 | ↑↑↑ | ↓↓↓ |
擦写速度/次数 | ↑↑ | ↓↓ |
擦写电路 | ↓以块为擦写单位 | ↑ |
易用性 | ↓↓ | ↑↑ |
体型/价格 | ↓↓ | ↑↑ |
接口 | 共用地址数据线 | 带SRAM接口,有足够的地址引脚来寻址,地址数据线独立,可直接寻址 |
位反转/坏块处理 | 坏块较多且随机分布,采用错误探测/错误更正(EDC/ECC)算法 | 坏块少,采用冗余比特替换法 |
浮栅充电 | F-N隧道效应(Fowler Nordheim tunneling) | 热电子注入(hot electron injection) |
特点 | 能提供极高的单元密度,可以达到高存储密度,并且写入和擦除的速度也很快。应用NAND的困难在于flash的管理需要特殊的系统接口 | 可在芯片内执行代码,传输效率很高,在1~4MB的小容量时具有很高的成本效益,但是很低的写入和擦除速度大大影响了它的性能 |
基本单元 | 读操作-页page 擦写操作-块block | 读写操作-扇区sector |
NAND Flash和NOR Flash的区别中有三点很关键:
1、首先,NAND Flash在出厂时就有一定的坏块,而且是随机分布,这是由NANDFlash的工艺造成的,以前也曾有过消除坏块的努力,但发现成品率太低,代价太高,根本不划算。所以,NAND Flash被允许在出厂时有很少量的坏块。
2、其次,NAND Flash在应用中会出现位翻转错误(bit failture)。虽然概率很低,大约每100亿次写操作才会出现一次,但是对于用作系统根文件系统的存储设备,这种错误是绝对不允许的。因为这种错误万一发生在系统配置文件上,则会影响系统的运行。但是作为流媒体文件的存储器,位翻转的问题并不大。
3、最后,NAND Flash和NORFlash在应用接口上有着本质的区别,NAND Flash是I/O方式,NOR Flash是总线方式,这是它们除了价格以外最主要的差别。
想要实现对NAND FLASH和NOR FLASH等存储器实现读写操作,可以采用STM32的FSMC(Flexible Static Memory Controller)可变静态存储控制器,它会自动对时序等操作进行处理,使用起来会比较方便。当然有的存储器支持SPI接口进行数据的传输,而我选择的NAND FLASH W25N01GVZEIG(数据手册在下载文件那里)支持Standard/DUAL/QUAD SPI。
以下是他们的引脚信息。
传输模式 | 引脚 |
---|---|
Standard SPI | CLK、/CS、DI、DO、/WP、/Hold |
DUAL SPI | CLK、/CS、IO0、IO1、/WP、/Hold |
QUAD SPI | CLK、/CS、IO0、IO1、IO2、IO3 |
接口说明:
CLK(Serial Clock):时钟线
/CS(Clip Select):片选线
DI(Serial Data Input(Data Input Output 0)):串行数据输入线
DO(Serial Data Output(Data Input Output 1)):串行数据输出线
/WP(Write Protect Input (Data Input Output 2)):写数据保护线
/Hold(Hold Input (Data Input Output 3)):数据输入保持线
我使用的是标准SPI方式进行数据的传输(配置方法)
从上面两图可以看出,它的列地址大小为2112B,由2048B的数据缓冲区和64B的备用区域(Spare Area)组成。它的行地址大小1024*64*2KB,即1024个区块(Block),每个区块包含64页(Page),每页大小为2KB。
由于NAND Flash在设定上存在坏块(曾经想要消灭坏块,但由于成品率太低所以就这样了),所以就有Spare Area,它被用于标记坏块(bad block)和保存对MainArray中main区的ECC码(Error Checking and Correcting,纠错码)。
下面是设备操作流程,其中BUF、ECC-E的状态会影响选择哪种读取数据的方式Continuous Read或是Buffer Read,这在后面的指令码选择会有用。
我们可以通过修改状态寄存器2来改变BUF和ECC-E的状态。
由于我使用的是W25N01GVZEIG即xxIG,按照默认方式的话就是如下的指令码。
根据时序我们编写读取芯片ID的顺序大致如下:
1、/CS拉低
2、发送指令0x9F
3、发送无用指令(0x00)占用8个虚拟时钟(dummy clock)
4、然后接收ID的高8位(temp|=data<<16、EFh为Winbond的ID)
5、再接收ID的中8位(temp|=data<<8)
6、最后接收ID的低8位(temp|=data,AA21h为设备的ID)
7、/CS拉高
作用:复位设备
指令码:0xFF
输入参数:无
返回值:无
作用:获取芯片的制造商和设备ID
指令码:0x9F
输入参数:无
返回值:制造商ID0xEF,设备ID0xAA21
作用:读状态寄存器对应地址的数据
指令码:0x0f/0x05
输入参数:8位状态寄存器地址SR_Address
返回值:8位状态寄存器值
作用:写状态寄存器对应地址的数据
指令码:0x1f/0x01
输入参数:8位状态寄存器地址SR_Address、8位写入对应地址的值
返回值:无
作用:写使能
指令码:0x06
输入参数:无
返回值:无
作用:写失能
指令码:0x04
输入参数:无
返回值:无
作用:坏块管理
指令码:0xA1
输入参数:16位逻辑块地址LBA(Logical Block Address)和16位物理块地址PBA(
Physical Block Address)
返回值:无
备注:在执行之前要执行写使能
作用:读取坏块管理查阅表
指令码:0xA5
输入参数:无
返回值:依次输出LUT中逻辑块地址和物理块地址
(All block address links will be output sequentially starting from the first link (LBA0 & PBA0) in the LUT(Look Up Table))
作用:输出上一个ECC校验错误的页地址
指令码:0xA9
输入参数:无
返回值:16位上一个ECC校验错误的页地址
作用:擦写指定块(64页,128KB)内所有内存设置为1(FFh)
指令码:0xD8
输入参数:16位页地址
返回值:无
备注:在执行之前要执行写使能
作用:将程序数据加载到数据缓冲器,然后发出程序执行指令,将数据从数据缓冲区传输到指定的内存页。
指令码:0x02
输入参数:16位列地址CA[15:0](其中只有CA[11:0]有效)、至少一字节的数据(1~2111Byte)
返回值:无
备注:
执行该条指令之前要先执行写使能操作使状态寄存器的WEL位置1。
如果启用内部ECC算法,所有2,112字节的数据都将被接受,但是额外的64字节部分中为ECC奇偶校验位指定的字节将被ECC计算覆盖。如果将ECC- e位设置为0以禁用内部ECC,则额外的64字节部分可用于外部ECC或其他用途。
它会重置未使用的数据缓冲区中的数据字节(FFh)
作用:同上
指令码:0x84
输入参数:同上
返回值:无
备注:部分同上,最后一句不同
只会更新数据字节命令指定的输入序列,其余的数据缓冲区将保持不变
作用:将从所有四个IO引脚输入数据字节,而不是单一数据输入引脚。当数据量较大时,该方法将显著减少将数据加载到数据缓冲区中的时间。
指令码:0x32
输入参数:列地址CA[15:0]
返回值:无
备注:它会重置未使用的数据缓冲区中的数据字节(FFh)
作用:同上
指令码:0x34
输入参数:同上
返回值:无
备注:只会更新数据字节命令指定的输入序列,其余的数据缓冲区将保持不变
作用:程序执行指令是程序操作的第二步。程序数据加载到2,112字节的数据缓冲区(启用ECC时为2,048字节)后,程序执行指令将把数据缓冲区内容编程到指令中指定的物理内存页
指令码:0x10
输入参数:页地址PA[15:0]
返回值:无
作用:页数据读取指令将指定内存页的数据传输到2,112字节的数据缓冲区
指令码:0x13
输入参数:页地址PA[15:0]
返回值:无
作用:读取数据指令允许在执行读取页数据指令后,从数据缓冲区中顺序读取一个或多个数据字节
指令码:0x03
输入参数:列地址CA[15:0]
返回值:返回最多2048字节的数据
作用:快速读取指令允许在执行读取页数据指令后,按顺序从数据缓冲区读取一个或多个数据字节
指令码:0x0B
输入参数:列地址CA[15:0]
返回值:返回最多2048字节的数据
1、NOR FLASH和NAND FLASH在程序的编写上真的有很大区别,前者读写的单位比较小,很容易就可以对内存进行操作,但后者读写的单位为页,擦写的单位是块。
2、在编写程序时要注意列地址(Column Address)和页地址(Page Address),我在地址编写时将它们组合在一起为方形阵列,就如前面第四章的架构所示。地址xxxx xxx前16位为页地址(0-65535),后12位则为页內偏移(0-2111,注意当ECC-E=1时只到0-2047)。下面为将上述地址转化为列地址和页地址。
#define ADDR_Col_CA15_CA8(ADD) (uint8_t)(((ADD) & 0x0F00) >> 8)
#define ADDR_Col_CA7_CA0(ADD) (uint8_t)((ADD) & 0xFF)
#define ADDR_Row_PA15_PA8(ADD) (uint8_t)(((ADD) & 0xFF00000) >> 20)
#define ADDR_Row_PA7_PA0(ADD) (uint8_t)(((ADD) & 0xFF000) >> 12)
3、使用NAND FLASH最好是使用BBM(Bad Block Management)坏块管理,因为它本来生产就不可避免地出现坏块,这样做避免数据错误和系统崩溃。另外可以采取一些策略来提高它的寿命和效率,例如是磨损均衡管理等。
1、SPI 的信号线最好加 1k 上拉电阻, 实测发现 W25N01GV 这颗芯片的驱动能力比较弱。在调试的时候这可能发生误导,因为 BUSY = 0 时表明命令执行完毕,实际上是读回来的虚假低电平。
2、Protection Register / Status Register-1 (Volatile Writable, OTP lockable) 此寄存器上电后的默认值是 0b01111100,也就是说 Flash 处于写保护状态。我们需要用 Write Status Register (1Fh/01h) 这条指令清除写保护。 否则擦写指令不起作用。
3、要注意两个型号的区别
W25N01GVxxIG, 默认 BUF = 1, 处于 Buffer Read Mode。
W25N01GVxxIT, 默认 BUF = 0,处于 Continous Read Mode。
两种模式可以用过命令切换。
4、写入数据分两步:1. 先把数据传入芯片内部的 2048 字节 RAM 缓冲区。比如通过 Quad Program Data Load (32h) 指令装载数据。 2 执行 Program Execute (10h)。如果只执行第一步,不执行第二步,数据还可以从缓冲区读出来,但是掉电后数据就没有了。
5、在执行 Block Erase,Program Execute等命令前要先执行Write Enable(06h)指令,否则指令不起作用。
参考资料:
百度百科
关于spare area的讨论
W25N01GV 驱动
NAND+Flash坏块管理算法及逻辑层驱动设计