说说NAND FLASH以及相关ECC校验方法
Flash名称的由来,Flash的擦除操作是以block块为单位的,与此相对应的是其他很多存储设备,是以bit位为最小读取/写入的单位,Flash是一次性地擦除整个块:在发送一个擦除命令后,一次性地将一个block,常见的块的大小是128KB/256KB,全部擦除为1,也就是里面的内容全部都是0xFF了,由于是一下子就擦除了,相对来说,擦除用的时间很短,可以用一闪而过来形容,所以,叫做Flash Memory。所以一般将Flash翻译为 (快速)闪存。
NAND Flash 在嵌入式系统中有着广泛的应用,负载平均和坏块管理是与之相关的两个核心议题。Uboot 和 Linux 系统对 NAND 的操作都封装了对这两个问题的处理方法。 本文首先讲述Nandflash基础知识,然后介绍现有的几类坏块管理(BBM)方法,通过分析典型嵌入式系统的 NAND 存储表,指出了轻量级管理方法的优势所在,分析了当前广泛使用的轻量级管理方法,指出其缺陷所在并详细说明了改进方法。
基础知识
Flash的硬件实现机制
Flash的内部存储是MOSFET,里面有个悬浮门(Floating Gate),是真正存储数据的单元。
在Flash之前,紫外线可擦除(uv-erasable)的EPROM,就已经采用了Floating Gate存储数据这一技术了。
典型的Flash内存物理结构 
数据在Flash内存单元中是以电荷(electrical charge) 形式存储的。存储电荷的多少,取决于图中的外部门(external gate)所被施加的电压,其控制了是向存储单元中冲入电荷还是使其释放电荷。而数据的表示,以所存储的电荷的电压是否超过一个特定的阈值Vth来表示,因此,Flash的存储单元的默认值,不是0(其他常见的存储设备,比如硬盘灯,默认值为0),而是1,而如果将电荷释放掉,电压降低到一定程度,表述数字0。
NandFlash的简介 
Nand flash成本相对低,说白了就是便宜,缺点是使用中数据读写容易出错,所以一般都需要有对应的软件或者硬件的数据校验算法,统称为ECC。但优点是,相对来说容量比较大,现在常见的Nand Flash都是1GB,2GB,更大的8GB的都有了,相对来说,价格便宜,因此适合用来存储大量的数据。其在嵌入式系统中的作用,相当于PC上的硬盘,用于存储大量数据。 
SLC和MLC 
Nand Flash按照内部存储数据单元的电压的不同层次,也就是单个内存单元中,是存储1位数据,还是多位数据,可以分为SLC和MLC。那么软件如何识别系统上使用过的SLC还是MLC呢? 
Nand Flash设计中,有个命令叫做Read ID,读取ID,读取好几个字节,一般最少是4个,新的芯片,支持5个甚至更多,从这些字节中,可以解析出很多相关的信息,比如此Nand Flash内部是几个芯片(chip)所组成的,每个chip包含了几片(Plane),每一片中的页大小,块大小,等等。在这些信息中,其中有一个,就是识别此flash是SLC还是MLC。
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中,存放很多和自己文件系统相关的信息。
 
