NandFlash驱动编写知识详解与坏块管理
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
【Nand Flash驱动编写之前要了解的知识】
1.硬件特性:
【Flash的硬件实现机制】
Flash全名叫做Flash Memory,属于非易失性存储设备(Non-volatile Memory Device),与此相对应的是易失性存储设备(Volatile Memory Device)。
关于什么是非易失性/易失性,从名字中就可以看出,非易失性就是不容易丢失,数据存储在这类设备中,即使断电了,也不会丢失,这类设备,除了Flash,还有其他比较常见的入硬盘,ROM等,与此相对的,易失性就是断电了,数据就丢失了,比如大家常用的内存,不论是以前的SDRAM,DDR SDRAM,还是现在的DDR2,DDR3等,都是断电后,数据就没了。
Flash的内部存储是MOSFET,里面有个悬浮门(Floating Gate),是真正存储数据的单元。
在Flash之前,紫外线可擦除(uv-erasable)的EPROM,就已经采用用Floating Gate存储数据这一技术了。
图1.典型的Flash内存单元的物理结构
数据在Flash内存单元中是以电荷(electrical charge) 形式存储的。
存储电荷的多少,取决于图中的外部门(external gate)所被施加的电压,其控制了是向存储单元中冲入电荷还是使其释放电荷。
而数据的表示,以所存储的电荷的电压是否超过一个特定的阈值Vth来表示。
【SLC和MLC的实现机制】
Nand Flash按照内部存储数据单元的电压的不同层次,也就是单个内存单元中,是存储1位数据,还是多位数据,可以分为SLC和MLC:
1.SLC,Single Level Cell:
单个存储单元,只存储一位数据,表示成1或0.
就是上面介绍的,对于数据的表示,单个存储单元中内部所存储电荷的电压,和某个特定的阈值电压Vth,相比,如果大于此Vth值,就是表示1,反之,小于Vth,就表示0.
对于nand Flash的数据的写入1,就是控制External Gate去充电,使得存储的电荷够多,超过阈值Vth,就表示1了。
而对于写入0,就是将其放电,电荷减少到小于Vth,就表示0了。
关于为何Nand Flash不能从0变成1,我的理解是,物理上来说,是可以实现每一位的,从0变成1的,但是实际上,对于实际的物理实现,出于效率的考虑,如果对于,每一个存储单元都能单独控制,即,0变成1就是,对每一个存储单元单独去充电,所需要的硬件实现就很复杂和昂贵,同时,所进行对块擦除的操作,也就无法实现之前的,一闪而过的速度了,也就失去了Flash的众多特性了。
2.MLC,Multi Level Cell:
与SLC相对应,就是单个存储单元,可以存储多个位,比如2位,4位等。
其实现机制,说起来比较简单,就是,通过控制内部电荷的多少,分成多个阈值,通过控制里面的电荷多少,而达到我们所需要的存储成不同的数据。
比如,假设输入电压是Vin=4V(实际没有这样的电压,此处只是为了举例方便),那么,可以设计出2的2次方=4个阈值, 1/4 的
图2.Nand Flash物理存储单元的阵列组织结构
上图是K9K8G08U0A的datasheet中的描述。
简单解释就是:
1.一个nand flash由很多个块(Block)组成,块的大小一般是128KB,256KB,512KB,此处是128KB。
2.每个块里面又包含了很多页(page)。
每个页的大小,对于现在常见的nand flash多数是2KB,更新的nand flash是4KB,这类的,页大小大于2KB的nand flash,被称作big block,对应的发读写命令地址,一共5个周期(cycle),而老的nand flash,页大小是256B,512B,这类的nand flash被称作small block,。
地址周期只有4个。
而块,也是Nand Flash的擦除操作的基本/最小单位。
3.每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),而Linux系统中,一般叫做OOB(Out Of Band),这个区域,是最初基于Nand Flash的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做EDC(Error Detection Code)/ECC(Error Code Correction, 或者 Error Checking and Correcting),所以设计了多余的区域,用于放置数据的校验值。
页是Nand Flash的写入操作的基本/最小的单位。
【Nand Flash数据存储单元的整体架构】
简单说就是,常见的nand flash,内部只有一个chip,每个chip只有一个plane。
而有些复杂的,容量更大的nand flash,内部有多个chip,每个chip有多个plane。
这类的nand flash,往往也有更加高级的功能,比如下面要介绍的Multi Plane Program和Interleave Page Program等。
比如,型号为K9K8G08U0A这个芯片(chip),内部有两个K9F4G08U0A,每个K9F4G08U0A包含了2个Plane,每个Plane是1Gb,所以K9F4G08U0A的大小是1Gb×2=2Gb=256MB,因此,K9K8G08U0A内部有2个K9F4G08U0A,即4个Plane,总大小是4×256MB=1GB。
而型号是K9WAG08U1A的nand flash,内部包含了2个K9K8G08U0A,所以,总容量是K9K8G08U0A的两倍=1GB
×2=2GB,类似地K9NBG08U5A,内部包含了4个K9K8G08U0A,总大小就是4×1GB=4GB。
【Flash名称的由来】
Flash的擦除操作是以block块为单位的,与此相对应的是其他很多存储设备,是以bit位为最小读取/写入的单位,Flash 是一次性地擦除整个块:在发送一个擦除命令后,一次性地将一个block,常见的块的大小是128KB/256KB。
,全部
单片机控制Nand Flash 涉及到的算法:ECC校验,坏块管理,垃圾搜集,负载均衡。
Nand Flash原理分析与编程
NAND Flash 在嵌入式系统中的地位与PC机上的硬盘是类似的。
用于保存系统运行所必需的操作系统,应用程序,用户数据,运行过程中产生的各类数据,系统掉电后数据不会护丢失.本文主要介绍关于NAND Flash的组织结构和编写程序的方法。
在三星的NAND Flash 中,当CPU从NAND Flash开始启动时,CPU会通过内部的硬件将NAND Flash开始的4KB数据复制到称为“Steppingstone”的4KB的内部RAM中,起始地址为0,然后跳到地址0处开始执行。
这也就是我们为什么可以把小于4KB的程序烧到NAND Flash中,可以运行,而当大于4KB时,却没有办法运行,必须借助于NAND Flash的读操作,读取4KB以后的程序到内存中。
NAND Flash的寻址方式和NAND Flash的memory组织方式紧密相关。
NAND Flash的数据是以bit的方式保存在
memory cell(存储单元)。
一般情况下,一个cell中只能存储一个bit。
这些cell以8个或者16个为单位,连成bit line ,形成所谓的byte(x8)/word(x16),这就是NAND Flash的位宽。
这些Line会再组成Pape(页)。
然后是每32个page形成一个Block,所以一个Block(块)大小是16k.Block是NAND Flash中最大的操作单元,其中的擦除操作是以Block为单位进行擦除的,而读写和编程是以page为单位进行操作的,并且读写之前必须进行flash的擦写。
我们这里以三星K9F1208U0M的NAND Flash 为例,它的大小是64MB的。
1block = 32page
1page = 512bytes(datafield) + 16bytes(oob)
K9F1208U0B总共有4096 个Blocks,故我们可以知道这块flash的容量为4096 *(32 *528)= 69206016 Bytes = 66 MB 但事实上每个Page上的最后16Bytes是用于存贮检验码用的,并不能存放实际的数据,所以实际上我们可以操作的芯片容量为
4096 *(32 *512) = 67108864 Bytes = 64 MB
Nand Flash 物理结构图
在NAND Flash中有8个I/O引脚(IO0—IO7)、5个全能信号(nWE ALE CLE nCE nRE)、一个引脚,1个写保护引脚。
操作NAND Flash时,先传输命令,然后传输地址,最后读写数据。
对于64MB的NAND Flash,需要一个26位的地址。
只能8个I/O引脚充当地址、数据、命令的复用端口,所以每次传地址只能传8位。
这样就需要4个地址序列。
因此读写一次nand flash需要传送4次(A[7:0] A[16:9] A[24:17] A[25])。
64M的NAND Flash的地址范围为
0x00000000—0x03FFFFFF。
一页有528个字节,而在前512B中存放着用户的数据。
在后面的16字节中(OOB)中存放着执行命令后的状态信息。
主要是ECC校验的标识。
列地址A0-A7可以寻址的范围是256个字节,要寻址528字节的话,将一页分为了A.(1half array)B(2 half array) C(spare array)。
A区0—255字节,B区256-511 字节C区512—527字节。
访问某页时必须选定特定的区。
这可以使地址指针指向特定的区实现。
在NAND Flash 中存在三类地址,分别为Block Address 、Column Address Page Address.。
Column Address 用来选择是在上半页寻址还是在下半页寻址A[0]—A[7].也就相当于页内的偏移地址。
在进行擦除时不需要列地址,因为擦除是以块为单位擦除。
32个Page需要5bit来表示。
也就是A[13:9];也就是页在块内的相对地址。
A8这一位用来设置512字节的上半页,还是下半页,1表示是在上半页,而2表示是在下半页。
Block的地址有A[25:14]组成。
存储操作特点:
1.擦除操作的最小单位是块
2.Nand Flash芯片每一位只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前一定要将相应块擦除(擦除就是将相应块的位全部变为1
3 OOB部分的第六字节(即517字节)标志是否坏块,如果不是坏块该值为FF,否则为坏块
4 除OOB第六字节外,通常至少把OOB前3字节存放Nand Flash硬件ECC码
一个容量为64M(512Mbit)的NAND Flash,分为131072页,528列。
(实际中由于存在spare area,故都大于这个值),有4096块,需要12bit来表示即A[25:14].如果是128M(1Gbit)的话,blodk Address为A[26:14].由于地址只能在
IO0—IO7上传送。
编程时通常通过移位来实现地址的传送。
传送过程如下:
第1个地址序列:传递column address,也就是NAND Flash[7:0],这一周期不需要移位即可传递到I/O[7:0]上,而half page pointer 即A8是由操作指令决定,00h,在A区,01h在B区,指令决定在哪个half page上进行读写,而真正
A8的值是不需要程序员关心的;
第2个地址序列:就是将NAND_ADDR 右移9位,而不是8位,将NAND_ADDR[16:9]传递到I/O[7:0]上;
第3个地址序列:将NAND_ADDR[24:17] 传递到I/O[7:0]上;
第4个地址序列:将NAND_ADDR[25]传送到I/O上。
整个地址的传送过程需要4步才能完成。
如果NAND Flash 的大小是32MB的以下的话,那么block address 最高位只到bit24,因此寻址只需要3步,就可以完成。
在进行擦除操作时由于是以块进行擦除,所以只需要3个地址序列,也就是只传递块的地址,即A[14:25]。
NAND Flash地址的计算:
Column Address 翻译过来是列地址,也就是在一页里的偏移地址。
其实是指定Page上的某个Byte,指定这个Byte,其实也就是指定此页的读写起始地址。
Page Address:页地址。
页的地址总是以512Bytes对齐的,所以它的低9位问题0,确定读写操作在NAND Flash中的哪个页进行。
当我们得到一个Nand Flash地址addr时,我们可以这样分解出Column Address和Page Address。
Columnaddr = addr % 512 // column address
Pageaddr = addr>>9 // page address
也就是一个Nand Flash地址的A0-A7是它的column address ,A9—A25是它的Page Address,地址A8被忽略。
现在假设我要从Nand Flash中的第5000字节处开始读取1024个字节到内存的0x30000000处,我们这样调用read 函数
NF_Read(5000, 0x30000000,1024);
我们来分析5000这个src_addr.
根据
column_addr=src_addr%512;
page_address=(src_addr>>9);
我们可得出column_addr=5000%512=392
page_address=(5000>>9)=9
于是我们可以知道5000这个地址是在第9页的第392个字节处,于是我们的NF_read函数将这样发送命令和参数column_addr=5000%512;
page_address=(5000>>9);
NF_CMD=0x01; //要从2nd half开始读取所以要发送命令0x01
NF_ADDR= column_addr &0xff; //1st Cycle A[7:0]
NF_ADDR=page_address& 0xff NF_ADDR=(page_address>>8)&0xff; //3rd.Cycle A[24:17]
NF_ADDR=(page_address>>16)&0xff; //4th.Cycle A[25] 向NandFlash的命令寄存器和地址寄存器发送完以上命令和参数之后,我们就可以从rNFDATA寄存器(NandFlash数据寄存器)读取数据了.
我用下面的代码进行数据的读取.
for(i=column_addr;i<512;i++)
{
*buf++=NF_RDDATA();
}
每当读取完一个Page之后,数据指针会落在下一个Page的0号Column(0号Byte).
1、关于NAND FLASH 的坏块管理
就现在的NAND FLASH ,有串口和并口两种,
并口就是48PIN的数据的输入和输入公用IO的
串口的就是使用SPI接口的NAND FLASH
说下NAND FLASH 管理的核心思维:
1 识别坏块,标记坏块,
2 如何保存坏块信息
3 交换区概念
4 保留去概念
1,NANDFLASH 的工艺不能保证写进去的每个块里面的数据,和读出来的数据一致,所以是存在坏块的,
而NAND FLASH的数据组织结构是这样的,
由N个块组成,而块由M个PAGE组成+MXSPARE区
比如K9F1G08U0D 以这个片子为例子,
BLOCK 大小为128K 用户可以写的,
实际大小为128K + 64*4*16
至于后面64*4*16的意思是这样的,因为该片子每个PAGE的大小为2048 也就是2K,
而因为NAND 存在坏块,所以,在每写512个字节后,都会产生一个校验码,而这个校验码用来标记,该块是坏块还是可以正常使用的,
当然这个512是针对大页的NAND FLASH,小页的话是每256个字节产出一个校验码,当然现在这样的小页的NAND FLASH很少见了,
老了,即使有,也是拆机弄下来的,
坏块的识别的话,这几种,并口的:一个是可以根据ECC校验算法来,判断是否为坏块,具体算法可以去百度查查,
一个是可以判断在写玩数据之后,来判断SPARE数据区域来判断段坏块,而对于小页和大页的有区别
小页的NANDFLASH ,比如一个页的大小为1K,那么,在读取1K后面数据的16个数据,如果第一个数据不为FF,那么标记该块为坏块,在标记的时候,可以标记的数据为全部为00,也就是在标记16个数据的时候表示为0
而在标记的时候,一般标记的区域是这个块的第一个PAGE后的16个数据
串口:串口的,里面有自校验,硬件的,所以在识别的时候,可以直接装载要判断的PAGE 的地址,然后去读取状态寄存器即可,
同样的也可以去标记坏块,标记花开的话和并口的一样
2 如何保存坏块信息,
坏块信息一般包括:
1 ,保存坏块的信息的地址,也就是你要获得坏块信息的话,一般去哪个地址去读,可以是固定的,也可以不是固定的,不固定的花费的时间长点,管理起来复杂
2,坏块信息要保存交换区的信息:这个信息是,你所使用的交换区,某个块是坏块,还是好块后面会介绍
3,保留区的地址映射状态,后面会介绍
4 ,如何去标记该块是作为,保存坏块信息表格呢,,
至于如何保存坏块信息,很多种方法,核心就是,用特殊的数据去标记,
比如,我在用来保存坏块信息表的时候,一般这样标记
该块的第一个PAGE的前面八个数据是这样的,,0xff55aa00,0x0000000
该块的最后一个PAGE ,最后8个数据是0X33445588.0XFF00FF00
然后该块的倒数第二个PAGE,标记交换区的信息和保留去地址映射关系,+一个校验和
而对于保存坏块信息表格所用的块,一般是3个或者5个不等,,交替使用,或者也可以不交替使用,,
对于不交替使用的情况就是,一旦发生坏块标记的情况的话,那么3或者5个块都要被写数
据,而却写的数据是一样的,
有人会说,占用这么多块不是浪费,其实,对于整个芯片来说,占用这几个块,而有个坏块管理程序是值得的
3 交换区概念:
比如,块0 ,该块大小为128K,在0X000-0X18000里面有数据,0x18800 - 0x20000这个有数据是文件系统认为的,
而文件系统要在0x18000-0x18800这个512的范围内要写数据,那该怎么办呢,
肯定要这个块个擦除掉的,如果没有交换区的话,那么擦除的话,会导致在0X000-0X18000,,0x18800 - 0x20000之间的数据都会丢失的,那该怎么办呢,
那就要使用交换区,交换区的作用就是这样的,
如果要擦除某个块的话,那么首先要将这个块里面的数据拷贝到另外一个块里面,而这个另外一个块,称之为交换区,
首先将整个块的数据,全部放到一个可以用的交换区,然后再将0这个块擦除,擦除之后0这个块里面的数据就是FF了,那么在擦除之后,在将交换区里面的数据回写到0这个块里面
那么出现的情况就是0X000-0X18000,,0x18200 - 0x20000 这个两个区间内的数据还是原来的数据,在将原来的数据回写到0块之后,然后在将这次文件系统要写的数据写到
0x18000-0x18200这个区域里面,这样既保证数据的完整性,又能将新数据写进去
4保留区概念:
保留区的作用就是这个,当在写数据的时候发生坏块的话,那么得将这个块的数据要保存另外一个块里面,同时要对产生的那个坏块进行标记。
2、NANDFLASH坏块管理思维
K9F2808U0C是16 MB×8 bit的NAND Flash,共有1 024个Block,1 Block=16 KB,32 Page/Block,1 Page=528 B=(512 B+16 B),其中16 B为备用区,主要存放NAND Flash出厂坏块标记、ECC 校验码以及用户自定义区。
K9F2808U0C地址空间是24 bit,分三个周期依次送入NAND Flash 的地址锁存器。
本文使用的地址均为字节地址,数据类型为DWORD(4 B)。
将K9F2808U0C的存储空间划分为四个区:坏块映射表存放区、交换块区、坏块映射区和实际数据存放区。
文件系统管理的空间就是实际的数据存放空间,如图2所示。
2.2 各分区宏定义
#define FLASH_BLOCK_SIZE 0x40000 //16 KB/Block
#define FLASH_PAGE_SIZE 0x200 //512 B/Page
#define FLASH_SECTOR_SIZE 0x200
//1Page=1Sector(only K9F2808U0C)
#define FLASH_BLOCKS_TABLE 3//坏块映射表存放块数
#define FLASH_SWAP_BLOCKS 5 //交换区的块数
#define FLASH_BAD_BLOCKS_REMAP 50
//坏簇重映区的块数
#define FLASH_MAX_ADDR 0xFFFFFF
//Flash最大字节地址
各分区首地址计算公式:
FLASH_BLOCK_TABLE_ADDR=FLASH_MAX_ADDR+
1-3*FLASH_BLOCK_SIZE);
FLASH_SWAP_BLOCK_ADDR=(FLASH_BLOCK_
TABLE_ADDR-5*FLASH_BLOCK_SIZE);
FLASH_BAD_BLOCK_REMAP_ADDR=(FLASH_SWAP_
BLOCK_ADDR-50*FLASH_BLOCK_SIZE);
FLASH_MAX_SECTOR_ADDR=(FLASH_MAX_ADDR-
3*FLASH_BLOCK_TABLE_ADDR-5*FLASH_SWAP_
BLOCK_ADDR-50*FLASH_BAD_BLOCK_REMAP_ADDR);
文件系统管理的最大字节地址。
任意地址Addr:
所在块地址:Addr&(~(FLASH_BLOCK_SIZE-1));
块内偏移地址:Addr&(FLASH_BLOCK_SIZE-1);
块中的页:(Addr&(FLASH_BLOCK_SIZE-1))/FLASH_
PAGE_SIZE;
2.3 分区功能设计
坏块映射区存放复制3份的坏块信息BBI(Bad Block Information)表。
复制3份是预防系统突然断电,造成BBI表数据丢失。
选择最后3个块,主要是出于固件设计。
当Flash首次上电,固件程序通过读取Flash ID,获得设备的容量等信息,然后从Flash的最后一块中寻找BBI表,如果最后一块没有发现BBI表,则认为此块为坏块,继续前移寻找,依此类推,直到在预留的3个块中找到,并将其数据读入到在主控芯片为其开设的RAM中。
如果还找不到,则固件认为该片Flash没有BBI表。
交换块区是对NAND Flash进行擦除或写操作时用来临时存放数据,共分配5个块。
选取5块是出于可靠性设计。
用一个数组FlashSwapBlockStatus[FLASH_SWAP_BLOCKS]记录交换块状态:有效还是已经损坏。
初始化时,固件认为所有的交换块都是有效块,在随后对其进行擦除或写操作时,通过读Flash状态寄存器判断该交换块的真实状态,并记录在数组中。
交换块的管理围绕固件请求返回当前可用交换块地址或当前正在使用的交换块地址,并判断标记当前使用的交换块状态为坏。
坏块映射区是当主机向数据区写数据时,检测到当前块(数据区)为坏块时,将数据写到坏块映射区中的相应好块中,并且将这两个块的块地址记录到BBI表中,以后主机若要对当前块(数据区)访问时,只需读BBI表就可以找到相应映射块,从而代替坏块的访问。
这样就使文件系统所见逻辑块地址LBA(Logical Block Address)变成连续的,但实际上物理块地址
PBA(Physical Block Address)可能并不连续。
上述方法就是坏块管理的精髓。
出于保守设计本文共选50块作为重映块。
用数组FlashRemapBlockStatus[FLASH_BAD_BLOCKS_REMAP]标识坏块映射区的状态:未使用、已使用还是已经损坏。
初始化时认为坏块映射区中所有块都是好块。
3 NFTL坏块管理设计
3.1 构建BBI表
用一数组FlashBadBlockTable[2][FLASH_BAD_BLOCKS_REMAP]存放BBI表。
第一维为坏块(数据区)的块地址,第二维为映射块地址(重映块)。
BBI表的构建可在Flash首次上电时,通过读取厂商设置在Flash备用区中的坏块标记识别坏块,建立坏块映射表,此法初始化时间与Flash的容量成正比;或通过读NAND Flash所有块内容并和0xFF作比较[3],如果不相同,则表示坏块。
这种方法只针对新闪存,并且构建BBI表的时间长,主控芯片的占用率高;另一种方法是在Flash初次上电时先不建立坏块表,认为当前所有的块都是好块,在随后操作中发现坏块,并更新BBI表。
本文选择后一种方法。
3.2 坏块映射表区的设计
将BBI表通过特殊的方式保存,以后通过此种方式的逆向来识别BBI表。
坏块映射表区的3个块的设计方法如下:
(1)在每块最后一页的首字节处做特殊标记,用于标识该块有没有准备被擦除:0xFF表示没有准备被擦除;0x00表示该块已经准备被擦除[4]。
(2)在每块的倒数第二页的首字节处写0x00,表示该块存放BBI表数据;并在第2、3、4、5个字节处以大端模式存放校验和,用于校验该块中存放的所有数据的正确性。
该页的剩余字节写0xFF。
(3)在第一页起依次写:表头特殊标记(0x0055AAFF)、坏块总数(FlashBadBlockCount)、BBI表(FlashBadBlockTable)、重映块状态(FlashRemapBlockStatus),其结构如图3所示。
3.3 逻辑地址映射
在与主机批量数据传输时,首先对主机发送的CBW命令解析,从而固件获得主机请求数
据传输的LBA,再对LBA地址映射以此获得相应的PBA,然后再执行主机命令。
具体方法:首先读取当前总的坏块数FlashBadBlocksCount,如果为0,表示无坏块,LBA就不用映射,直接返回;如果最后一次访问地址与LBA同属于一个块,那么也不用地址映射;如果当前坏块的数量为1,判断LBA块地址与BBI表中坏块地址是否相同,从而决定是否采取地址映射;如果当前坏块的数量不为1,就需要查找BBI表判断是不是含有LBA的块地址,如果存在,则要地址重新映射,如果不存在,就不用重新映射。
可以采用二分法查表实现两地址快速比较[4]。
3.4 坏块处理
对映射后的地址进行写或擦除过程中发现当前块变坏的处理方法:首先查找映射区中的块状态FlashRemapBlockStatus[FLASH_BAD_BLOCKS_REMAP],寻找可用的映射块。
如果50个重映块都被标记为坏或已使用,则程序进入死循环;如果找到可用的映射块,则固件对可能出现坏块的三种情况进行散转:(1)擦除当前块时出错:将当前块地址与可用的重映块地址写入BBI表中,按地址大小排列有利于二分法查表,并返回重映块地址。
(2)复制某页从交换区到操作地址时失败:首先获得当前使用的交换块的块地址,然后判断操作地址是在数据存储区还是在重映区。
如果在重映区,说明当前操作地址所对应的在数据存储区中的块已经是坏块,并且在这次操作中重映块也变坏,此时就应该标记当前映射块状态为坏,并在重映区中寻找下一个可用重映块,将原来在数据存储区中的块地址与更新后的映射块地址写入BBI表中,并返回新的映射后的地址;如果在数据存储区,就进行第一次重映射,更新坏块表,并且将映射地址返回。
然后再完成复制工作。
(3)向操作地址写一页数据时出错:将当前地址所在块中的前面页从交换块中相应地址复制到重映块,然后将操作地址所在当前页中的没有写到的数据从交换块中复制,并将缓冲区中的数据重新写到重映射的地址中,并返回重映射地址。
函数实现如下:
DWORD FlashDealBadBlock(DWORD Addr,DWORD Type)
{
DWORD i;
DWORD RemapBlockAddr;
DWORD SwapBlockAddr;
while(1)
{
RemapBlockAddr=FlashGetNewRemapBlock();
if(RemapBlockAddr==-1)
return Addr;
switch(Type)
{ case 1:
goto Exit;
break;
case 2:
SwapBlockAddr=FlashGetCurrentSwapBlock();
for(i=0;i<(Addr&(FLASH_BLOCK_SIZE-1))/FLASH_PAGE_SIZE+1;i++)
{
if(0x00==(FlashCopyPage(SwapBlockAddr+i*FLASH_PAGE_SIZE,RemapBlockAddr+i*FLASH_ PAGE_SIZE)))
goto BadRemapBlock;
}
goto Exit;
break;
case 3:
SwapBlockAddr=FlashGetCurrentSwapBlock();
for(i=0;i<(Addr&(FLASH_BLOCK_SIZE-1))/FLASH_PAGE_SIZE;i++)
{
if(0x00==(FlashCopyPage(SwapBlockAddr+i*FLASH_PAGE_SIZE,RemapBlockAddr+
i*FLASH_PAGE_SIZE)))
goto BadRemapBlock;
}
if(0x00==(FlashCopyPage(Addr,RemapBlockAddr+i*FLASH_PAGE_SIZE)))
goto BadRemapBlock;
goto Exit;
break;
default:
break;
}
BadRemapBlock: FlashMarkRemapBlockBad(RemapBlockAddr);
}
Exit:FlashUpdateBadBlockTable(Addr,RemapBlockAddr);
return RemapBlockAddr+(Addr&(FLASH_BLOCK_SIZE-1));
}
3.5 连续读写操作
当主机与设备建立批量传输数据连接时,固件通过解析CBW封包获得起始LBA。
对该地址进行映射和坏块管理从而获得PBA。
设置一个变量,当此变量有效时,表示主机对Flash 仍需要读或写数据,直到此变量失效为止。
同时将主机上一次读写扇区的地址存入另一个变量,此变量在连续读中作用不大,但在连续写时,通过与上一次写操作地址所在块做比较,判断是否同属一个块,以此决定是不是需要进行地址跨块处理。
本文设计了一种针对NAND型的闪存转译层,使NFTL完成地址映射和坏块管理以及连续读写数据的操作。
对NAND Flash的分区设计,使块管理结构清晰,有利于固件的开发。
本文没有对Flash的ECC校验进行过多的设计,这是因为在实际应用中NAND Flash主要用于存储多媒体数据(图片、语音文件)等,并不会对它进行频繁的写入或擦除操作,而且多媒体文件数据对数据的完整性不敏感[5],所以不需要对存储在其中的每一位数据进行严格的ECC校验,可以通过另外设计简单的校验方法来代替ECC校验。
3、参考ST官方资料计算公式,计算FSMC各个时序参数
HCLK 内部AHB时钟频率 72 MHz
tsu(Data_NE) + tv(A_NE) 数据至FSMC_NEx高的建立时间+ FSMC_NEx低至FSMC_A有效 36 ns
读/写访问时间= ((SET + 1) + (WAIT + 1) + (HOLD + 1)) × HCLK
读/写使能信号低至高时间= (WAIT + 1) × HCLK
片选建立时间–数据建立时间= HiZ × HCLK
片选低至写使能高时间= ((SET + 1) + (WAIT + 1)) × HCLK
为了保证正确地配置FSMC的时序,下述因素应加以考虑:
●最大读/写访问时间
● FSMC内部各部分的延迟
●存储器内部各部分的延迟
因此,我们得到下述公式:
(WAIT + 1) × HCLK = max (tWP, tRP)
((SET + 1) + (WAIT + 1)) × HCLK = max (tCS, tALS, tCLS)
HOLD = max (tCH, tALH, tCLH)/HCLK
还需要满足下述公式的验证:
((SET + 1) + (WAIT + 1) + (HOLD + 1)) × HCLK = max (tRC, tWC) HiZ = (max (tCS, tALS, tCLS) – tDS)/HCLK) – 1
考虑FSMC和存储器内部各部分的延迟,这些公式变为如下形式:
● WAIT需要满足:
(WAIT+1+ SET + 1) = ((tCEA + tsu(Data_NE) + tv(A_NE))/HCLK)
WAIT = ((tCEA + tsu(Data_NE) + tv(A_NE))/HCLK) – SET – 2
● SET需要满足
(SET + 1) = max ((tCS, tALS, tCLS) – max (tWP, tRP))/HCLK – 1
SET = (max (tCS, tALS, tCLS) – max (tWP, tRP))/HCLK – 1
下表列出了NAND存储器各项参数的意义和时序
NAND闪存存储器时序
符号参数数值单位
tCEA 片选低至输出有效 35 ns
tWP 写使能低至写使能高 15 ns
tRP 写使能低至写使能高 15 ns
tCS 片选低至写使能高 20 ns
tALS AL建立时间 15 ns
tCLS CL建立时间 15 ns
tCH E保持时间 5 ns
tALH AL保持时间 5 ns
tCLH CL保持时间 5 ns
使用上述公式、存储器时序(表6)和STM32F10xxx参数(表2),我们得到:●地址建立时间:0x1
●地址保持时间:0x3
●数据建立时间:0x2
●数据总线高阻时间:0x2。