Nand Flash数据存储单元的整体架构
http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html
简单说就是,常见的Nand Flash,内部只有一个chip,每个chip只有一个plane。
而有些复杂的,容量更大的Nand Flash,内部有多个chip,每个chip有多个plane。这类的Nand Flash,往往也有更加高级的功能,
比如下面要介绍的Multi Plane Program和Interleave Page Program等。
概念上,由大到小来说,就是:
Nand Flash ⇒ Chip ⇒ Plane ⇒ Block ⇒ Page ⇒ oob
比如,型号为K9K8G08U0A这块Nand Flash(有时候也被称为此块chip芯片),
其内部有两个K9F4G08U0A的chip,chip#1和chip#2,
每个K9F4G08U0A的chip包含了2个Plane,每个Plane是2Gbbit,
所以K9F4G08U0A的大小是2Gb×2 = 4Gb = 512MB,
因此,K9K8G08U0A内部有2个K9F4G08U0A,或者说4个Plane,总大小是×256MB=1GB。
公式 1.1. K9K8G08U0A的物理结构所组成的总容量 K9K8G08U0A(这块Nand Flash) = 2 × K9F4G08U0A(K9F4G08U0A是chip,1 K9F4G08U0A = 2 Plane) = 2 × 2个Plane = 4 Plane(1 Plane = 2048 Block) = 4 × 2048个Block(1 Block = 64 Page) = 4 × 2048 × 64Page(1 Page = 2KB) = 4 × 2048 × 64Page × 2KB = 4 × 2048 × 128KB(1 Block = 128KB) = 4 × 256MB(1 Plane = 2Gb = 256MB) = 2 × 512MB(1 K9F4G08U0A = 4Gb = 512MB) = 1GB(1 K9K8G08U0A = 1GB)
而型号是K9WAG08U1A的Nand Flash,内部包含了2个K9K8G08U0A,所以,总容量是K9K8G08U0A的两倍=1GB×2=2GB,
类似地K9NBG08U5A,内部包含了4个K9K8G08U0A,总大小就是4×1GB=4GB。
Nand Flash物理存储单元的阵列组织结构
1.2.5.1. Block块
一个Nand Flash(的chip,芯片)由很多个块(Block)组成,块的大小一般是128KB,256KB,512KB,
此处是128KB。其他的小于128KB的,比如64KB,一般都是下面将要介绍到的small block的Nand Flash。
块Block,是Nand Flash的擦除操作的基本/最小单位。
1.2.5.2. Page页
每个块里面又包含了很多页(page)。每个页的大小,对于现在常见的Nand Flash多数是2KB,
最新的Nand Flash的是4KB、8KB等,这类的页大小大于2KB的Nand Flash,
被称作big block的Nand Flash,对应的发读写命令地址,一共5个周期(cycle),
而老的Nand Flash,页大小是256B,512B,这类的Nand Flash被称作small block,地址周期只有4个。
页Page,是读写操作的基本单位。
不过,也有例外的是,有些Nand Flash支持subpage(1/2页或1/4页)子页的读写操作,不过一般很少见。
1.2.5.3. oob / Redundant Area / Spare Area
每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),
而Linux系统中,一般叫做OOB(Out Of Band),这个区域,是最初基于Nand Flash的硬件特性:
数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,
此机制被叫做EDC(Error Detection Code)/ECC(Error Code Correction, 或者
Error Checking and Correcting),所以设计了多余的区域,用于放置数据的校验值。
Oob的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了oob。
关于oob具体用途,总结起来有:
- 标记是否是坏快
- 存储ECC数据
- 存储一些和文件系统相关的数据。如jffs2就会用到这些空间存储一些特定信息,
而yaffs2文件系统,会在oob中,存放很多和自己文件系统相关的信息。
1.2.8. Nand Flash的位反转特性
Nand Flash的位反转,也叫做位翻转,对应的英文表达有:Bit Flip=Bit Flipping=Bit-Flip=Bit twiddling。
Nand Flash由于本身硬件的内在特性,会导致(极其)偶尔的出现位反转的现象。
所谓的位反转,bit flip,指的是原先Nand Flash中的某个位,变化了,即要么从1变成0了,要么从0变成1了。
1.2.8.1. Nand Flash位反转的原因
Nand Flash的位反转现象,主要是由以下一些原因/效应所导致:
- 漂移效应(Drifting Effects)
漂移效应指的是,Nand Flash中cell的电压值,慢慢地变了,变的和原始值不一样了。
- 编程干扰所产生的错误(Program-Disturb Errors)
此现象有时候也叫做,过度编程效应(over-program effect)。
对于某个页面的编程操作,即写操作,引起非相关的其他的页面的某个位跳变了。
- 读操作干扰产生的错误(Read-Disturb Errors)
此效应是,对一个页进行数据读取操作,却使得对应的某个位的数据,产生了永久性的变化,即Nand Flash上的该位的值变了。
1.2.8.2. Nand Flash位反转的影响
位反转,说白了,就是读取数据的时候,数据出错了。
因此,如果你读取的数据正好是属于某个重要的文件中的数据,比如系统的配置文件等,那么此时错了一位,都会导致系统出现异常,问题相对会很严重。
而如果此数据属于音视频流中的数据,那么此时即使错了一位,对整个音视频的播放产生的影响也很小,所以问题也不大。
1.2.8.3. Nand Flash位反转的类型和解决办法
对应的位反转的类型,有两种:
- 一种是nand flash物理上的数据存储的单元上的数据,是正确的,
只是在读取此数据出来的数据中的某位,发生变化,出现了位反转,
即读取出来的数据中,某位错了,本来是0变成1,或者本来是1变成0了。
此处可以成为软件上位反转。此数据位的错误,当然可以通过一定的校验算法检测并纠正。 - 另外一种,就是nand flash中的物理存储单元中,对应的某个位,物理上发生了变化,
原来是1的,变成了0,或原来是0的,变成了1,发生了物理上的位的数据变化。
此处可以成为硬件上的位反转。此错误,由于是物理上发生的,
虽然读取出来的数据的错误,可以通过软件或硬件去检测并纠正过来,
但是物理上真正发生的位的变化,则没办法改变了。
不过个人理解,好像也是可以通过擦除Erase整个数据块Block的方式去擦除此错误,
不过在之后的Nand Flash的使用过程中,估计此位还是很可能继续发生同样的硬件的位反转的错误。
以上两种类型的位反转,其实对于从Nand Flash读取出来的数据来说,
解决其中的错误的位的方法,都是一样的,即通过一定的校验算法,常称为ECC,去检测出来,或检测并纠正错误。
如果只是单独检测错误,那么如果发现数据有误,那么再重新读取一次即可。
实际中更多的做法是,ECC校验发现有错误,会有对应的算法去找出哪位错误并且纠正过来。
其中对错误的检测和纠正,具体的实现方式,有软件算法,也有硬件实现,
即硬件Nand Flash的控制器controller本身包含对应的硬件模块以实现数据的校验和纠错的。
表 1.4. Nand Flash引脚功能的中文说明
引脚名称 | 引脚功能 |
---|---|
I/O0 ~ I/O7 | 用于输入地址/数据/命令,输出数据 |
CLE | Command Latch Enable,命令锁存使能,在输入命令之前,要先在模式寄存器中,设置CLE使能 |
ALE | Address Latch Enable,地址锁存使能,在输入地址之前,要先在模式寄存器中,设置ALE使能 |
CE# | Chip Enable,芯片使能,在操作Nand Flash之前,要先选中此芯片,才能操作 |
RE# | Read Enable,读使能,在读取数据之前,要先使CE#有效。 |
WE# | Write Enable,写使能, 在写取数据之前,要先使WE#有效 |
WP# | Write Protect,写保护 |
R/B# | Ready/Busy Output,就绪/忙,主要用于在发送完编程/擦除命令后,检测这些操作是否完成,忙,表示编程/擦除操作仍在进行中,就绪表示操作完成 |
Vcc | Power,电源 |
Vss | Ground,接地 |
N.C | Non-Connection,未定义,未连接 |
数据手册中的#表示低电平
在数据手册中,你常会看到,对于一个引脚定义,有些字母上面带一横杠的,那是说明此引脚/信号是低电平有效,
比如你上面看到的RE头上有个横线,就是说明,此RE是低电平有效,此外,为了书写方便,在字母后面加“#”,也是表示低电平有效,
比如我上面写的CE#;如果字母头上啥都没有,就是默认的高电平有效,比如上面的CLE,就是高电平有效。
1.2.10. Nand Flash的一些典型(typical)的特性
- 页擦除时间是200us,有些慢的有800us
- 块擦除时间是1.5ms
- 页数据读取到数据寄存器的时间一般是20us
- 串行访问(Serial access)读取一个数据的时间是25ns,而一些旧的Nand Flash是30ns,甚至是50ns
- 输入输出端口是地址和数据以及命令一起multiplex复用的
- Nand Flash的编程/擦除的寿命:即,最多允许的擦除的次数
以前老的Nand Flash,编程/擦除时间比较短,比如K9G8G08U0M,才5K次,而后来的多数也只有10K=1万次,
而现在很多新的Nand Flash,技术提高了,比如,Micron的MT29F1GxxABB,Numonyx的 NAND04G-B2D/NAND08G-BxC,
都可以达到100K,也就是10万次的编程/擦除,达到和接近于之前常见的Nor Flash,几乎是同样的使用寿命了。
1.2.14. Nand Flash中页的访问顺序
在一个块内,对每一个页进行编程的话,必须是顺序的,而不能是随机的。
比如,一个块中有128个页,那么你只能先对page0编程,再对page1编程,。。。。,
而不能随机的,比如先对page3,再page1,page2,page0,page4,。。。
关于此处对于只能顺序给页编程的说法,只是翻译自datasheet,但是实际情况却发现是,程序中没有按照此逻辑处理,
可以任意对某Block内的Page去做Program的动作,而不必是顺序的。
但是datasheet为何如此解释,原因未知,有待知情者给解释一下。
1.2.15. 常见的Nand Flash的操作
图 1.6. Nand Flash K9K8G08U0A的命令集合
从上图可以看到,如果要实现读一个页的数据,就要发送Read的命令,而且是分两个周期(Cycle),
即分两次发送对应的命令,第一次是0x00h,第二次是0x30h,而两次命令中间,
需要发送对应的你所要读取的页的地址,关于此部分详细内容,留待后表。
对应地,其他常见的一些操作,比如写一个页的数据(Page Program),
就是先发送0x80h,然后发生要写入的地址,再发送0x10h。
对于不同厂家的不同型号的Nand Flash 的基本操作,
即读页数据Read Page,写页数据(对页进行编程)Page Program,擦除整个块的数据Erase Block等操作,
所用的命令都是一样的,但是针对一些Nand Flash的高级的一些特性,比如
交错页编程(Interleave Page Program),多片同时编程(Simultaneously Program Multi Plane)等,
所用的命令,未必一样,不过对于同一厂家的Nand Flash的芯片,那一般来说,都是统一的。
1.2.15.1. 页编程(Page Program)注意事项
Nand flash的写操作叫做编程Program,编程,一般情况下,是以页为单位的。
有的Nand Flash,比如K9K8G08U0A,支持部分页编程(Partial Page Program),
但是有一些限制:在同一个页内的,连续的部分页的编程,不能超过4次。
一般情况下,都是以页为单位进行编程操作的,很少使用到部分页编程。
关于这个部分页编程,本来是一个页的写操作,却用两个命令或更多的命令去实现,
看起来是操作多余,效率不高,但是实际上,有其特殊考虑:
至少对于块擦除来说,开始的命令0x60是擦除设置命令(erase setup comman),然后传入要擦除的块地址,
然后再传入擦除确认命令(erase confirm command)0xD0,以开始擦除的操作。
这种完成单个操作要分两步发送命令的设计,即先开始设置,再最后确认的命令方式,
是为了避免由于外部由于无意的/未预料而产生的噪音,比如,由于某种噪音,而产生了0x60命令,
此时,即使被Nand Flash误认为是擦除操作,但是没有之后的确认操作0xD0,
Nand Flash就不会去擦除数据,这样使得数据更安全,不会由于噪音而误操作。
1.2.15.2. 读(Read)操作过程详解
解释时序图之前,让我们先要搞清楚,我们要做的事情:从Nand Flash的某个页Page里面,读取我们要的数据。
要实现此功能,会涉及到几部分的知识,即使我们不太懂Nand Flash的细节,
但是通过前面的基本知识的介绍,那么以我们的常识,至少很容易想到的就是,
需要用到哪些命令,怎么发这些命令,怎么计算所需要的地址,怎么读取我们要的数据等等。
下面就一步步的解释,需要做什么,以及如何去做:
1.2.15.2.1. 需要使用何种命令
我们知道,要读取数据,要用到Read命令,该命令需要2个周期,第一个周期发0x00,第二个周期发0x30。
1.2.15.2.2. 发送命令前的准备工作以及时序图各个信号的具体含义
以防有些读者和我以前一样,在听这类词语的时候,属于初次接触,或者接触不多的,就很容易被搞得一头雾水的
(虽然该词汇对于某些专业人士说是属于最基本的词汇了,囧)。
使能(Enable),是指使其(某个信号)有效,使其生效的意思,“使其”“能够”怎么怎么样。。。。
比如,上面图中的CLE线号,是高电平有效,如果此时将其设为高电平,我们就叫做,将CLE使能,也就是使其生效的意思。
黄色竖线所处的时刻,是在发送读操作的第一个周期的命令0x00之前的那一刻。
让我们看看,在那一刻,其所穿过好几行都对应什么值,以及进一步理解,为何要那个值。
- 黄色竖线穿过的第一行,是CLE。还记得前面介绍命令所存使能(CLE)那个引脚吧?
CLE,将CLE置1,就说明你将要通过I/O复用端口发送进入Nand Flash的,是命令,
而不是地址或者其他类型的数据。只有这样将CLE置1,使其有效,才能去通知了内部硬件逻辑,
你接下来将收到的是命令,内部硬件逻辑,才会将受到的命令,放到命令寄存器中,
才能实现后面正确的操作,否则,不去将CLE置1使其有效,硬件会无所适从,不知道你传入的到底是数据还是命令了。 - 而第二行,是CE#,那一刻的值是0。这个道理很简单,你既然要向Nand Flash发命令,
那么先要选中它,所以,要保证CE#为低电平,使其有效,也就是片选有效。 - 第三行是WE#,意思是写使能。因为接下来是往Nand Flash里面写命令,
所以,要使得WE#有效,所以设为低电平。 - 第四行,是ALE是低电平,而ALE是高电平有效,此时意思就是使其无效。
而对应地,前面介绍的,使CLE有效,因为将要数据的是命令(此时是发送图示所示的读命令第二周期的0x30),
而不是地址。如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使CLE无效了。 - 第五行,RE#,此时是高电平,无效。可以看到,知道后面低6阶段,
才变成低电平,才有效,因为那时候,要发生读取命令,去读取数据。 - 第六行,就是我们重点要介绍的,复用的输入输出I/O端口了,
此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据/地址。 - 第七行,R/B#,高电平,表示R(Ready)/就绪,因为到了后面的第5阶段,硬件内部,
在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,
这段时间,属于系统在忙着干活,属于忙的阶段,所以,R/B#才变成低,表示Busy忙的状态的。
介绍了时刻①的各个信号的值,以及为何是这个值之后,相信,后面的各个时刻,
对应的不同信号的各个值,大家就会自己慢慢分析了,也就容易理解具体的操作顺序和原理了。
1.2.15.2.3. 如何计算出我们要传入的行地址和列地址
在介绍具体读取数据的详细流程之前,还要做一件事,那就是,先要搞懂我们要访问的地址,
以及这些地址,如何分解后,一点点传入进去,使得硬件能识别才行。
此处还是以K9K8G08U0A为例,此Nand Flash,一共有8192个块,每个块内有64页,每个页是2K+64 Bytes。
物理地址 =块大小×块号 + 页大小×页号 + 页内地址
接下来,我们就看看,怎么才能把这个实际的物理地址,转化为Nand Flash所要求的格式。
在解释地址组成之前,先要来看看其datasheet中关于地址周期的介绍:
图 1.8. Nand Flash的地址周期组成
此Nand Flash地址周期共有5个,2个列(Column)周期,3个行(Row)周期。
- 对应地,列地址A0~A10,就是页内地址,地址范围是从 0 到 2047。
细心的读者可能注意到了,为何此处多出来个A11呢?
这样从A0到A11,一共就是12位,可以表示的范围就是0~212,即从 0 到 4096。
实际上,由于我们访问页内地址,可能会访问到oob的位置,即2048-2111这64个字节的范围内,
所以,此处实际上只用到了2048~2111,用于表示页内的oob区域,其大小是64字节。 - 对应地,A12~A17,称作页号,页的号码,可以定位到对应的块的哪一个页 ( 0 .. 63 )。
- A18~A30,表示对应的块号,即属于哪个块 ( 0..8191 )。
1.2.15.2.4. 读操作过程的解释
准备工作终于完了,下面就可以开始解释说明,对于读操作的,上面图中标出来的,1-6个阶段,具体是什么含义。
操作准备阶段:此处是读(Read)操作,所以,先发一个图5中读命令的第一个阶段的0x00,表示,让硬件先准备一下,接下来的操作是读。
发送两个周期的列地址。也就是页内地址,表示,我要从一个页的什么位置开始读取数据。
接下来再传入三个行地址。对应的也就是页号。
然后再发一个读操作的第二个周期的命令0x30。接下来,就是硬件内部自己的事情了。
Nand Flash内部硬件逻辑,负责去按照你的要求,根据传入的地址,找到哪个块中的哪个页,然后把整个这一页的数据,都一点点搬运到页缓存中去。
而在此期间,你所能做的事,也就只需要去读取状态寄存器,看看对应的位的值,也就是R/B#那一位,是1还是0,
如果是0的话,就表示,系统是busy,仍在”忙“(着读取数据),
如果是1,就说系统活干完了,忙清了,已经把整个页的数据都搬运到页缓存里去了,你可以接下来读取你要的数据了。
对于这里。估计有人会问了,这一个页一共2048+64字节,如果我传入的页内地址,就像上面给的1208一类的值,
只是想读取1028到2011这部分数据,而不是页开始的0地址整个页的数据,那么内部硬件却读取整个页的数据出来,岂不是很浪费吗?
答案是,的确很浪费,效率看起来不高,但是实际就是这么做的,而且本身读取整个页的数据,相对时间并不长,而且读出来之后,
内部数据指针会定位到你刚才所制定的1208的那个位置。