NAND_Flash控制器
《嵌入式Linux应用完全开发手册》第2篇第8章总结归纳
本章要点
- 了解NAND Flash芯片的接口
- 掌握通过NAND Flash控制器访问NAND Flash的方法
NAND Flash介绍和NAND Flash控制器使用
NAND Flash在嵌入式系统中地位与PC中的硬盘类似,用于保存系统运行所必须的操作系统、应用程序、用户数据、运行过程中产生的各类数据。与内存掉电后数据丢失不同,NAND Flash中的数据在掉电后仍可永久保存。
Flash介绍
常用的Flash类型有NOR Flash和NAND Flash两种。NOR Flash由Intel公司在1988年发明,以替代当时在市场上占据主要地位的EPROM和EEPROM。NAND Flash由Toshiba公司在1989年发明。两者主要差别如下表所示:
项目 | NOR | NAND |
---|---|---|
容量 | 1MB-32MB | 16MB-512MB |
XIP | YES | NO |
擦除性能 | 非常慢(5S) | (快)3ms |
写性能 | 慢 | 快 |
读性能 | 快 | 快 |
可靠性 | 比较高,位反转的比例小于NAND Flash的10% | 比较低,位反转比较常见,必须有校验措施。比如TNR必须有坏块管理措施 |
可擦除次数 | 10000-100000 | 100000-1000000 |
生命周期 | 低于NAND Flash的10% | 是NOR Flash的10倍以上 |
接口 | 与RAM接口相同 | I/O接口 |
访问方法 | 随机访问 | 顺序访问 |
易用性 | 容易 | 复杂 |
主要用途 | 用于保存代码和关键数据 | 用于保存数据 |
价格 | 高 | 低 |
NOR Flash支持XIP,即代码可以直接在NOR上运行,无须复制到内存中。这是由于NOR的接口与RAM完全相同,可以随机访问任意地址的数据。在NOR上进行读数据的效率非常高,但是擦除和写的效率很低。而且NOR的容量一般比较小。NAND Flash进行擦除和写操作的效率更高,并且容量更大。一般而言,NOR Flash用于存储程序,NAND Flash用于存储数据。基于NAND Flash的设备通常也要搭配NOR以存储程序。
Flash存储器件由擦除单元(块)组成,当要写某个块时,需要确保这个块已经被擦除。NOR Flash的块大小范围为64KB-128KB;NAND Flash的块大小为8KB-64KB。擦/写一个NOR Flash块需要4S,而擦/写一个NAND Flash块仅需2ms。NOR Flash的块太大,不仅增加了擦写时间,对于给定的操作,NOR Flash也需要更多的擦除操作–特别是小文件。比如一个文件只有1KB,但是为了保存它却需要擦除大小为64KB-128KB的NOR Flash块。
NOR Flash的接口与RAM完全相同,可以随意访问任意地址的数据。而NAND Flash的接口仅仅包含几个I/O引脚,需要串行访问。NAND Flash一般以512字节为单位进行读写。这使得NOR Flash更适合运行程序,NAND Flash更适合存储数据。
容量相同的情况下,NAND Flash的体积更小,对于空间有严格要求的系统,NAND Flash可以节省更多空间。市场上NOR Flash的容量通常为1MB-4MB(也有32MB的NOR Flash)。NAND Flash的容量为8MB-512MB。容量的差别也使得NOR Flash多用于存储程序,NAND Flash多用于存储数据。
基于Flash存储器件的可靠性需要考虑3点:位反转、坏块和可擦除次数。所有Flash器件都遭遇位反转问题:由于Flash固有的电气特性,在读写数据的过程中,偶尔会产生一位或几位数据错误,而NAND Flash出现的概率远大于NOR Flash。当位反转发生在关键的代码、数据时,有可能导致系统崩溃。当仅仅是报告位反转,重新读取即可;如果确实发生了位反转,则必须有相应的错误检测/恢复措施。在NAND Flash上发生位反转的概率更高,推荐使用EDC/ECC进行错误检测和恢复。NAND Flash上面会有坏块随机分布,在使用前需要将坏块扫描出来,确保不在使用它们,否则会使产品含有严重的故障。NAND Flash每块的可擦除次数通常在100000次左右。是NOR Flash的10倍。另外,因为NAND Flash的块大小通常是NOR Flash的1/8,所以NAND Flash的寿命远远超过NOR Flash。
嵌入式Linux对NOR、NAND Flash的软件支持都很成熟。在NOR Flash上常用jffs2文件系统,而在NAND Flash常用yaffs文件系统。在更底层,有MTD驱动程序实现对它们的读、写、擦除操作,它也实现了EDC/ECC校验。
NAND Flash的物理结构
以NAND Flash K9F1208U0M为例,K9F1208U0M是Samsung公司生产的容量为64MB的NAND Flash,常用于手持设备等消费电子产品。它的封装下图所示:
外部引脚如下表所示:
引脚名称 | 引脚功能 |
---|---|
I/O0-I/O7 | 数据输入/输出 |
CLE | 命令锁存使能 |
ALE | 地址锁存使能 |
CE | 芯片使能 |
RE | 读使能 |
WE | 写使能 |
WP | 写保护 |
R/B | 就绪/忙输出信号 |
Vcc | 电源 |
Vss | 地 |
N.C | 不接 |
K9F1208U0M的功能结构图:
K9F1208U0M的内部结构包含10个功能部件:
- X-Buffers Latche & Decoders:用于行地址。
- Y-Buffers Latche & Decoders:用于列地址。
- Command Register:用于命令字。
- Control Logic & High Voltage Generator:控制逻辑及产生Flash所需高压。
- Nand Flash Array:存储部件。
- Page Register & S/A:页寄存器,当读、写某页时,会将数据先读入/写入此寄存器,大小为528字节。
- Y-Gating。
- I/O Buffers & Latches。
- Global Buffers。
- Output Drivers。
Nand Flash 存储单元组织结构如下图所示:
K9F1208U0M容量为528Mbit,分为131072行(页)、528列。每一页大小为512字节,外加16字节的额外空间,这16字节额外空间的列地址为512-527。
命令、地址、数据都通过8个I/O口输入/输出,这种形式减少了芯片的引脚个数,并使得系统很容易升级到更大的容量。写入命令、地址或数据时,都需要将WE、CE信号同时拉低。
数据在WE信号的上升沿被NAND Flash锁存。命令锁存信号CLE、地址锁存信号ALE用来分辨、锁存命令或地址。K9F1208U0M的64MB存储空间需要26位地址,因此以字节为单位访问Flash时需要4个地址序列:列地址、行地址的低位部分,行地址的高位部分。读/写页发出命令之后,需要4个地址序列,而擦除块在发出擦除命令后仅需要3个地址序列。
Nand Flash的访问方法
硬件连接
NAND Flash和S3C2410/S3C2440的硬件连接图如下所示:
NAND Flash和S3C2410/S3C2440的连线较少:8个I/O引脚(I/O0-I/O7),5个使能信号(nWE、ALE、CLE、nCE、nRE)、1个状态引脚(RDY/B)、1个写保护引脚(nWP)。地址、数据、命令都是在这些使能信号的配合下,通过8个I/O引脚的传输。写地址、数据、命令时,nCE、nWE信号必须为低电平,它们在nWE信号的上升沿被锁存。命令锁存使能信号CLE和地址锁存信号ALE用来区分I/O引脚上传输的是命令还是地址。
命令字及操作方法
操作NAND Flash时,先传输命令,然后传输地址,最后读写数据。期间要检查Flash的状态。对于K9F1208U0M,它的容量是64MB,需要一个26位的地址。发出命令后,后面要紧跟着4个地址序列。比如读Flash时,发出读命令和4个地址序列后,后续的读操作就可以得到这个地址及器后续地址的数据。相应的命令字和地址序列如下表所示:
命令 | 第1个访问周期 | 第2个访问周期 | 第3个访问周期 |
---|---|---|---|
Read1(读) | 00h/01h | - | - |
Read2(读) | 50h | - | - |
Read ID(读芯片ID) | 90h | - | - |
Page Program(写页) | 80h | 10h | - |
Block Erase(擦除块) | 60h | D0h | - |
Read Status(读状态) | 70h | - | - |
Read Multi-Plane Status(读多层的状态) | 71h | - | - |
Reset(复位) | FFh | - | - |
Page Program(Dummy) | 80h | 11h | - |
Copy-Back Program(True) | 00h | 8Ah | 10H |
Copy-Back Program(Dummy) | 03h | 8Ah | 11H |
Multi-Plane Block Erase | 60h-60h | D0h | - |
/ | I/O0 | I/O1 | I/O2 | I/O3 | I/O4 | I/O5 | I/O6 | I/O7 | 备注 |
---|---|---|---|---|---|---|---|---|---|
第1个地址序列 | A0 | A1 | A2 | A3 | A4 | A5 | A6 | A7 | 列地址 |
第2个地址序列 | A9 | A10 | A11 | A12 | A13 | A14 | A15 | A16 | 行地址(页地址) |
第3个地址序列 | A17 | A18 | A19 | A20 | A21 | A22 | A23 | A24 | 行地址(页地址) |
第4个地址序列 | A25 | L | L | L | L | L | L | L | 行地址(页地址) |
注:
①K9F1208U0M一页大小为512字节,分两部分:上半部、下半部。
②列地址用来在半页(256字节)中寻址。
③当发出读命令00h时,表示列地址将在上半部寻址。当发出写命令01h时,表示列地址将在下半部寻址。
④A8被读命令00h设为低电平,被01h设为高电平。
⑤L表示低电平。
K9F1208U0M一页大小为508字节,而列地址A0-A7可以寻址的范围是256字节,所以必须辅以其他手段才能完全寻址着528字节。将一页分为A、B、C三个区:A区为0-255字节,B区为256-511字节,C区为512-527字节。访问某页时,需要选定特定的区,这称为“使地址指针指向特定的区”。这通过3个命令来实现:命令00h让地址指针指向A区、命令01h让地址指针指向B区、命令50h让地址指针指向C区。命令00h和命令50h会使得访问Flash的地址指针一直从A区或C区开始,除非发出了其他的修改地址指针的命令。命令01h的效果只能维持一次,当前的读、写、擦除、复位或者上电操作完成后,地址指针重新指向A区。写A区或C区的数据时,必须在发出命令80h之前发出命令00h或50h;写B区的数据时,发出命令01h后必须紧接着发出命令80h。下图形象的描述了这个过程:
- Read 1:命令字为00h或01h
在发出命令00h或者01h后,就选定了读操作是从A区还是B区开始。列地址A0-A7可以寻址的范围是256字节,命令00h和01h使得可以在512字节大小的页内任意寻址–这相当于A8被命令00h设为0,而命令01h设为1。
发出命令字后,随后发出4个地址序列,然后就可以检测R/nB引脚以确定Flash是否准备好。如果准备好了,就可以发起读操作一次读出数据。 - Read 2:命令字为50h
与Read 1类似,不过读取的是C区数据,操作序列为:发出命令字50h、发出4个地址序列、等待R/nB引脚为高,最后读取数据。不同的是,地址序列中A0-A3用于设定C区(16字节)要读取的起始地址,A4-A7被忽略。 - Read ID:命令字90h。
发出命令字90h,发出4个地址序列(都设为0),然后就可以连续读入5个数据,分别表示:厂商代码(Samsung格式为Ech)、设备代码(K9F1208U0M为76h)、保留的字节(K9F1208U0M为A5h)、多层操作代码(C0h表示支持多层操作)。 - Reset:命令字为FFh
发出命令字FFh即可复位NAND Flash芯片。如果芯片正处于读、写、擦除状态,复位命令会终止这些命令。 - Page Program(True):命令字分两阶段,80h和10h
它的操作序列如下图所示:
NAND Flash的写操作一般是以页为单位的,但是可以只写一页中的一部分。发出命令字80h后,紧接着的是4个地址序列,然后向Flash发送数据(最大可达528字节),然后发送命令字10h启动写操作,此时Flash内部会自动完成写、校验操作。一旦发出命令字10h后,就可以通过读状态命令70h获知当前写操作是否完成、是否成功。 - Page Program(Dummy):命令字分为两阶段,80h和11h。
NAND Flash K9F1208U0M分为4个128Mbit的存储层(plane),每个存储层包含1024个block和528字节的寄存器。这使得可以同时写多个页(page)或者同时擦除多个块(block)。块的地址经过精心安排,可以在4个连续的块内同时进行写或者擦除操作。下图为K9F1208U0M的块组织图:
命令Page Program(Dummy)正是在这种结构下对命令Page Program(True)的扩展,后者仅能对一页进行写操作,前者可以同时写4页。操作序列如下图:
发出命令字80h、4个地址序列及最多528字节的数据之后,发出命令字11h(11h称为“Dummy Page Program command”,相对的,10h称为“True Page Program Command”);接着对相邻层(plane)上的页进行同样的操作。仅在第4页的最后使用10h替代11h,这样即可启动Flash内部的写操作。此时可以通过命令71h获知这些写操作是否完成、是否成功。 - Copy-Back Program(True):命令字分3阶段,00h、08h、10h。
此命令用于将一页复制到同一层(plane)内的另一页,它省略了读出源数据、将数据重新载入Flash,这使得效率大卫提高。此命令有两个限制:源页、目的页必须在同一层(plane)中,并且将源地址、目的地址的A14、A15必须相同。
操作序列如下图所示:
首先发出命令Read 1(00h)、4个源地址序列,此时源页的528字节数据很快就被全部读入内部寄存器中;接着发出命令字8Ah(Page-Copy Data-input command),随之发出4个目的地址序列;最后发出命令字10h启动对目的页的写操作。此后可以使用命令70h来查看此操作是否完成、是否成功。 - Copy-Back Program(Dummy):命令字分3个阶段,03h、8Ah、11h。
与命令Page Program(Dummy)类似,Copy-Back Program(Dummy)可以同时启动对多达4个连续plane内的Copy-Back Program操作。操作序列如下所示:
从图中可得知,首先发出命令字00h、源页地址,这使得源页的528字节数据被读入所在plane的寄存器;对于随后的其他plane的源页,发出命令字03h和相应的源页地址将数据读入该plane的寄存器;按照前述说明读出最多4页的数据到寄存器后,发出命令字8Ah、目的地址、命令字11h,在发出最后一页地址后,用10h代替11h启动写操作。 - Block Erase:命令字分3阶段,60h、D0h。
此命令用于擦除NAND Flash块(block,大小为16KB)。发出命令字后,发出block地址—仅需要3个地址序列,并且A9-A13被忽略,操作序列如下图所示: - Multi-Plane Block Erase:60h—-60h D0h。
此命令用于擦除不同的plane中的块,发出命令字60h后,紧接着发出block地址序列,如此最多可以发出4个block地址,最后发出命令字D0h启动擦除操作。操作序列如图所示: - 读状态命令字有如下两种:
①Read Status:命令字为70h。
②Read Multi-Plane Status:命令字为71h。
Falsh中有状态寄存器,发出命令字70h或71h后,启动读操作即可读入此寄存器。状态寄存器中各位的含义如下表所示:I/O引脚 所标识的状态 命令70h对应的定义 命令71h对应的定义 I/O0 总标记:成功/失败 成功:0 失败:1 成功:0 失败:1 I/O1 Plane0的标记:成功/失败 忽略 成功:0 失败:1 I/O2 Plane1的标记:成功/失败 忽略 成功:0 失败:1 I/O3 Plane2的标记:成功/失败 忽略 成功:0 失败:1 I/O4 Plane3的标记:成功/失败 忽略 成功:0 失败:1 I/O5 保留 忽略 忽略 I/O6 设备状态 忙:0 就绪:1 成功:0 失败:1 I/O7 写保护状态 保护:0 没有保护:1 保护:0 没有保护:1
注:
①I/O0是所有Plane的“总标记”,只要有一个Plane的操作是失败的,I/O0就会被设为“失败”。
②I/O0-I/O4引脚只部件它对应的Plane。
S3C2410/S3C2440 NAND Flash控制器介绍
NAND Flash控制器提供几个寄存器来简化对NAND Flash的操作。比如要发出读命令时,只需要往NFCMD寄存器中写入0即可,NAND Flash控制器会自动发出各种控制信号。
操作方法概述
访问NAND Flash时需要先发出命令,然后发出地址序列,最后读写数据。需要使用各个使能信号来分辨是命令、地址还是数据。S3C2410的NAND Flash控制器提供了NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT和NFECC等6个寄存器来简化这些操作。S3C2440的NAND Flash控制器则提供了NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT和其他与ECC有关的寄存器。对NAND Flash控制器的操作,S3C2410与S3C2440有一点小差别,有的寄存器地址不一样,有的寄存器内容不一样。
NAND Flash的读写操作次序如下:
- 设置NFCONF(对于S3C2440,还要设置NFCONT)寄存器,配置NAND Flash。
- 向NFCMD寄存器写入命令。
- 向NFADDR写入地址。
- 读/写数据,通过寄存器NFSTAT检测NAND Flash的状态,在启动某个操作之后,应该检测R/nB信号以确定该操作是否完成、是否成功。
寄存器介绍
- NFCONF:NAND Flash配置寄存器
这个寄存器在S3C2410、S3C2440上功能有所不同。
S3C2410上的NFCONF寄存器,被用来使能/禁止NAND Flash控制器,使能/禁止控制引脚信号nFCE、初始化ECC、设置NAND Flash的时序参数等。TACLS、TWRPH0和TWRPH1这3个参数控制的是NAND Flash信号线CLE/ALE与写控制信号nWE的时序关系。
S3C2440的NFCONF寄存器,被用来设置NAND Flash的时序参数TACLS、TWRPH0、TWRPH1,设置数据位宽;还有一些只读位,用来指示是否支持其他大小的页(256/512/1024/2048字节)。
它没有实现S3C2410的NFCONF寄存器的控制功能,这些功能在S3C2440的NFCONT寄存器里实现。 - NFCONT:NAND Flash控制寄存器,S3C2410没有这个寄存器。
被用来使能/禁止NAND Flash控制器、使能/禁止控制引脚信号nFCE、初始化ECC。它还有其他功能,在一般的应用中用不到,比如锁定NAND Flash。 - NFCMD:NAND Flash命令寄存器
对于不同型号的Flash,操作命令一般不一样。 - NFADDR:NAND Flash地址寄存器。
当写这个寄存器时,它将对Flash发出地址信号。 - NFDATA:NAND Flash数据寄存器。
只用到低8位,读写此寄存器将启动对NAND Flash的读数据、写数据操作。 - NFSTAT:NAND Flash状态寄存器
只用到位0。0:busy,1:ready。
NAND Flash控制器操作实例:读Flash
本实例讲述如何读取NAND Flash,擦除、写Flash的操作与读Flash类似,读者可以自行编写程序程序。
读NAND Flash的步骤
下面讲述如何从NAND Flash中读出数据,假设读地址为addr。
1.设置NFCONF(对于S3C2440,还要设置NFCONT)
对于S3C2410
实例中此寄存器设为0x9830—使能NAND Flash控制器、初始化ECC、NAND Flash片选信号nFCE=1(inactive,真正使用时再让它等于0),设置TACLS=0,TWRPH0=3,TWRPH1=0。这些时序参数的含义为:TACLS=1个HCLK时钟,TWRPH0=4个HCLK时钟,TWRPH1=1个HCLK时钟。
K9F1208U0M的时间特性如下:1
2
3CLE setup Time = 0 ns,CLE Hold Time = 10 ns,
ALE setup Time = 0 ns,ALE Hold Time = 10 ns,
WE Pulse Width 25 ns参考上图:即使在HCLK=100MHZ的情况下,TACLS+TWRPH0+TWRPH1=6/100μs=60ns,也是可以满足NAND Flash K9F1208U0M的时序要求的。
对于S3C2440
时间参数也设为:TACLS=0,TWRPH0=3,TWRPH1=0。NFCONF寄存器的值如下:1
NFCONF = 0x300
NFCONT寄存器的取指如下,表示使能NAND Flash控制器、禁止控制引脚信号nFCE、初始化ECC。
1
NFCONT = (1<<4) | (1<<1) | (1<<0)
2.在第一次操作NAND Flash前,通常复位一下NAND Flash
- 对于S3C2410然后循环查询NFSTAT位0,直到它等于1。
1
2NFCONF &= ~(1<<11) (发出片选信号)
NFCMD = 0xff (reset命令)
最后禁止片选信号,在实际使用NAND Flash时再使能。1
NFCONF |= (1<<11) (禁止NAND Flash)
- 对于S3C2440然后循环查询NFSTAT位0,知道它等于1。
1
2NFCONT &= ~(1<<11) (发出片选信号)
NFCMD = 0xff (reset命令)
最后禁止片选信号,在实际使用NAND Flash时再使能。1
NFCONT |= 0x2 (禁止NAND Flash)
3.发出读命令
先使能NAND Flash,然后发出读命令。
- 对于S3C2410
1
2NFCONF &= ~(1<<11) (发出片选信号)
NFCMD = 0 (读命令) - 对于S3C2440
1
2NFCONT &= ~(1<<11) (发出片选信号)
NFCND = 0 (读命令)
4.发出地址信号
这步请注意,表8.3列出了在地址操作的4个步骤对应的地址线,没用到A8(它由读命令设置,当读命令为0时,A8=0;当读命令为1时,A8=1),如下所示:
1 | NFADDR = addr & 0xff |
5.循环查询NFSTAT位0,直到等于1,这时候就可以读取数据了
6.连续读取NFDATA寄存器512次,得到一页数据(512字节)
循环执行第3、4、5、6这四个步骤,直到读出所要求的所有数据。
7.最后禁止NAND Flash的片选信号
- 对于S3C2410
1
NFCONF |= (1<<11)
- 对于S3C2440
1
NFCONT |= (1<<1)
代码详解
源文件为head.S、init.c和main.c。本实例的目的是把一部分代码存放在NAND Flash地址4096后,当程序启动NAND Flash控制器将它们读出来、执行。以前的代码都小于4096字节,开发板启动后它们被自动复制进“Steppingstone”中。
连接脚本nand.lds把它们分为两部分,nand.lds代码如下:
1 | SECTIONS{ |
第2行表示head.o、init.o、nand.o这3个文件的运行地址为0,它们在生成的映像文件中的偏移地址也为0(从0开始存放)。
第3行表示main.o的运行地址为0x30000000,它在生成的映像文件中的偏移地址为4096。
head.S调用init.c中的函数来关WATCH DOG、初始化SDRAM;调用nand.c中的函数来初始化NAND Flash,然后将main.c中的代码从NAND Flash地址4096开始处复制到SDRAM中;最后跳到main.c中的main函数继续执行。
由于S3C2410、S3C2440的NAND Flash控制器并非完全一样,这个程序要既能处理S3C2410,也能处理S3C2440,首先需要分辨是S3C2410还是S3C2440,然后使用不同的函数进行处理。读取GSTATUS1寄存器,如果它的值为0x32410000或0x32410002,就表示处理器是S3C2410,否则就是S3C2440。
nand.c向外引出两个函数:用来初始化NAND Flash的nand_init函数,用来将数据从NAND Flash读到SDRAM的nand_read函数。
nand_init 函数分析
代码如下:
1 | /* |
第12行读取GSTATUS1寄存器来判断为S3C2410还是S3C2440,然后分别处理:S3C2410、S3C2440的NAND Flash控制器中,有一些寄存器的功能是相同的,但是它们的地址是不一样的;有一些寄存器的功能已经发生变化。所以使用两套函数来进行处理。
第14-20行设置S3C2410的NAND Flash处理函数,第27-33行设置S3C2440的NAND Flash处理函数,把这些函数赋值给nand_chip结构,以后通过这个结构来调用。
如果处理器是S3C2410,则调用第23行的代码设置NFCONF寄存器;使能NAND Flash控制器,初始化ECC,禁止片选,设置时序。如果处理器是S3C2440,则使用第36、38两行代码来进行相同的设置。
最后第41行调用nand_reset函数复位NAND Flash。在第一次使用前通常复位一下。其中涉及的各个函数都只有几行,主要是读写寄存器。
nand_read 函数分析
它的原型如下,表示从NAND Flash位置satrt_addr开始,将数据复制到SDRAM地址buf处,共复制size字节。
1 | void nand_read(unsigned char *buf,unsigned long start_addr,int size) |
代码如下:
1 | void nand_read (unsigned char *buf,unsigned long start_addr,int size) |
可以看到,读NAND Flash的操作分为6步。
- 选择芯片——nand_select_chip();
- 发出读命令——write_cmd();
- 发出地址——write_addr();
- 等待数据就绪——wait_idle();
- 读取数据——read_data();
- 结束后,取消片选信号;
流程图如下:
从NAND Flash复制代码到SDRAM并运行的过程: