6.1.3) Block 块
一个 Nand Flash (的 chip,芯片) 由很多个块(Block)组成,块的大小一般是 128KB, 256KB, 512KB,此处是 128KB。其他的小于 128KB 的,比如 64KB,一般都是下面将要介绍到的small block的Nand Flash。 块 Block,是Nand Flash的擦除操作的基本/最小单位。
6.1.4) 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页)子页的读写操作,不过一般很少见。
6.1.5) oob / Redundant Area / Spare Area
每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),而Linux 系统中,一般叫做 OOB(Out Of Band),这个区域,是最初基于Nand Flash的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做 EDC(Error Detection Code)/所以设计了多余的区域,用于放置数据的校验值。Oob 的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了 oob。
关于 oob具体用途,总结起来有:
标记是否是坏快
存储ECC数据
存储一些和文件系统相关的数据。如 jffs2 就会用到这些空间存储一些特定信息,而yaffs2 文件系统,会在 oob中,存放很多和自己文件系统相关的信息
6.1.6)NANDFLASH常用命令设置
从上图可以看到,如果要实现读一个页的数据,就要发送 Read的命令,而且是分两个周期(Cycle),即分两次发送对应的命令,第一次是 0x00h,第二次是 0x30h,而两次命令中间,需要发送对应的你所要读取的页的地址。
对应地,其他常见的一些操作,比如写一个页的数据(Page Program),就是先发送 0x80h,然后发生要写入的地址,再发送0x10h。
Notes:#代表低电平有效
6.1.8)理解NANDFLASH的操作时序图以Read Operation为例
Notes:此图来自三星的型号 K9F2G08的 Nand Flash的数据手册(datasheet)。
我们来一起看看,我在上图的特意标注的①边上的红色竖线。 红色竖线所处的时刻,是在发送读操作的第一个周期的命令 0x00之前的那一刻。 让我们看看,在那一刻,其所穿过好几行都对应什么值,以及进一步理解,为何要那个值。
1)红色竖线穿过的第一行,是 CLE。前面介绍命令所存使能(CLE)的那个引脚将CLE 置 1,就说明你将要通过 I/O 复用端口发送进入Nand Flash的,是命令,而不是地址或者其他类型的数据。只有这样将 CLE 置 1,使其有效,才能去通知了内部硬件逻辑,你接下来将收到的是命令,内部硬件逻辑,才会将受到的命令,放到命令寄存器中,才能实现后面正确的操作,否则,不去将 CLE 置 1 使其有效,硬件会无所适从,不知道你传入的到底是数据还是命令了。
2)而第二行,是 CE#,那一刻的值是 0。这个道理很简单,你既然要向Nand Flash发命令,那么先要选中它,所以,要保证 CE#为低电平,使其有效,也就是片选有效。
3)第三行是 WE#,意思是写使能。因为接下来是往 Nand Flash里面写命令,所以,要使得 WE#有效,所以设为低电平。
4)第四行,是 ALE 是低电平,而 ALE 是高电平有效,此时意思就是使其无效。而对应地,前面介绍的,使 CLE 有效,因为将要数据的是命令(此时是发送图示所示的读命令第二周期的 0x30) ,而不是地址。如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使 CLE 无效了。
5)第五行,RE#,此时是高电平,无效。可以看到,直到后面第 6 阶段,才变成低电平,才有效,因为那时候,要发生读取命令,去读取数据。
6)第六行,就是我们重点要介绍的,复用的输入输出 I/O 端口了,此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据/地址。
7)第七行,R/B#,高电平,表示 R(Ready)/就绪,因为到了后面的第 5阶段,硬件内部,在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,这段时间,属于系统在忙着干活,属于忙的阶段,所以,R/B#才变成低,表示 Busy忙的状态的。 其他的时序的就类似的理解。
2. NAND Flash 中的坏块(Bad Block)
NAND Flash 中,一个块中含有 1 个或多个位是坏的,就称为其为坏块 Bad Block。坏块的稳定性是无法保证的,也就是说,不能保证你写入的数据是对的,或者写入对了,读出来也不一定对的。与此对应的正常的块,肯定是写入读出都是正常的。坏块有两种:
(1) 出厂时就有存在的坏块:
一种是出厂的时候,也就是,你买到的新的,还没用过的 NAND Flash,就可以包含了坏块。此类出厂时就有的坏块,被称作 factory (masked) bad block 或 initial bad/invalid block,在出厂之前,就会做对应的标记,标为坏块。
(2) 使用过程中产生的坏块:
第二类叫做在使用过程中产生的,由于使用过程时间长了,在擦块除的时候,出错了,说明此块坏了,也要在程序运行过程中,发现,并且标记成坏块的。具体标记的位置,和上面一样。这类块叫做 worn-out bad block。即用坏了的块。
2.1坏块的标记
具体标记的地方是,对于现在常见的页大小为2K的 NAND Flash,是块中第一个页的 oob 起始位置的第1个字节(旧的小页面,pagesize是512B至256B的 NAND Flash,坏块标记是第 6 个字节)。如果不是 0xFF, 就说明是坏块。相对应的是,所有正常的块,好的块,里面所有数据都是 0xFF 的。不过,对于现在新出的有些 Nand Flash,很多标记方式,有些变化,有的变成该坏块的第一个页或者第二个页,也有的是,倒数最后一个或倒数第二个页,用于标记坏块的。具体的信息,请参考对应的 Nand Flash 的数据手册,其中会有说明。对于坏块的标记,本质上,也只是对应的 flash 上的某些字节的数据是非 0xFF 而已,所以,只要是数据,就是可以读取和写入的。也就意味着,可以写入其他值,也就把这个坏块标记信息破坏了。对于出厂时的坏块,一般是不建议将标记好的信息擦除掉的。uboot 中有个命令是“nand scrub”就可以将块中所有的内容都擦除了,包括坏块标记,不论是出厂时的,还是后来使用过程中出现而新标记的。一般来说,不建议用这个。其实最好的做法是,用“nand erase”只擦除好的块,对于已经标记坏块的块,不要轻易擦除掉,否则就很难区分哪些是出厂时就坏的,哪些是后来使用过程中用坏的了。
2.2坏块的管理
对于坏块的管理,在Linux系统中,叫做坏块管理(BBM,Bad Block Management),对应的会有一个表去记录好块,坏块的信息,以及坏块是出厂就有的,还是后来使用产生的,这个表叫做坏块表(BBT,Bad Block Table) 。在 Linux 内核 MTD 架构下的Nand Flash驱动,和 Uboot 中 NAND Flash 驱动中,在加载完驱动之后,如果你没有加入参数主动要求跳过坏块扫描的话,那么都会去主动扫描坏块,建立必要的 BBT 的,以备后面坏块管理所使用。
前两个周期发送A0~A11,为列地址,即页内地址。A0-A10数据寻址。A11坏块标识寻址,即页内最后64Byte的地址。后三个周期发送A12~A28,为行地址,即页地址。但是程序中获取页地址是把地址addr右移11位,而不是12位。开始一直很困惑。经过一番探索之后才理解了其中缘由。首先需要了解NAND Flash页内地址的结构,它分为Main Field(2K)和Spare Field(64Byte)两部分,而我们的数据是存放在2K当中,普通的数据读写不对后面64Byte操作,使用A0~A10地址就可实现对2K数据存储区的寻址。下图为K9F2G08U0B页内结构:
函数nand_read_page_ll传递进来的addr地址没有包含对Sapre Field的寻址。所以只移11位,而不是移12位。NAND Flash读取数据是以页为单位,当NAND Flash接收到地址后,会把该页内addr&0x7F地址之后的数据依次输出包括Spare Field。在nand_read_page_ll函数里,列地址为0,读取此页所有数据。所以for循环读数据时,只读前2K的内容后就跳出循环。