JZ2440 裸机驱动 第8章 NAND Flash控制器

本章目标 
了解NAND Flash 芯片的接口
掌握通过NAND Flash控制器访问NAND Flash的方法
8.1 NAND Flash介绍和NAND Flash控制器使用
    NAND Flash在嵌入式系统中的地位与PC上的硬盘类似,用于保存系统运行所需的操
作系统、应用程序、用户数据、运行过程中产生的各类数据。与内存掉电数据丢失不同,
NAND Flash中的数据在掉电后仍可永久保存。
8.1.1 Flash介绍
    常用的Flash类型由NOR Flash和NAND Flash两种。两者的主要差别如表8.1所示。
    NOR Flash支持XIP,即代码可以直接在NOR Flash上执行,无需复制到内存中。这是
由于NOR Flash的接口与RAM完全相同,可以随机访问任意地址的数据。在NOR Flash上进行
读操作的效率非常高,但擦除和写操作的效率很低;另外,NOR Flash的容量一般比较小。
NAND Flash进行擦除和写操作的效率更高,而且容量更大。一般而言,NOR Flash用于存储
程序,NAND Flash用于存储数据。基于NAND Flash的设备通常也要搭配NOR Flash以存储程序。
    Flash存储器件由擦除单元(也称为块)组成,当要写某个块时,需要确保这个块已经被
擦除。NOR Flash的块大小范围为64KB~128KB;NAND Flash的块大小范围为8KB~64KB,擦写一
个NOR Flash块需要4s,而擦写一个NAND Flash块仅需2ms。NOR Flahs的块太大,不仅增加了
擦写时间,对于给定的写操作,NOR Flash也需要更多的擦除操作——特别是一些小文件,比
如一个文件只有1KB,但是为了保存它,确需要擦除大小为64KB~128KB的NOR Flash块。
    NOR Flash的接口与RAM完全相同,可以随意访问任意地址的数据,而NAND Flahs的接口
仅仅包含几个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每块的可擦除次数通常在10 0000次,
是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校验。
8.1.2 NAND Flash的物理结构
    NAND Flash K9F1208U0M为例,这款芯片时三星公司生产的容量为64MB的NAND Flash,常用于
手持式设备等消费类电子。它的封装及外部引脚如图8.7所示:
     K9F1208U0M的功能结构图如图8.2所示。
     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 Driver。
    NAND Flash存储单元组织结构如图8.3所示。
     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个地址序列。
8.1.3 NAND Flash访问方法
1.硬件连接
    NAND Flash与S3C2410/S3C2440的硬件连接如图8.4所示。
    NAND Flash与S3C2410/S3C2440的连线比较少:8个I/O引脚(IO0~IO7)、5个使能
信号(nWE、ALE、CLE、nCE、nRE)、1个状态引脚(RDY/B)、1个写保护引脚(nWP)。
地址、数据和命令都是在这些使能信号的配合下,通过8个I/O引脚传输。写地址、数据、
命令时,nCE、nWE信号必须为低电平,它们在nWE信号的上升沿被锁存。命令锁存使
能信号CLE和地址锁存信号ALE用来区分I/O引脚上传输的是命令还是地址。
2.命令字及操作方法
    操作NAND Flash时,先传输命令,然后传输地址,最后读写数据,期间要检查Flash
的状态。对于K9F1208U0M,它的容量为64MB,需要一个26位的地址。发出命令后,后
面要紧跟着4个地址序列。比如读Flash时,发出读命令和4个地址序列后,后续的读操作就
可以得到这个地址及其后续地址的数据。相应的命令字和地址序列如表8.2、8.3所示。
 
    K9F1208U0M一页大小为528字节,而列地址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。
    图8.5形象地表示了K9F1208U0M的这个特性。
     下面逐个讲解表8.2的命令字。
    (1)Read1:命令字为00h或01h。
    如图8.5所示,发出命令00h或者01h后,就选定了读操作从A区还是B区开始。从表8.3