Bad Block Management坏块管理
Nand Flash由于其物理特性,只有有限的擦写次数,超过那个次数,基本上就是坏了。在使用过程中,有些Nand Flash的block会出现被用坏了,当发现了,要及时将此block标注为坏块,不再使用。于此相关的管理工作,属于Nand Flash的坏块管理的一部分工作。
Wear-Leveling负载平衡
Nand Flash的block管理,还包括负载平衡。
正是由于Nand Flash的block,都是有一定寿命限制的,所以如果你每次都往同一个block擦除然后写入数据,那么那个block就很容易被用坏了,所以我们要去管理一下,将这么多次的对同一个block的操作,平均分布到其他一些block上面,使得在block的使用上,相对较平均,这样相对来说,可以更能充分利用Nand Flash。
ECC错误校验码
Nand Flash物理特性上使得其数据读写过程中会发生一定几率的错误,所以要有个对应的错误检测和纠正的机制,于是才有此ECC,用于数据错误的检测与纠正。Nand Flash的ECC,常见的算法有海明码和BCH,这类算法的实现,可以是软件也可以是硬件。不同系统,根据自己的需求,采用对应的软件或者是硬件。
相对来说,硬件实现这类ECC算法,肯定要比软件速度要快,但是多加了对应的硬件部分,所以成本相对要高些。如果系统对于性能要求不是很高,那么可以采用软件实现这类ECC算法,但是由于增加了数据读取和写入前后要做的数据错误检测和纠错,所以性能相对要降低一些,即Nand Flash的读取和写入速度相对会有所影响。
其中,Linux中的软件实现ECC算法,即NAND_ECC_SOFT模式,就是用的对应的海明码。
而对于目前常见的MLC的Nand Flash来说,由于容量比较大,动辄2GB,4GB,8GB等,常用BCH算法。BCH算法,相对来说,算法比较复杂。
笔者由于水平有限,目前仍未完全搞懂BCH算法的原理。
BCH算法,通常是由对应的Nand Flash的Controller中,包含对应的硬件BCH ECC模块,实现了BCH算法,而作为软件方面,需要在读取数据后,写入数据之前,分别操作对应BCH相关的寄存器,设置成BCH模式,然后读取对应的BCH状态寄存器,得知是否有错误,和生成的BCH校验码,用于写入。
其具体代码是如何操作这些寄存器的,由于是和具体的硬件,具体的nand flash的controller不同而不同,无法用同一的代码。如果你是nand flash驱动开发者,自然会得到对应的起nand flash的controller部分的datasheet,按照手册说明,去操作即可。
不过,额外说明一下的是,关于BCH算法,往往是要从专门的做软件算法的厂家购买的,但是Micron之前在网上放出一个免费版本的BCH算法。
位反转
Nand Flash的位反转现象,主要是由以下一些原因/效应所导致:
- 漂移效应(Drifting Effects)
漂移效应指的是,Nand Flash中cell的电压值,慢慢地变了,变的和原始值不一样了。
 - 编程干扰所产生的错误(Program-Disturb Errors)
此现象有时候也叫做,过度编程效应(over-program effect)。
对于某个页面的编程操作,即写操作,引起非相关的其他的页面的某个位跳变了。
 - 读操作干扰产生的错误(Read-Disturb Errors)
此效应是,对一个页进行数据读取操作,却使得对应的某个位的数据,产生了永久性的变化,即Nand Flash上的该位的值变了。
 
对应位反转的类型,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本身包含对应的硬件模块以实现数据的校验和纠错的。

Nand Flash的一些typical特性
- 页擦除时间是200us,有些慢的有800us
 - 块擦除时间是1.5ms
 - 页数据读取到数据寄存器的时间一般是20us
 - 串行访问(Serial access)读取一个数据的时间是25ns,而一些旧的Nand Flash是30ns,甚至是50ns
 - 输入输出端口是地址和数据以及命令一起multiplex复用的
 - Nand Flash的编程/擦除的寿命:即,最多允许10万次的编程/擦除,达到和接近于之前常见的Nor Flash,几乎是同样的使用寿命了。
 - 封装形式:48引脚的TSOP1封装 或 52引脚的ULGA封装
 
