1 nandflash原理#
1.1 原理图#
1.2 引脚描述#
引脚名称 | 引脚功能 |
---|---|
IO0~IO7 | 数据输入输出(命令、地址、数据共用数据总线) |
CLE | 命令使能 |
ALE | 地址使能 |
/CE | 芯片使能(片选) |
/RE | 读使能 |
/WE | 写使能 |
R/B | 就绪/忙输出信号(低电平表示操作还在进行中,高电平表示操作完成) |
1.3 nand存储结构#
我们常见的Nand Flash,内部只有一个chip,每个chip只有一个plane。但也有些复杂的,容量更大的Nand Flash,内部有多个chip,每个chip有多个plane,这类的Nand Flash,其实就是多了一个主控将多块flash叠加在一起,如下图:
1.3.1 Block块#
一个Nand Flash由很多个块(Block)组成,块的大小一般有64K,128KB,256KB,512KB,Block是Nand Flash的擦除操作的基本/最小单位。Nand Flash中,一个块中含有1个或多个位是坏的,就称为其为坏块Bad Block。
1.3.2 Page页#
每个块里面又包含了很多页(page)。每个页的大小,对于现在常见的Nand Flash多数是2KB,当然也有的nand flash的页大小为4KB、8KB等。页Page,是读写操作的基本单位。
1.3.3 oob区域#
每一个page页,对应还附加了一块区域,一般为64byte,叫做空闲区域(spare area)/oob区域(Out Of Band),由于nandflash在操作过程中容易产生位反转,这是nandflash的物理特性决定的,所以必须要有对应的检测和纠错机制,这种机制被叫做Error Checking and Correcting,所以设计了多余的oob区域,用于放置数据的校验值。oob的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了oob。
关于oob具体用途,总结起来有:
1 | 标记是否是坏快 |
1.3.4 存储单位关系#
一般情况下:1block = 64page = 64 * (2K+64B) = 128K + 4K, 一个page包含2K数据和64B的oob。
1.4 Feature特性#
以K9F2G08U0C这款nandflash为例:
可以看出此款nandflash特性如下:
1 | 1. 容量为256M,外加8M的冗余oob存储区。 |
1.5 常见的flash厂商#
常见的flash厂商有:Micron(镁光)、Toshiba(东芝)、Samsung(三星)、MXIC(旺宏)、dosilicon(东芯),(Winbond)华邦、ESMT等。
2 Nand控制器#
前面s3c2440裸机编程-内存控制器介绍讲到,我们知道nand没有独立地址线,cpu无法直接访问nand上的指令,所以nand不能片上执行。那么为何程序还能支持nand启动的呢?
为了支持NAND启动,S3C2440A配备了一个称为“ Steppingstone”的内部SRAM缓冲区,容量为4K。 开机时,Nandflash中的前4K数据将被加载到Steppingstone中,而引导代码将被加载到SRAM中将被执行,如下图所示:
2.1 OM启动介质选择#
我们知道s3c2440支持2种boot方式,nand或者nor,那么需要配置OM引脚来设置引导方式,如下图:
内存控制器的地址映射表如下:
当SW2闭合,OM0=1, OM[1:0]=01, 0地址对应nor,那么从nor启动。
当SW2断开,OM0=0, OM[1:0]=00, 0地址对应bootSRAM(4K),那么0地址对应该SRAM, 那么从nand启动。
2.2 nand控制器引脚配置#
当上电启动时,NAND Flash 控制器将通过下面的引脚配置来获取连接的 NAND Flash 的信息。
NCON:NAND Flash 存储器选择(普通/先进)
1 | 0:普通 NAND Flash(256 字或 512 字节页大小,3 或 4 个地址周期) |
GPG13:NAND Flash 存储器page size选择
1 | 0:页=256 字(NCON=0)或页=1K 字(NCON=1) |
GPG14:NAND Flash 存储器地址周期选择
1 | 0:3 个地址周期(NCON=0)或 4 个地址周期(NCON=1) |
GPG15:NAND Flash 存储器总线宽度选择
1 | 0:8 位宽度 |
如下表所示更直观:
3 Nand控制器访问时序#
nandflash访问时需要遵循一定的时序才能完成命令、地址、数据的发送。nandflash有8bit位宽数据总线,那么没有地址线它是怎么和cpu通信的呢?
1 | 1.nandflash是DATA0~DATA7上既传输数据,又传输地址,又传输命令; |
下面分别介绍命令、地址、数据的发送过程。
3.1 命令/地址锁存时序(写命令/地址)#
- 首先看时钟,nand控制器的时钟源采用的是HCLK, 也就是AHB高速总线模式,可以参考s3c2440裸机编程-时钟体系那么HCLK=100Mhz, T=1/HCLK=10ns。
- 从上图可知命令、地址锁存的时序是一样的,复用一个时序图,当到达①的位置时,CLE/ALE=0;
- 当到达位置②时,CLE/ALE=1,表示命令/地址信号拉高,命令/地址开始使能,然后往数据总线DATA上放入命令或地址;
- 经过TACLS时间,到达位置③时,拉低nWE引脚,这时数据总线DATA上的命令/地址开始被锁存,锁存需要一定的时间,所以经过TWRPH0时间后,数据总线DATA上的命令/地址锁存完成;
- 到达位置④,此时释放nWE信号,nWE=1,这时还需要经过TWRPH1时间后,释放CLE/ALE,此时一个完整的命令/地址锁存过程完成。
上面分析了命令/地址的锁存时序过程,下面详细解释下上面几个时间参数的含义:
1 | TACLS:CLE/ALE使能信号发送多久后才可以发送nWE信号 |
这些时间参数要根据我们实际使用的具体nandflash型号和性能来配置我们的nand控制器。
3.1.1 K9F2G08U0C命令/地址锁存时序#
以K9F2G08U0C这款nandflash为例进行讲解,规格书上命令和地址锁存周期如下:
和nand控制器的命令/地址锁存时序图对比发现:
1 | TACLS = max(tCLS,tALS) - tWP; |
nand控制器把命令、地址锁存时序复用成了一个时序图,其实命令和地址锁存时序参数基本一致,只不过发命令只需要一个周期就OK了,发地址需要5个时钟周期,为什么?
1 | 你想,命令多简单,无非就是读写擦,像我们这款nand数据位宽8bit,一个周期绰绰有余。但地址就不一样了,比如此款nandflash容量256M = 2^28,那么需要28根数据线来传输才能一个周期传输完,但这款nandflash的数据总线位宽只有8bit, 只有8根数据线,所以需要把地址拆分成多次发送,先发送col地址,再发送row地址,此款nandflash是用了5个周期发送地址。 |
3.2 数据锁存时序(写数据)#
从前面的命令地址锁存时序图中我们得知:
1 | CLE信号拉高,ALE信号拉低时,表示发送的命令; |
1 | CLE信号拉低, ALE信号拉低时,表示发送的数据; |
- 当到达①时,nWE还是高电平,写使能没有开启;
- 当到达②,③时,那么经过了tWP时间(TDS时间),数据开始被锁存;
- 到达④,经过tDH时间,数据锁存完成;
- 到达⑤,也就是数据开始锁存后再过了tWH时间后释放nWE信号;
- 重复②③④⑤过程,得到DIN0, DIN1, DIN final。
根据上面这三个图(手册上的命令、地址、数据锁存时序图),下面详细解释各个时间参数的含义:
3.3 数据顺序读时序(读数据)#
- ①处,表示要过tRR后才能发送读使能信号nRE进行访问(上一次的访问结束后,需要等待ready状态稳定后才可以进行下一次访问);
- 当到达②,需要经过rREA时间后nRE信号才有效(待nRE稳定);
- 当到达③,DATA总线上的读取被读取;
- 当到达④,nRE释放tREH时间后才允许下一次读使能;
我们看到连续顺序访问时,单次访问的时间为tRC,那么这些时间参数的值也可以从K9F2G08U0C datasheet中找到:为25ns
4 nandFlash初始化和识别#
4.1 nandFlash命令表#
找到K9F2G08U0C datasheet,对NAND FLASH的操作需要发出命令,下面有个NAND FLASH的命令表格,用此表格上的命令来访问我们的nandflash:
4.2 读ID时序#
命令表中的读id还不太直观,下图是从nand芯片手册中截取出的读id时序图:
- 第一条竖线位置,发送了nCE,CLE,nWE信号,所以90命令被锁存(readID命令);
- 第二条竖线位置,发送了nCE,ALE,nWE信号,所以地址00被锁存;继续往后,命令、地址都发完了,要read数据了,所以释放nWE,ALE,这里tAR表示ALE释放多久后才可以发送nRE信号,tREA表示nRE信号的建立时间;
- 第三条竖线位置,发送了nCE,nRE信号,所以数据被锁存,第一个访问周期锁存的数据为marker code,值为0xEC,第二个访问周期的数据为device code,值为0xDA。读id时读5个周期含义对应如下表:
该款nandflash的5个周期读出来的值对应如下:
第四个访问周期含义如下表:
第五个访问周期含义如下表:
根据第4、5个访问周期的结果0x15、0x44我们得知该flash的block_size=128K,page_size=2k, 有2个plane,plane_size=1Gb = 128M, 共256M。
4.3 初始化#
4.3.1 初始化nand控制器#
4.3.1.1 NFCONF-配置寄存器#
nand控制器要按照我们nandflash的实际型号和性能来设置初始值。NFCONF寄存器,也叫nand配置寄存器:
以K9F2G08U0C这款nandflash为例:
前面第3.1.1小节分析了TACLS = max(tCLS,tALS) - tWP,我们得知tCLS、tALS、tWP最小都可以取到12ns, 所以我们可以取TACLS=0;
TWRPH0 = tWP,我们的nand手册上要求tWP最少12ns, 那么取TWRPH0 =1, Duration = HCLK*(TWRPH0+1)=20ns>12ns,满足要求;
TWRPH0 = max(tCLH,tALH), 我们的nand手册上要求tCLH、tALH最少5ns, 那么取TWRPH1 =0, Duration = HCLK*(TWRPH1+1)=10ns>5ns,满足要求。
再配置BusWidth总线位宽为8bit;
所以NFCONF寄存器设置如下:
1 |
|
4.3.1.2 NFCONT-控制寄存器#
1 | MODE [0]: 设置为1,使能NAND控制器。 |
所以NFCONF寄存器设置如下:
1 | /*使能NAND FLASH控制器,禁止片选*/ |
4.4 识别nandflash#
4.4.1 NFCMMD-命令寄存器#
我们可以使用2440上的NAND FLASH控制器简化操作,只需要往NFCMMD寄存器写入要传输的命令就可以了,NAND FLASH控制器默认把上面复杂的时序发出来。
NFADDR-地址寄存器
发命令后,后面就需要发送地址了,当nWE和ALE有效的时候,表示锁存的是地址,往NFADDR寄存器中写值就可以了,比如:NFADDR=0x00。
我们得知地址需要用5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址。前面第3小节已详细分析过了命令、地址、数据锁存时序过程。
1 | 1. column: 列地址A0~A10,就是页内地址,地址范围是从0到2047。(A11用来确定oob的地址,即2048-2111这64个字节的范围) |
4.4.2 NFDATA-数据寄存器#
当命令、地址都发送完后就可以从数据总线上DATA[7:0]获取数据或者写入数据。同样往NFDATA寄存器中写值或者读值就可以了,如unsigned char buf=NFDATA,由于是数据位宽是8位的,所以访问时数据组织形式如下:
从上图可以看出,当byte access时,只需一个时钟周期;当wold access的时候,需要4个时钟周期,小端模式下第一个时钟周期对应低字节,第四个时钟周期对应高字节。
识别nandflash代码如下:
1 | /*初始化nand控制器*/ |
5 NandFlash数据操作#
5.1 读数据#
读数据以page为单位的。下图的表格,来说明NAND FLASH内部结构,前面2K(02047)表示页数据,后边64字节(20482111)表示oob。
CPU想读取,第2048个数据,它是哪以一个?
是Page1的第0个字节。CPU使用某个地址访问数据的时候,是在页数据空间来寻址的。
下图为读NAND FLASH的read时序操作:
- 首先需要锁存00命令,nCE、CLE、nWE有效,0x00命令被锁存;
- 此时CLE无效,ALE开始有效,地址被锁存(从NAND FLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址);
- 锁存0x30命令;
- 然后会有一个busy时间段,R/nB为低电平。tRR表示busy状态的持续时间(手册上最小为20ns)。
- 开始锁存数据,nRE使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期(tRC),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据。
下面开始写代码:
5.1.1 NFSTAT-状态寄存器#
当发完命令、地址后再进行读数据前我们知道有一段时间tRR处于busy状态,我们可以通过查询NFSTAT寄存器来确定busy状态有没有结束,是不是已经ready了。
wait_ready函数等待NAND FLASH空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时NAND FLASH是空闲的,我们可以通过该位来判断NAND FLASH是否繁忙。代码如下:
1 | void wait_ready(void) { |
nand_read函数为NAND FLASH的读函数,代码如下:
1 | void nand_read(unsigned int addr, unsigned char *buf, unsigned int len) { |
我们看到每read一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。
5.1.2 自动识别是nor还是nand启动#
在init.c文件中,加上如下代码,用来判断所使用的FLASH是NOR FLASH还是NAND FLASH。代码如下:
1 | /*我们知道nand启动0地址对应片内SRAM,可以像内存一样的写0地址;nor启动,0地址对应nor,nor不能像内存一样的写地址, |
下面是代码重定位时可以自动区分nand和nor启动,无论是nand启动还是nor启动,都能将程序重定位到sdram中去。
1 | void copy2sdram(void) { |
5.2 擦数据#
擦除数据以block为单位的。block erase时序图的过程大致如下:
1.首先发送0x60命令
2.发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可)
3.发送0xd0,执行擦除动作
4.然后会有一个busy时间段,R/nB为低电平
5.发送0x70命令,用来读取状态
6.判断NFDATA寄存器的第0位是否擦除成功
代码实现如下:
1 | int nand_erase(unsigned int addr, unsigned int len) { |
5.3 写数据#
写数据以page为单位。往NAND FLASH写数据时,只需要把要写的数据复制给NFDATA寄存器即可。代码如下:
1 | void nand_w_data(unsigned char val) { |
page write的写时序图如下:
1.首先发送0x80命令
2.发送地址(5个周期)
3.发送数据
4.发送0x10命令,执行烧写动作
4.然后会有一个busy时间段,R/nB为低电平
5.发送0x70命令,用来读取状态
6.判断NFDATA寄存器的第0位是否烧写成功
1 | void nand_write(unsigned int addr, unsigned char *buf, unsigned int len) { |
我们看到每写一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。
5.4 测试#
1 | void do_erase_nand_flash(void) { |
说明:本节的读、写、擦都只涉及到页数据区,不涉及到oob区的操作。
5.5 坏快的标记和解除#
Nand Flash怎么标记某一个BLOCK是坏的? 如何识别一个flash中的坏快?
1 | 它使用该BLOCK中第1个扇区的OOB数据中某一个字节来标记: 其值为0xff表示该BLOCK是好的, 其值为非0xff表示该BLOCK是坏的。 |