可知,列地址A0~A7可以寻址的范围是256字节,命令00h和01h使得可以在512字节大小
的页内任意寻址——这相当于A8被命令00h设为0,而被命令01h设为1。
    发出命令字后,依照表8.3发出4个地址序列,然后就可以检测R/nB引脚以确定Flash
是否准备好。如果准备好了,就可以发起读操作依次读入数据。
    (2)Read2:命令字为50h。
    与Read1类似,不过读取的是C区数据,操作序列为:发出命令字50h、发出4个地址
序列、等待R/nB引脚为高,最后读取数据。不同的是,地址序列中A0~A3用于设定C区
(大小为16字节)要读取的起始地址,A4~A7被忽略。
    (3)ReadID:命令字位90h。
    发出命令字90h,发出4个地址序列(都设为0),然后就可以连续读入5个数据,分别
表示:厂商代码(对于SAMSUNG公司为Ech)、设备代码(对于K9F1208U0M为76h)、保
留的字节(对于K9F1208U0M为A5h)、多层操作代码(C0h表示支持多层操作)。
    (4)Reset:命令字位FFh。
    发出命令字FFh即可复位NAND Flash芯片。如果芯片正处于读、写、擦除状态,复位
命令会终止这些命令。
    (5)Page Program(True):命令字分两段,80h和10h。
    它的操作序列如图8.6所示。
     NAND Flash的写操作一般是以页为单位的,但是可以只写一页中的一部分。发出命令
字80h后,紧接着是4个地址序列,然后向Flash发送数据(最大可以达到528字节),然后发
出命令字10h启动写操作,此时Flash内部会自动完成写、校验操作。一旦发出命令字10h
后,就可以通过读状态命令70h获知当前操作是否完成、是否成功。
    (6)Page Program(Dummy):命令字分两阶段,80h和11h。
    NAND Flash K9F1208U0M分为4个128Mbit的存储层(plane),每个存储层包含1024个
block和528字节的寄存器。这使得可以同时写多个页(page)或者同时擦除多个块(block)。
块的地址经过精心安排,可以在4个连续的块内同时进行写或者擦除操作。
    如图8.7所示位K9F1208U0M的块组织图。
    命令Page Program(Dummy)正是在这种结构下对命令Page Program(True)的扩展,后者仅能
对一页进行写操作,前者可以同时写4页。命令Page Program(Dummy)的操作序列如图8.8所示。
     发出命令字80h、4个地址序列及最多528字节的数据之后,发出命令字11h(11h称
“Dummy Page Program command”,相对地,10h称为“True Page Program command”);
接着对相邻层(plane)上的页进行同样的操作;仅在第4页的最后使用10h替代11h,这样
即可启动Flash内部的写操作。此时可以通过命令71h获知这些操作是否完成、是否成功。
    (7)Copy-Back Program(True):命令字分3阶段,00h、8Ah、10h。
    此命令用于将一页复制到同一层(plane)内的另一页,它省略了读出源数据、将数据重新
载入Flash,这使得效率大为提高。此命令有两个限制:源页、目的页必须在同一层(plane)
中,并且源地址、目的地址的A14、A15必须相同。
    操作序列如图8.9所示。
    首先发出命令Read1(00h)、4个源地址序列,此时源页的528字节数据很快就被全部
内部寄存器中;
    接着发出命令字8Ah(Page-Copy Data-input command),随之发出4个目的地址序列;
    最后发出命令字10h启动对目的页的写操作。
    此后,可以使用命令70h来查看此操作是否完成、是否成功。
    (8)Copy-Back Program(Dummy):命令字分3阶段,03h、8Ah、11h。
    与命令Page Program(Dummy)类似,Copy-Back Program(Dummy)可以同时启动对多
达4个连续plane内的Copy-Back Program操作。操作序列如图8.10所示。
    从图8.10 可知,首先发出命令字00h、源页地址,这使得源页的528字节数据被读入