Nand Flash控制器与Nand Flash芯片
我们写驱动,是写Nand Flash 控制器的驱动,而不是Nand Flash 芯片的驱动,因为独立的Nand Flash芯片,一般来说,是很少直接拿来用的,多数都是硬件上有对应的硬件的Nand Flash的控制器,去操作和控制Nand Flash,包括提供时钟信号,提供硬件ECC校验等等功能,我们所写的驱动软件,是去操作Nand Flash的控制器
然后由控制器去操作Nand Flash芯片,实现我们所要的功能。
由于Nand Flash读取和编程操作来说,一般最小单位是页,所以Nand Flash在硬件设计时候,就考虑到这一特性,对于每一片(Plane),都有一个对应的区域专门用于存放,将要写入到物理存储单元中去的或者刚从存储单元中读取出来的,一页的数据,这个数据缓存区,本质上就是一个缓存buffer,但是只是此处datasheet里面把其叫做页寄存器page register而已,实际将其理解为页缓存,更贴切原意。
而正是因为有些人不了解此内部结构,才容易产生之前遇到的某人的误解,以为内存里面的数据,通过Nand Flash的FIFO,写入到Nand Flash里面去,就以为立刻实现了实际数据写入到物理存储单元中了,而实际上只是写到了这个页缓存中,只有当你再发送了对应的编程第二阶段的确认命令,即0x10,之后,实际的编程动作才开始,才开始把页缓存中的数据,一点点写到物理存储单元中去。
坏块的标记
具体标记的地方是,对于现在常见的页大小为2K的Nand Flash,是块中第一个页的oob起始位置的第1个字节(旧的小页面,pagesize是512B甚至256B的Nand Flash,坏块标记是第6个字节),如果不是0xFF,就说明是坏块。相对应的是,所有正常的块,好的块,里面所有数据都是0xFF的。
对于坏块的标记,本质上,也只是对应的flash上的某些字节的数据是非0xFF而已,所以,只要是数据,就是可以读取和写入的。也就意味着,可以写入其他值,也就把这个坏块标记信息破坏了。对于出厂时的坏块,一般是不建议将标记好的信息擦除掉的。
uboot中有个命令是
nand scrub
就可以将块中所有的内容都擦除了,包括坏块标记,不论是出厂时的,还是后来使用过程中出现而新标记的。
nand erase
只擦除好的块,对于已经标记坏块的块,不要轻易擦除掉,否则就很难区分哪些是出厂时就坏的,哪些是后来使用过程中用坏的了。
Uboot 的轻量级坏块管理方法
NAND 坏块管理都是基于坏块表(BBT)的,通过这张表来标识系统中的所有坏块。所以,不同的管理方法之间的差异可以通过以下几个问题来找到答案。
- 如何初始化和读取坏块表?
 - 产生新的坏块时,如何标记并更新坏块表?
 - 如何保存坏块表?是否有保存时断电保护机制?
 - 对 NAND 写入数据时,如果当前块是坏块,如何找到可替换的好块?
 
Uboot 是目前使用最为广泛的 bootloader,它提供了两种轻量级坏块管理方法,可称之为基本型和改进型。通过下表,我们可以看到两者的差异。

虽然 uboot 的改进型坏块管理方法的做了一些改进,但它仍然有三个主要的缺点。
- 出现坏块,则将数据顺序写入下一个好块。如果 NAND 中存放了多个软件模块,则每个模块都需要预留一个较大的空间作为备用的好块,这会浪费较多的 NAND 空间。通常,每个模块预留的备用好块数为 NAND 芯片所允许的最大坏块数,该值因不同的芯片而有所不同,典型值为 20 或 80。假设 NAND 是大页类型,总共有 N 个模块,则总共需要预留的空间大小为 N*80*128KB。
 - 读取 BBT 时仅检查签名,没有对 BBT 的数据做校验。
 - 没有掉电保护机制。如果在保存 BBT 时断电,BBT 将丢失。
 
针对现有管理方法的缺陷,本文提出了一种更加安全高效的管理方法,将从以下三个方面阐述其实现原理。
共用好块池机制
首先,使用一个统一的备用好块池,为所有存放在 NAND 中的模块提供可替换的好块。这样,就不需要在每个模块后面放置一个保留区,提高了 NAND 的空间利用率。
共用好块池示意图

