NAND_Flash控制器

《嵌入式Linux应用完全开发手册》第2篇第8章总结归纳

本章要点

  1. 了解NAND Flash芯片的接口
  2. 掌握通过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,常用于手持设备等消费电子产品。它的封装下图所示:
img not found

外部引脚如下表所示:

引脚名称 引脚功能
I/O0-I/O7 数据输入/输出
CLE 命令锁存使能
ALE 地址锁存使能
CE 芯片使能
RE 读使能
WE 写使能
WP 写保护
R/B 就绪/忙输出信号
Vcc 电源
Vss
N.C 不接

K9F1208U0M的功能结构图:
img not found
K9F1208U0M的内部结构包含10个功能部件:

  1. X-Buffers Latche & Decoders:用于行地址。
  2. Y-Buffers Latche & Decoders:用于列地址。
  3. Command Register:用于命令字。
  4. Control Logic & High Voltage Generator:控制逻辑及产生Flash所需高压。
  5. Nand Flash Array:存储部件。
  6. Page Register & S/A:页寄存器,当读、写某页时,会将数据先读入/写入此寄存器,大小为528字节。
  7. Y-Gating。
  8. I/O Buffers & Latches。
  9. Global Buffers。
  10. Output Drivers。

Nand Flash 存储单元组织结构如下图所示:
img not found
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的硬件连接图如下所示:
img not found
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。下图形象的描述了这个过程:
img not found

  1. Read 1:命令字为00h或01h
    在发出命令00h或者01h后,就选定了读操作是从A区还是B区开始。列地址A0-A7可以寻址的范围是256字节,命令00h和01h使得可以在512字节大小的页内任意寻址–这相当于A8被命令00h设为0,而命令01h设为1。
    发出命令字后,随后发出4个地址序列,然后就可以检测R/nB引脚以确定Flash是否准备好。如果准备好了,就可以发起读操作一次读出数据。
  2. Read 2:命令字为50h
    与Read 1类似,不过读取的是C区数据,操作序列为:发出命令字50h、发出4个地址序列、等待R/nB引脚为高,最后读取数据。不同的是,地址序列中A0-A3用于设定C区(16字节)要读取的起始地址,A4-A7被忽略。
  3. Read ID:命令字90h。
    发出命令字90h,发出4个地址序列(都设为0),然后就可以连续读入5个数据,分别表示:厂商代码(Samsung格式为Ech)、设备代码(K9F1208U0M为76h)、保留的字节(K9F1208U0M为A5h)、多层操作代码(C0h表示支持多层操作)。
  4. Reset:命令字为FFh
    发出命令字FFh即可复位NAND Flash芯片。如果芯片正处于读、写、擦除状态,复位命令会终止这些命令。
  5. Page Program(True):命令字分两阶段,80h和10h
    它的操作序列如下图所示:
    img not found
    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个连续的块内同时进行写或者擦除操作。下图为K9F1208U0M的块组织图:
    img not found
    命令Page Program(Dummy)正是在这种结构下对命令Page Program(True)的扩展,后者仅能对一页进行写操作,前者可以同时写4页。操作序列如下图:
    img not found
    发出命令字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、08h、10h。
    此命令用于将一页复制到同一层(plane)内的另一页,它省略了读出源数据、将数据重新载入Flash,这使得效率大卫提高。此命令有两个限制:源页、目的页必须在同一层(plane)中,并且将源地址、目的地址的A14、A15必须相同。
    操作序列如下图所示:
    img not found
    首先发出命令Read 1(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操作。操作序列如下所示:
    img not found
    从图中可得知,首先发出命令字00h、源页地址,这使得源页的528字节数据被读入所在plane的寄存器;对于随后的其他plane的源页,发出命令字03h和相应的源页地址将数据读入该plane的寄存器;按照前述说明读出最多4页的数据到寄存器后,发出命令字8Ah、目的地址、命令字11h,在发出最后一页地址后,用10h代替11h启动写操作。
  9. Block Erase:命令字分3阶段,60h、D0h。
    此命令用于擦除NAND Flash块(block,大小为16KB)。发出命令字后,发出block地址—仅需要3个地址序列,并且A9-A13被忽略,操作序列如下图所示:
    img not found
  10. Multi-Plane Block Erase:60h—-60h D0h。
    此命令用于擦除不同的plane中的块,发出命令字60h后,紧接着发出block地址序列,如此最多可以发出4个block地址,最后发出命令字D0h启动擦除操作。操作序列如图所示:
    img not found
  11. 读状态命令字有如下两种:
    ①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的读写操作次序如下:

  1. 设置NFCONF(对于S3C2440,还要设置NFCONT)寄存器,配置NAND Flash。
  2. 向NFCMD寄存器写入命令。
  3. 向NFADDR写入地址。
  4. 读/写数据,通过寄存器NFSTAT检测NAND Flash的状态,在启动某个操作之后,应该检测R/nB信号以确定该操作是否完成、是否成功。

寄存器介绍

  1. NFCONF:NAND Flash配置寄存器
    这个寄存器在S3C2410、S3C2440上功能有所不同。
    S3C2410上的NFCONF寄存器,被用来使能/禁止NAND Flash控制器,使能/禁止控制引脚信号nFCE、初始化ECC、设置NAND Flash的时序参数等。TACLS、TWRPH0和TWRPH1这3个参数控制的是NAND Flash信号线CLE/ALE与写控制信号nWE的时序关系。
    img not found
    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,操作命令一般不一样。
  4. NFADDR:NAND Flash地址寄存器。
    当写这个寄存器时,它将对Flash发出地址信号。
  5. NFDATA:NAND Flash数据寄存器。
    只用到低8位,读写此寄存器将启动对NAND Flash的读数据、写数据操作。
  6. NFSTAT:NAND Flash状态寄存器
    只用到位0。0:busy,1:ready。

NAND Flash控制器操作实例:读Flash

本实例讲述如何读取NAND Flash,擦除、写Flash的操作与读Flash类似,读者可以自行编写程序程序。

读NAND Flash的步骤

下面讲述如何从NAND Flash中读出数据,假设读地址为addr。

1.设置NFCONF(对于S3C2440,还要设置NFCONT)

  1. 对于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
    3
    CLE 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的时序要求的。

  2. 对于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

  1. 对于S3C2410
    1
    2
    NFCONF &= ~(1<<11)  (发出片选信号)
    NFCMD = 0xff (reset命令)
    然后循环查询NFSTAT位0,直到它等于1。
    最后禁止片选信号,在实际使用NAND Flash时再使能。
    1
    NFCONF  |=  (1<<11) (禁止NAND Flash)
  2. 对于S3C2440
    1
    2
    NFCONT  &=  ~(1<<11)    (发出片选信号)
    NFCMD = 0xff (reset命令)
    然后循环查询NFSTAT位0,知道它等于1。
    最后禁止片选信号,在实际使用NAND Flash时再使能。
    1
    NFCONT  |=  0x2         (禁止NAND Flash)

3.发出读命令

先使能NAND Flash,然后发出读命令。

  1. 对于S3C2410
    1
    2
    NFCONF &=   ~(1<<11)        (发出片选信号)
    NFCMD = 0 (读命令)
  2. 对于S3C2440
    1
    2
    NFCONT  &=  ~(1<<11)        (发出片选信号)
    NFCND = 0 (读命令)

4.发出地址信号

这步请注意,表8.3列出了在地址操作的4个步骤对应的地址线,没用到A8(它由读命令设置,当读命令为0时,A8=0;当读命令为1时,A8=1),如下所示:

1
2
3
4
NFADDR  = addr & 0xff
NFADDR = (addr >> 9) & 0xff (左移9位,不是8位)
NFADDR = (addr >> 17) & 0xff (左移17位,不是16位)
NFADDR = (addr >> 25) & 0xff (左移25位,不是24位)

5.循环查询NFSTAT位0,直到等于1,这时候就可以读取数据了

6.连续读取NFDATA寄存器512次,得到一页数据(512字节)

循环执行第3、4、5、6这四个步骤,直到读出所要求的所有数据。

7.最后禁止NAND Flash的片选信号

  1. 对于S3C2410
    1
    NFCONF |= (1<<11)
  2. 对于S3C2440
    1
    NFCONT  |=  (1<<1)

代码详解

源文件为head.S、init.c和main.c。本实例的目的是把一部分代码存放在NAND Flash地址4096后,当程序启动NAND Flash控制器将它们读出来、执行。以前的代码都小于4096字节,开发板启动后它们被自动复制进“Steppingstone”中。
连接脚本nand.lds把它们分为两部分,nand.lds代码如下:

1
2
3
4
SECTIONS{
first 0x00000000 :{head.o init.o nand.o}
second 0x30000000 : AT(4096) {main.o}
}

第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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
初始化NAND Flash
*/

void nand_init(void)
{
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0

/*判断是S3C2410还是S3C2440*/
if((GSTATUS1 == 0x32410000) || (GSTATAUS1 == 0x32410002))
{
nand_chip.nand_reset = s3c2410_nand_reset;
nand_chip.wait_idle = s3c2410_wait_idle;
nand_chip.nand_select_chip = s3c2410_nand_select_chip;
nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip;
nand_chip.write_cmd = s3c2410_write_cmd;
nand_chip.write_addr = s3c2410_write_addr;
nand_chip.read_data = s3c2410_read_data;

/*使能NAND Flash控制器,初始化ECC,禁止片选,设置时序*/
s3c2410nand->NFCONF = (1<<15) | (1<<12) | (1<<11) | (TACLS<<8) | (TWRPH0<<4) | (TWRPH1<<0);
}
else
{
nand_chip.nand_reset = s3c2440_nand_reset;
nand_chip.wait_idle = s3c2440_wait_idle;
nand_chip.nand_select_chip = s3c2440_nand_select_chip;
nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
nand_chip.write_cmd = s3c2440_write_cmd;
nand_chip.write_addr = s3c2440_write_addr;
nand_chip.read_data = s3c2440_read_data;

/*设置时序*/
s3c2410nand->NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/*使能NAND Flash控制器,初始化ECC,禁止片选,设置时序*/
s3c2410nand->NFCONF = (1<<4) | (1<<1) | (1<<0);
}
/*复位NAND Flash*/
nand_reset();
}

第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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void nand_read (unsigned char *buf,unsigned long start_addr,int size)
{
int i,j;

if((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)){
return;/*地址或长度不对齐*/
}

/*选中芯片*/
nand_select_chip();

for(i = start_addr;i < start_addr + size;){
/*发出READ0命令*/
write_cmd(0);

/*Write Address*/
write_addr(i);
wait_idle();

for(j = 0;i < NAND_SECTOR_SIZE;j++,i++){
*buf = read_data();
buf++;
}
}

/*取消片选信号*/
nand_deselect_chip();

return;
}

可以看到,读NAND Flash的操作分为6步。

  1. 选择芯片——nand_select_chip();
  2. 发出读命令——write_cmd();
  3. 发出地址——write_addr();
  4. 等待数据就绪——wait_idle();
  5. 读取数据——read_data();
  6. 结束后,取消片选信号;

流程图如下:
img not found
从NAND Flash复制代码到SDRAM并运行的过程:
img not found
img not found
img not found