所在plane的寄存器;对于随后的其他plane的源页,发出命令字03h和相应的源页地址
将数据读入该plane的寄存器;按照前述说明读出最多4页的数据到寄存器后,发出命令
字8Ah、目的地址、命令字11h,在发出最后一页的地址后,用10h代替11h以启动写操作。
    (9)Block Erase:命令字分3阶段,60h、D0h。
    此命令用于擦除NAND Flash块(block,大小为16KB)。发出命令字60h之后,发出block
地址——仅需要3个地址序列(请参考表8.3,仅需要发出2、3、4 cycle所示地址),并且
A9~A13被忽略。操作序列如图8.11所示。

     (10)Multi-Plane Block Erase:60h——60h D0h
    此命令用于同时擦除不同的plane中的块。发出命令字60h之后,紧接着发出block地址序
列,如此最多可以发出4个block地址,最后发出命令字D0h启动擦除操作。操作序列如图
8.12所示。

     (11)读状态命令有以下两种:
    ① Read Status:命令字位70h;
    ② Read Multi-Plane Status:命令字位71h。
    Flash中有状态寄存器,发出命令字70h或者71h之后,启动读操作即可读入此寄存器。
状态寄存器中各位的含义如表8.4所示。
    
 8.1.4 S3C2410/S3C2440 NAND Flash控制器介绍
    NAND Flash控制器提供几个寄存器来简单对NAND Flash的操作。比如要发出读命令时,
只需要向NFCMD寄存器中写入0即可,NAND Flash控制器会自动发出各种控制信号。
1.操作方法概述
    访问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(对于S3C2410,还要设置NFCONT)寄存器,配置NAND Flash;
    ② 向NFCMD寄存器写入命令,这些命令字可参考表8.2;
    ③ 向NFADDR寄存器写入地址;
    ④ 读写数据:通过寄存器NFSTAT检测NAND Flash的状态,在启动某个操作后,应该检
测R/nB信号以确定该操作是否完成,是否成功。
2.寄存器介绍
    下面讲解这些寄存器的功能及具体用法。
(1)NFCONF:NAND Flash配置寄存器。
    这个寄存器在S3C2410、S3C2440上功能有所不同。
    ① S3C2410的NFCONF寄存器。
    被用来使能/禁止NAND Flash控制器、使能/禁止控制引脚信号nFCE、初始化ECC、设
置NAND Flash的时序参数等。
    TACLS、TWRPH0和TWRPH1这3个参数控制的是NAND Flash信号线CLE/ALE与写控制信号
nWE的时序关系,如图8.13所示。
    ② S3C2440的NFCONF寄存器。
    被用来设置NAND Flash的时序参数TACLS、TWRPH0、TWRPH1,设置数据尾款;还有
一些只读位,用来指示是否支持其他大小页(比如一页大小为256/512/1024/2048字节)。
    它没有实现S3C2410的NFCONF寄存器的控制功能,这些功能在S3C2440的NFCONT寄存
器里实现。
(2)NFCONT:NAND Flash控制寄存器,S3C2410没有这个寄存器。
    被用来使能/禁止NAND Flash控制器、使能/禁止控制引脚信号nFCE、初始化ECC。它
还有其他功能,在一般的应用中用不到,比如锁定NAND Flash。
(3)NFCMD:NAND Flash命令寄存器。
    对于不同信号的Flash,操作命令一般不一样。对于本开发板使用的K9F1208U0M,请参
考表8.2.
(4)NFADDR:NAND Flash地址寄存器
    当写这个寄存器时,它将对Flash发出地址信号。
(5)NFDATA:NAND Flash数据寄存器。
    只用到低8位,读、写此寄存器将启动对NAND Flash的读、写数据操作。
(6)NFSTAT:NAND Flash状态寄存器。
    只用到位0,0:busy;1:ready。
8.2 NAND Flash 控制器操作实例:读Flash
    本实例讲述如何读取NAND Flash,擦除、写Flash的操作与读Flash类似,读者可以
自行编写程序。
8.2.1 读NAND Flash的步骤
    下面讲述如何从NAND Flash中读出数据,假设读地址为addr。