为了实现共用好块池,需要建立一个从坏块到好块的映射,所以,除了 BBT 之外,还需定义一个替换表(SBT)。这样一来,当读第 i 个块的数据时,如果发现 BBT 中记录该块为坏块,就去 SBT 中查询其替换块;如果写第 i 个块出错,需要在 BBT 中标记该块为坏块,同时从好块池中获取一个新的好块,假设其序号为 j,然后将此好块的序号 j 写入 SBT 中的第 i 个字节,而且 SBT 的第 j 个字节写序号 i。SBT 中的这种双向映射可确保数据的可靠性。此外,好块池中的块也有可能成为坏块,如果扫描时发现是坏块,则将 SBT 中的对应位置标记为 0x00,如果是在写的过程中出错,则除了在 SBT 对应位置标记 0x00 之外,还要更新双向映射数据。
BBT/SBT 映射示意图
安全的 BBT/SBT 数据校验机制
传统方法仅检查 BBT 所在块的签名,将读到的前几个字节和一个特征字符串进行比较,如果一致,就认为当前块的数据为 BBT,然后读取接下来的 BBT 数据,但并不对 BBT 的数据做校验。如果 BBT 保存在 NAND 中,数据的有效性是可以得到验证的,因为 NAND 控制器或驱动一般都会对数据做 ECC 校验。但是,大多数控制器使用的 ECC 算法也仅仅能纠正一个 bit、发现 2 两个 bit 的错误。如果 BBT 保存在其他的没有 ECC 校验机制的存储体中,比如 NOR Flash,没有对 BBT 的数据进行校验显然是不安全的。
为了更加可靠和灵活地验证 BBT/SBT 数据,定义下面这个结构体来描述 BBM 信息。
BBM 头信息
typedef struct {
UINT8     acSignature[4];/* BBM 签名 */
UINT32    ulBBToffset;/* BBT 偏移 */
UINT32    ulSBToffset;/* SBT 偏移 */
UINT16    usBlockNum;/* BBM 管理的 block 数目 */
UINT16    usSBTstart;/* SBT 所在位置的起始 block 序号 */
UINT16    usSBtop;/* SBT top block */
UINT16    usSBnum;/* SBT number */
UINT32    ulBBTcrc;/* BBT 数据 CRC 校验码 */
UINT32    ulSBTcrc;/* SBT 数据 CRC 校验码 */
UINT32    ulHeadcrc;/* BBM 头信息 CRC 校验码 */
} BBM_HEAD
BBT/SBT 的保存形式

使用三重 CRC 校验机制,无论 BBT 保存在哪种存储体中,都可以更加严格地验证数据的有效性。
安全的掉电保存机制
传统的方法仅保存一份 BBT 数据,如果在写 BBT 时系统掉电,则 BBT 丢失,系统将可能无法正常启动或工作。为安全起见,本文所述方法将同时保留三个备份,如果在写某个备份时掉电,则还有两个完好的备份。最坏的情况是,如果在写第一个备份时掉电,则当前最新的一个坏块信息丢失。
读取坏块表时,顺序读取三个备份,如果发现三个备份的数据不一致,用记录的坏块数最多的备份为当前的有效备份,同时立刻更新另外两备份。
总结
本文介绍了NandFlash基础知识和几类 NAND 坏块管理方法,指出了 uboot 的轻量级管理方法的缺陷,提出了一种改进的方法,提高了 NAND 的利用率及坏块管理的安全性,可对嵌入式开发起到有很好的借鉴作用。
ECC定义
技术原理
示例
说说NAND FLASH以及相关ECC校验方法的更多相关文章
- 转 -- OK6410 tftp下载内核、文件系统以及nand flash地址相关整理、总结
		
转载地址:http://emouse.cnblogs.com/ 飞凌官方提供了一键下载烧写linux的方式,相对来说比较方便,但是对于开发来说不够灵活,因此这篇文章把tftp相关的点介绍一下,整理下其 ...
 - nand flash相关
		
关于nandflash的说明,请参考其他. 现在先贴出来韦东山先生的代码,作我学习之用. @************************************************ @ Fil ...
 - nand flash详解及驱动编写
		
https://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html#nand_ ...
 - NAND Flash结构及驱动函数
		