1.设置NFCONF(对于S3C2440,还要设置NFCONT)
(1)对于S3C2420。
    在本章实例中设为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的时间特性如下:
    CLE setup Time = 0 ns,CLE Hold Time = 10ns,
    ALE setup Time = 0 ns,ALE Hold Time = 10ns,
    WE Pulse Width = 25ns
    参考图8.13,可以计算:即使在HCLK = 100MHz的情况下,
        TACLS + TWRPH0 + TWRPH1 = 6/100us = 60ns ,
也是可以满足NAND Flash K9F1208U0M的时序要求的。
(2)对于S3C2440。
    时间参数也设为:TACLS = 0,TWRPH0 = 3,TWRPH1= 0.NFCONF寄存器的值如下:
    NFCONF = 0x300
    NFCONT寄存器的取值如下,表示使能NAND Flash控制器、禁止控制引脚信号nFCE、
初始化ECC。
    NFCONT = (1 << 4) | (1 << 1) | (1 << 0)
2.在第一次操作NAND Flash前,通常复位一下NAND Flash
(1)对于S3C2410。
    NFCONF &= ~(1 << 11)    (发出片选信号)
    NFCMD     = 0xff             (reset命令)
     然后循环查询NFSTAT的位0,直到它等于1。
    最后禁止片选信号,在实际使用NAND Flash时再使能。
    NFCONF |= (1 << 11)        (禁止NAND Flash)
(2)对于S3C2440。
    NFCONT &= ~(1 << 1)      (发出片选信号)
    NFCMD     = 0xff             (reset命令)
    然后循环查询NFSTAT位0,知道它等于1。
    最后禁止片选信号,在实际使用NAND Flash时再使能。
    NFCONT |= 0x2                (禁止NAND Flash)
3.发出读命令
    先使能NAND Flash,然后发出读命令。
(1)对于S3C2410。
    NFCONF &= ~(1 << 11)    (发出片选信号)
    NFCMD     = 0                (读命令)
(2)对于S3C2440。
    NFCONT &= ~(1 << 1)       (发出片选信号)
    NFCMD     = 0                  (读命令)
4.发出地址信号
    这步请注意,表8.3列出了在地址操作的4个步骤对应的地址线,没有用到A8(它由读命令
设置,当读命令位0时,A8 = 0;当为1时,A8 = 1),如下所示:
1 NFADDR = addr & 0xff
2 NFADDR = (addr >> 9) & 0xff    (左移9位,不是8位)
3 NFADDR = (addr >> 17) & 0xff    (左移17位,不是16位)
4 NFADDR = (addr >> 25) & 0xff    (左移25位,不是24位) 
NFADDR
5.循环查询NFSTAT位0,直到它等于1,这时可以读取数据了
6.连续读NFDATA寄存器512次,得到一页数据(512字节)
    循环执行第3、4、5、6这4个步骤,直到读出所要求的所有数据。
7.最后,禁止NAND Flash的片选信号
(1)对于S3C2410。
    NFCONF |= (1 << 11) 
(2)对于S3C2440。
    NFCONT |= (1 << 1)
8.2.2 代码详解
    实验代码在/work/hardware/nand目录下,源文件为head.S、init.c和main.c。本实例的目的
是把一部分代码存放在NAND Flash地址4096之后,当程序启动后通过NAND Flash控制器将
它们读出来、执行。以前的代码都小于4096字节,开发板启动后它们被自动复制进
“Steppingstone”中。
    连接脚本nand.lds把它们分为两部分,nand.lds代码如下:
1 SECTIONS{
2     first    0x00000000:{head.o init.o nand.o}
3     second   0x30000000:AT(4096){main.o}
4 }
nand.lds
    第2行表示head.o、init.o、nand.o这3个文件的运行地址为0,它们在生成的映像文件中
的偏移地址也为0(从0开始存放)。
    第3行表示main.o的运行地址为0x30000000,它在生成的映像文件中的偏移地址为4096。
    head.S调用init.c中的函数来关看门狗、初始化SDRAM;
        调用nand.c中的函数来初始化NAND Flash,
        然后将main.c中的代码从NAND Flash地址4096开始复制到SDRAM中;
        最后跳到main.c中的main函数继续执行。
    由于S3C2410、S3C2440的NAND Flash控制器并非完全一样,这个程序要能分辨出是
S3C2410还是S3C2440,然后使用不同的函数进行处理。
    读取GSTATUS1寄存器,如果它的值为0x3241 0000或0x3241 0002,就表示处理器是
S3C2410,否则是S3C2440。
    nand.c向外引出两个函数:初始化NAND Flash的nand_init函数、将数据从NAND Flash
读到SDRAM的nand_read函数。
1.nand_init函数分析
代码如下:
 1 /*初始化NAND Flash*/
 2 void nand_init(void)
 3 {
 4     #define TACLS     0
 5     #define TWRPH0    3
 6     #define TWRPH1    0
 7     
 8     /*判断S3C2410还是S3C2440*/
 9     if((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
10     {
11         nand_chip.nand_reset          = s3c2410_nand_reset;
12         nand_chip.wait_idle           = s3c2410_wait_idle;
13         nand_chip.nand_select_chip    = s3c2410_nand_select_chip;
14         nand_chip.nand_deselect_chip  = s3c2410_nand_deselect_chip;
15         nand_chip.write_cmd           = s3c2410_write_cmd;
16         nand_chip.read_data           = s3c2410_read_data;
17         
18         /*使能NAND Flash控制器,初始化ECC,禁止片选,设置时序*/
19         s3c2410nand->NFCONF = (1 << 15) | (1 << 12) | (1 << 11) | (TACLS << 8) | (TWRPH0 << 4) | (TWRPH1);
20     }
21     else
22     {
23         nand_chip.nand_reset          = s3c2440_nand_reset;
24         nand_chip.wait_idle           = s3c2440_wait_idle;
25         nand_chip.nand_select_chip    = s3c2440_nand_select_chip;
26         nand_chip.nand_deselect_chip  = s3c2440_nand_deselect_chip;
27         nand_chip.write_cmd           = s3c2440_write_cmd;
28         nand_chip.read_data           = s3c2440_read_data;
29         
30         /*设置时序*/
31         s3c2440nand->NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);
32         /*使能NAND Flash控制器,初始化ECC,禁止片选*/
33         s3c2440nand->NFCONF = (1 << 4) | (1 << 1) | (1 << 0);
34     }
35     
36     /*复位NAND Flash*/
37     nand_reset();
38 }
nand_init()
2.nand_read函数分析
    它的原型如下,表示从NAND Flash位置start 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)
 2 {
 3     int i, j;
 4     
 5     if((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)){
 6         return;    /*地址或长度不对齐*/
 7     }
 8     
 9     /*选中芯片*/
10     nand_select_chip();
11     
12     for(i = start_addr; i < (start_addr + size) ; ){
13         /*发出READ0命令*/
14         write_cmd(0);
15         
16         /*Write Address*/
17         write_addr(i);
18         wait_idle();
19         
20         for(j = 0; j < NAND_SECTOR_SIZE; j++, i++){
21             *buf = read_data();
22             buf++;
23         }
24     }
25     
26     /*取消片选信号*/
27     nand_deselect_chip();
28     
29     return;
30 }
nand_read()
    可以看到,读取NAND Flash的操作分为6步:
    ① 选择芯片;
    ② 发出读命令;
    ③ 发出地址;
    ④ 等待数据就绪;
    ⑤ 读取数据;
    ⑥ 结束后,取消片选信号。
    流程图如图8.14所示。
     为了更形象地了解程序执行时代码复制、程序执行位置,请参考图8.15。

附:代码:

链接: https://pan.baidu.com/s/1kV24a9L 密码: tfab
原文地址:https://www.cnblogs.com/sz189981/p/7710919.html