目标:以NAND Flash K9F2G08U0M为例介绍其结构及其驱动程序的书写 1. 结构 由芯片手册中的图可知:K9F2G08U0M大小为2112Mbits(即 256MB = 2Gb ) 共有 ...
 - 嵌入式Linux学习笔记 NAND Flash控制器
		
一.NAND Flash介绍和NAND Flash控制器的使用 NAND Flash在嵌入式系统中的作用,相当于PC上的硬盘 常见的Flash有NOR Flash和NAND Flash,NOR Fla ...
 - Linux学习 : 裸板调试 之 配置使用NAND FLASH
		
关于NAND FLASH的结构是以页为单位写,以块为单位来擦除: 1Gb 为大页 page=2048Kb BLOCK=128K 512Mb 为小页 page=512byte ...
 - 四、NAND Flash
		
4.1 nand flash启动u-boot nand flash 启动的时候,CPU 需要将 nand flash 中前面 4KB 的内容复制到 SRAM 中执行,然后将 NAND Flash 中的 ...
 - S5PV210 NAND Flash
		
NAND Flash 关于NAND FlashS5PV210的NAND Flash控制器有如下特点:1) 支持512byte,2k,4k,8k的页大小2) 通过各种软件模式来进行NAND Flash的 ...
 - Nand flash code
		
(1)流水灯 1>我们来看原理图 2>datasheet 3> ...
 
随机推荐
- Java中常见的异常__
			
作为一名游戏开发者,程序员,很自然必须熟悉对程序的调试方法.而要调试程序,自然需要对程序中的常见的异常有一定的了解,这些日子很多朋友都提出了很多问题,都是关于游戏中的报错,因此在这里我将一些常见的程序 ...
 - win10无法访问局域网共享文件?(因微软账户和本地账户登陆问题导致)
			
1 笔记本系统win10 X64企业版,其中一文件夹已设置为“共享”.本地帐号登录系统. 2 平板电脑系统win8.1 X64专业版,可以顺畅的访问笔记本的共享文件.微软帐号登录系统. 3 平板电脑系 ...
 - 双行表头DatagridView的简单实现
			
DatagridView默认不支持多行表头的实现,一些第三方的控件,比如Spread就可以,因此要实现这个功能,只能自己想办法了.介绍两种思路:1,用重写DataGridView的Paint等方法,可 ...
 - 多个git账号之间的切换
			
在这篇文章中: 介绍 处理 一些题外话 我有一个repo,想要同时push到不同的仓库该如何设置? 我有一个github的repo,clone没有问题,push的时候总是报错:error: The r ...
 - Python 随机数,break,continue
			
#-*- coding:utf-8 -*- #导入模块 import random #打印10以内的随机数 num = 5 while num > 0: #random.randint(0,10 ...
 - Struts2中jsp前台传值到action后台的三种方式以及valueStack的使用
			
struts2中的Action接收表单传递过来的参数有3种方法: 如,登陆表单login.jsp: <form action="login" method="pos ...
 - 【转】【C++】__stdcall、__cdcel和__fastcall三者的区别
			
__stdcall.__cdecl和__fastcall是三种函数调用协议,函数调用协议会影响函数参数的入栈方式.栈内数据的清除方式.编译器函数名的修饰规则等.如下图所示,可以在IDE环境中设定所有函 ...
 - (转)V4L2 Video overlay, Video output, Video output overlay的区别
			
原文地址:http://blog.csdn.net/kickxxx/article/details/7755127 三者都是V4L2定义的接口,英文原文参见 http://v4l2spec.bytes ...
 - 关于在SQLITE数据库表中插入本地系统时间的做法
			
首先,我参考下面的博文地址:http://blog.csdn.net/liuzhidong123/article/details/6847104 sqlite3 表里插入系统时间(时间戳) 分类: s ...
 - linux -- Ubuntu开启root账户,并切换到root用户登陆
			
启用root账户 ubuntu 的root账户具有最高的系统权限,它类似于windows系统中的管理员账号,但是比windows系统中管理员账号的权限更高,一般都情况下不要使用root账户,但是有的时 ...
 
			
		