加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Nand flash driver

发布时间:2020-12-15 18:10:41 所属栏目:百科 来源:网络整理
导读:http://blog.chinaunix.net/uid-14163325-id-3175398.html http://wenku.baidu.com/view/6a91fd116edb6f1aff001fa3.html S3C2410 nandflash 读写过程: http://wenku.baidu.com/view/ae4dfbc00c22590102029d55.html MTD NAND driver programming interface:

http://blog.chinaunix.net/uid-14163325-id-3175398.html

http://wenku.baidu.com/view/6a91fd116edb6f1aff001fa3.html


S3C2410 nandflash 读写过程:

http://wenku.baidu.com/view/ae4dfbc00c22590102029d55.html

MTD NAND driver programming interface:

http://www.linux-mtd.infradead.org/tech/mtdnand/book1.html

转载:

http://wenku.baidu.com/view/b36cfcc30c22590102029d69.html


今天学习了NandFlash的驱动,硬件操作非常简单,就是这个linux下的驱动比较复杂,主要还是MTD层的问题,用了一下午时间整理出来一份详细的分析,只是分析函数结构和调用关系,具体代码实现就不看了,里面有N个结构体,搞得我头大。

?

我用linux2.6.25内核,2440板子,先从启动信息入手。

内核启动信息,NAND部分:

S3C24XXNAND Driver,(c) 2004 Simtec Electronics

s3c2440-nands3c2440-nand: Tacls=2,20ns Twrph0=3 30ns,Twrph1=2 20ns

NANDdevice: Manufacturer ID: 0xec,Chip ID: 0x76 (Samsung NAND 64MiB 3,3V8-bit)

Scanningdevice for bad blocks

Creating3 MTD partitions on "NAND 64MiB 3,3V 8-bit":

0x00000000-0x00040000: "boot"

0x0004c000-0x0024c000: "kernel"

0x0024c000-0x03ffc000: "yaffs2"

?

第一行,在driver/mtd/nand/s3c2410.c中第910行,s3c2410_nand_init函数:

printk("S3C24XXNAND Driver,(c) 2004 Simtec Electronicsn");

?

行二行,同一文件,第212行,s3c2410_nand_inithw函数:

dev_info(info->device,"Tacls=%d,%dns Twrph0=%d %dns,Twrph1=%d %dnsn",tacls,to_ns(tacls,clkrate),twrph0,to_ns(twrph0,twrph1,to_ns(twrph1,clkrate));

?

第三行,在driver/mtd/nand/nand_base.c中第2346行,

printk(KERN_INFO"NAND device: Manufacturer ID:" " 0x%02x,Chip ID:0x%02x (%s %s)n",*maf_id,dev_id,nand_manuf_ids[maf_idx].name,type->name);

?

第四行,在driver/mtd/nand/nand_bbt.c中第380行,creat_bbt函数:

Printk(KERNINFO " Scanning device for bad blocks n");

?

第五行,在driver/mtd/mtdpart.c中第340行,add_mtd_partitions函数:

printk(KERN_NOTICE "Creating %d MTD partitions on "%s":n",nbparts,master->name);

?

下面三行,是flash分区表,也在mtdpart.c同一函数中,第430行:

printk(KERN_NOTICE "0x%08x-0x%08x : "%s"n",slave->offset,slave->offset + slave->mtd.size,slave->mtd.name);

?

MTD体系结构:

linux中提供了MTDMemoryTechnology Device,内存技术设备)系统来建立Flash针对linux的统一、抽象的接口

引入MTD后,linux系统中的Flash设备驱动及接口可分为4:

设备节点

MTD设备层

MTD原始设备层

硬件驱动层

?

硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读、写、擦除,Linux MTD设备的NANDFlash驱动位于driver/mtd/nand子目录下

s3c2410对应的nandFlash驱动为s3c2410.c

MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码,另一部分是各个特定Flash的数据,比如分区

主要构成的文件有:

drivers/mtd/mtdcore.c支持mtd字符设备

driver/mtd/mtdpart.c支持mtd块设备

MTD设备层:基于MTD原始设备,Linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90),构成MTD设备层

?

简单的说就是:使用一个mtd层来作为具体的硬件设备驱动和上层文件系统的桥梁。mtd给出了系统中所有mtd设备(nandnordiskonchip)的统一组织方式。

?

mtd层用一个数组struct mtd_info *mtd_table[MAX_MTD_DEVICES]保存系统中所有的设备,mtd设备利用structmtd_info这个结构来描述,该结构中描述了存储设备的基本信息和具体操作所需要的内核函数,mtd系统的那个机制主要就是围绕这个结构来实现的。结构体在include/linux/mtd/mtd.h中定义:

structmtd_info {

u_char type; //MTD设备类型

u_int32_t flags; //MTD设备属性标志

u_int32_t size; //标示了这个mtd设备的大小

u_int32_t erasesize; //MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小

u_int32_t oobblock; //oob区在页内的位置,对于512字节一页的nand来说是512

u_int32_t oobsize; //oob区的大小,对于16

u_int32_t ecctype; //ecc校验类型

u_int32_t eccsize; //ecc的大小

?

char* name; //设备的名字

int index; //设备在MTD列表中的位置

?

struct nand_oobinfo oobinfo;//oob区的信息,包括是否使用eccecc的大小

?

//以下是关于mtd的一些读写函数,将在nand_base中的nand_scan中重载

int (*erase)

int (*read)

int (*write)

int (*read_ecc)

int (*write_ecc)

int (*read_oob)

int(*read_oob)

?

void*priv;//设备私有数据指针,对于NandFlash来说指nand芯片的结构

?

下面看nand_chip结构,在include/linux/mtd/nand.h中定义:

structnand_chip {

void __iomem *IO_ADDR_R; //这是nandflash的读写寄存器

void __iomem *IO_ADDR_W;

//以下都是nandflash的操作函数,这些函数将根据相应的配置进行重载

u_char (*read_byte)(struct mtd_info *mtd);

void (*write_byte)(struct mtd_info *mtd,u_char byte);

u16 (*read_word)(struct mtd_info *mtd);

void (*write_word)(struct mtd_info *mtd,u16 word);

void (*write_buf)(struct mtd_info *mtd,const u_char *buf,int len);

void (*read_buf)(struct mtd_info *mtd,u_char *buf,SimSun">int (*verify_buf)(struct mtd_info *mtd,SimSun">void (*select_chip)(struct mtd_info *mtd,int chip);

int (*block_bad)(struct mtd_info *mtd,loff_t ofs,int getchip);

int (*block_markbad)(struct mtd_info *mtd,loff_t ofs);

void (*hwcontrol)(struct mtd_info *mtd,int cmd);

int (*dev_ready)(struct mtd_info *mtd);

void (*cmdfunc)(struct mtd_info *mtd,unsigned command,int column,int page_addr);

int (*waitfunc)(struct mtd_info *mtd,struct nand_chip *this,intstate);

int (*calculate_ecc)(struct mtd_info *mtd,const u_char *dat,u_char*ecc_code);

int (*correct_data)(struct mtd_info *mtd,u_char *dat,u_char*read_ecc,u_char *calc_ecc);

void (*enable_hwecc)(struct mtd_info *mtd,int mode);

void (*erase_cmd)(struct mtd_info *mtd,int page);

int (*scan_bbt)(struct mtd_info *mtd);

int eccmode; //ecc的校验模式(软件,硬件)

int chip_delay; //芯片时序延迟参数

int page_shift; //页偏移,对于512B/页的,一般是9

u_char *data_buf; //数据缓存区

?

NAND操作相关的函数:

1nand_base.c

定义了NAND驱动中对NAND芯片最基本的操作函数和操作流程,如擦除、读写page、读写oob等。当然这些函数都只是进行一些常规的操作,若你的系统在对NAND操作时有一些特殊的动作,则需要在你自己的驱动代码中进行定义。

2nand_bbt.c

定义了NAND驱动中与坏块管理有关的函数和结构体。

3nand_ids.c

定义了两个全局类型的结构体:structnand_flash_dev nand_flash_ids[ ]structnand_manufacturers nand_manuf_ids[]。其中前者定义了一些NAND芯片的类型,后者定义了NAND芯片的几个厂商。NAND芯片的ID至少包含两项内容:厂商ID和厂商为自己的NAND芯片定义的芯片ID。当NAND加载时会找这两个结构体,读出ID,如果找不到,就会加载失败。

4nand_ecc.c

定义了NAND驱动中与softewareECC有关的函数和结构体,若你的系统支持hardwareECC,且不需要softwareECC,则该文件也不需理会。

?

我们需要关心的是/nand/s3c2410,这个文件实现的是s3c2410/2440nandflash控制器最基本的硬件操作,读写擦除操作由上层函数完成。

s3c2410.c分析:

首先看一下要用到的结构体的注册:

structs3c2410_nand_mtd {

structmtd_info mtd; //mtd_info的结构体

structnand_chip chip; //nand_chip的结构体

structs3c2410_nand_set *set;

structs3c2410_nand_info *info;

int scan_res;

};

?

enums3c_cpu_type { //用来枚举CPU类型

TYPE_S3C2410,

TYPE_S3C2412,SimSun">TYPE_S3C2440,SimSun">};

structs3c2410_nand_info {

/*mtd info */

structnand_hw_control controller;

structs3c2410_nand_mtd *mtds;

structs3c2410_platform_nand *platform;

?

/*device info */

structdevice *device;

structresource *area;

structclk *clk;

void__iomem *regs;

void__iomem *sel_reg;

int sel_bit;

int mtd_count;

unsignedlong save_nfconf;

enums3c_cpu_type cpu_type;

};

?

设备的注册:

staticint __init s3c2410_nand_init(void)

{

?

platform_driver_register(&s3c2412_nand_driver);

platform_driver_register(&s3c2440_nand_driver);

returnplatform_driver_register(&s3c2410_nand_driver);

}

platform_driver_register向内核注册设备,同时支持这三种CPU

&s3c2440_nand_driver是一个platform_driver类型的结构体:

?

staticstruct platform_driver s3c2440_nand_driver = {

.probe =s3c2440_nand_probe,SimSun">.remove = s3c2410_nand_remove,

.suspend =s3c24xx_nand_suspend,

.resume =s3c24xx_nand_resume,SimSun">.driver ={

.name ="s3c2440-nand",SimSun">.owner =THIS_MODULE,SimSun">},SimSun">};

?

最主要的函数就是s3c2440_nand_probe,(调用s3c24XX_nand_probe,完成对nand设备的探测,

staticint s3c24xx_nand_probe(struct platform_device *pdev,SimSun">enum s3c_cpu_type cpu_type)

/*主要完成一些硬件的初始化,其中调用函数:*/

s3c2410_nand_init_chip(info,nmtd,sets);

/*init_chip结束后,调用nand_scan完成对flash的探测及mtd_info读写函数的赋值*/

nmtd->scan_res=nand_scan(&nmtd->mtd,(sets) ? sets->nr_chips : 1);

if(nmtd->scan_res == 0) {

s3c2410_nand_add_partitionNand_scan是在初始化nand的时候对nand进行的一步非常好重要的操作,在nand_scan中会对我们所写的关于特定芯片的读写函数重载到nand_chip结构中去,并会将mtd_info结构体中的函数用nand的函数来重载,实现了mtd到底层驱动的联系。

并且在nand_scan函数中会通过读取nand芯片的设备号和厂家号自动在芯片列表中寻找相应的型号和参数,并将其注册进去。

?

?

static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,SimSun">struct s3c2410_nand_mtd *nmtd,SimSun">struct s3c2410_nand_set *set)

structnand_chip *chip = &nmtd->chip;

void__iomem *regs = info->regs;

/*以下都是对chip赋值,对应nand_chip中的函数chip->write_buf = s3c2410_nand_write_buf; //buf

chip->read_buf = s3c2410_nand_read_buf; //buf

chip->select_chip = s3c2410_nand_select_chip;//片选

chip->chip_delay = 50;

chip->priv = nmtd;

chip->options = 0;

chip->controller = &info->controller; //??

?

switch(info->cpu_type) {

caseTYPE_S3C2440:

chip->IO_ADDR_W= regs + S3C2440_NFDATA; //数据寄存器

info->sel_reg = regs + S3C2440_NFCONT; //控制寄存器

info->sel_bit =S3C2440_NFCONT_nFCE;

chip->cmd_ctrl = s3c2440_nand_hwcontrol; //硬件控制

chip->dev_ready= s3c2440_nand_devready; //设备就绪

chip->read_buf = s3c2440_nand_read_buf; //chip->write_buf =s3c2440_nand_write_buf;//break;

chip->IO_ADDR_R= chip->IO_ADDR_W; //读写寄存器都是同一个

nmtd->info = info;

nmtd->mtd.priv = chip; //私有数据指针指向chip

nmtd->mtd.owner = THIS_MODULE;

nmtd->set = set;

/*后面是和ECC校验有关的,省略*/

}

?

初始化后,实现对nand的基本硬件操作就可以了,包括以下函数:

s3c2410_nand_inithw //初始化硬件,在probe中调用

s3c2410_nand_select_chip //片选

s3c2440_nand_hwcontrol //硬件控制,其实就是片选

s3c2440_nand_devready //设备就绪

s3c2440_nand_enable_hwecc //使能硬件ECC校验

s3c2440_nand_calculate_ecc //计算ECC

s3c2440_nand_read_buf s3c2440_nand_write_buf

?

注册nand设备到MTD原始设备层:(这个函数由probe调用)

#ifdefCONFIG_MTD_PARTITIONS //如果定义了MTD分区

staticint s3c2410_nand_add_partition(struct s3c2410_nand_info *info,SimSun">struct s3c2410_nand_mtd *mtd,SimSun">if(set == NULL)

return add_mtd_device(&mtd->mtd);

if(set->nr_partitions > 0 && set->partitions != NULL){

return add_mtd_partitions(&mtd->mtd,set->partitions,set->nr_partitions);

#else

注册设备用这两个函数:

add_mtd_device //如果nand整体不分区,用这个,

//该函数在mtdcore.c中实现

add_mtd_partitions //如果nand是分区结构,用这个,

mtdpart.c中实现

同样,注销设备也有两个函数:

del_mtd_device

del_mtd_partitions

?

NandFlash还有一个分区表结构体,mtd_partition,这个是在arch/arm/plat-s3c24XX/common-smdk.c中定义的。

?

staticstruct mtd_partition smdk_default_nand_part[] = {

[0]= {

.name ="boot",SimSun">.size =0x00040000,SimSun">.offset =0,SimSun">[1]= {

.name ="kernel",SimSun">.offset= 0x0004C000,SimSun">.size =0x00200000,SimSun">[2]= {

.name ="yaffs2",SimSun">.offset= 0x0024C000,SimSun">.size =0x03DB0000,SimSun">};

记录了当前的nandflash有几个分区,每个分区的名字,大小,偏移量是多少

系统就是依靠这些分区表找到各个文件系统的

这些分区表nand中的文件系统没有必然关系,分区表只是把flash分成不同的部分

?

如果自己编写一个nandflash驱动,只需要填充这三个结构体:

Mtd_info nand_chip mtd_partition

并实现对物理设备的控制,上层的驱动控制已由mtd做好了,不需要关心

?

?

2410NandFlash控制器

管脚配置

D[7:0]: DATA0-7数据/命令/地址/的输入/输出口(与数据总线共享)

CLE : GPA17 命令锁存使能(输出)

ALE : GPA18 地址锁存使能(输出)

nFCE : GPA22 NAND Flash片选使能(输出)

nFRE : GPA20 NAND Flash读使能nFWE : GPA19 NAND Flash写使能R/nB : GPA21 NAND Flash准备好/繁忙(输入)

?

相关寄存器:

NFCONF NandFlash控制寄存器

15NandFlash控制器使能/禁止0 = 禁止 1 =使能

1413]保留

12]初始化ECC解码器/编码器0 = 不初始化 1 =初始化

11]芯片使能nFCE控制0 = 使能 1 =禁止

108TACLS持续时间=HCLK*TACLS+1

64TWRPH0

20TWRPH1

?

NFCMD命令设置寄存器

70]命令值

NFADDR地址设置寄存器

0]存储器地址

NFDATA数据寄存器

0]存放数据

NFSTAT状态寄存器

0 = 存储器忙 1 =存储器准备好

NFECC ECC寄存器

2316ECC校验码2

151

80

?

写操作:

写入操作以页为单位。写入必须在擦除之后,否则写入将出错。

页写入周期中包括以下步骤:

写入串行数据输入指令(80h)。然后写入4个字节的地址,最后串行写入数据(528Byte)。串行写入的数据最多为528byte

串行数据写入完成后,需要写入“页写入确认”指令10h,这条指令将初始化器件内部写入操作。

10h写入之后,nandflash的内部写控制器将自动执行内部写入和校验中必要的算法和时序,

系统可以通过检测R/B的输出,或读状态寄存器的状态位(I/O6)来判断内部写入是否结束

?

擦除操作:

擦除操作时以块(16K Byte)为单位进行的

擦除的启动指令为60h,随后的3个时钟周期是块地址。其中只有A14A25是有效的,而A9A13是可以忽略的。

块地址之后是擦除确认指令D0h,用来开始内部的擦除操作。

器件检测到擦除确认命令后,在/WE的上升沿启动内部写控制器,开始执行擦除和擦除校验。内部擦除操作完成后,应该检测写状态位(I/O0),从而了解擦除操作是否成功完成。

?

读操作有两种读模式:

读方式1用于读正常数据;

读方式2用于读附加数据

在初始上电时,器件进入缺省的“读方式1模式”。在这一模式下,页读取操作通过将00h指令写入指令寄存器,接着写入3个地址(一个列地址和2个行地址)来启动。一旦页读指令被器件锁存,下面的页操作就不需要再重复写入指令了。

写入指令和地址后,处理器可以通过对信号线R//B的分析来判断该才作是否完成。

外部控制器可以再以50ns为周期的连续/RE脉冲信号的控制下,从I/O口依次读出数据

备用区域的从512527地址的数据,可以通过读方式2指令进行指令进行读取(命令为50h)。地址A0A3设置了备用区域的起始地址,A4A7被忽略掉

?

时序要求:

写地址、数据、命令时,nCEnWE信号必须为低电平,它们在nWE信号的上升沿被锁存。命令锁存使能信号CLE和地址锁存信号ALE用来区分I/O引脚上传输的是命令还是地址。

?

寻址方式:

NANDFlash的寻址方式和NANDFlashmemory组织方式紧密相关。NANDFlash的数据以bit的方式保存在memorycell,一个cell中只能存储一个bit。这些cell8个或者16个为单位,连成bitline,形成byte(x8)/word(x16),这就是NAND的数据宽度。

这些Line会再组成Page典型情况下:通常是528Byte/page或者264Word/page。然后,每32page形成一个BlockSizeof(block)=16.5kByte。其中528Byte=512Byte+16Byte,前512Byte为数据区,后16Byte存放数据校验码等,因此习惯上人们称1page512个字节,每个Block16Kbytes

现在在一些大容量的FLASH存贮设备中也采用以下配置:2112Byte /page 1056Word/page64page/BlockSizeof(block)= 132kByte;同上:2112= 2048 +64,人们习惯称一页含2k个字节,一个Block含有64个页,容量为128KB

?

BlockNANDFlash中最大的操作单元,擦除可以按照blockpage为单位完成,而编程/读取是按照page为单位完成的

所以,按照这样的组织方式可以形成所谓的三类地址:

-Block?Address块地址

-Page??Address页地址

-Column Address列地址

???首先,必须清楚一点,对于NANDFlash来讲,地址和命令只能在I/O[7:0]上传递,数据宽度可以是8位或者16位,但是,对于x16NAND DeviceI/O[15:8]只用于传递数据。

???清楚了这一点,我们就可以开始分析NANDFlash的寻址方式了。

528Byte/page总容量64MByte+512kbyteNAND器件为例:

因为

1page=528byte=512byte(MainArea)+16byte(Spare Area)

1block=32page= 16kbyte

64Mbyte= 4096 Block

?

用户数据保存在mainarea中。

512byte需要9bit来表示,对于528byte系列的NAND,这512byte被分成1sthalf2ndhalf,各自的访问由所谓的pointeroperation命令来选择,也就是选择了bit8的高低。因此A8就是halfpagepointerA[7:0]就是所谓的columnaddress

?

32page需要5bit来表示,占用A[13:9]即该page在块内的相对地址。

Block的地址是由A14以上的bit来表示,例如64MBNAND,共4096block,因此,需要12bit来表示,即A[25:14],如果是1Gbit528byte/pageNANDFlash,共8192block,则blockaddressA[30:14]表示。

?

NANDFlash的地址表示为:

BlockAddress|PageAddress in block |halfpage pointer|Column Address

地址传送顺序是ColumnAddress,Page Address,Block Address

?

例如一个地址:0x00aa55aa

000000001010 1010 0101 01011010 1010

?

由于地址只能在I/O[7:0]上传递,因此,必须采用移位的方式进行。

例如,对于64MBx8NANDflash,地址范围是0~0x3FF_FFFF,只要是这个范围内的数值表示的地址都是有效的。

NAND_ADDR为例:

1步是传递columnaddress,就是NAND_ADDR[7:0],不需移位即可传递到I/O[7:0]上,而halfpagepointerbit8是由操作指令决定的,即指令决定在哪个halfpage上进行读写,而真正的bit8的值是don'tcare的。

2步就是将NAND_ADDR右移9位,将NAND_ADDR[16:9]传到I/O[7:0];

3步将NAND_ADDR[24:17]放到I/O4步需要将NAND_ADDR[25]放到因此,整个地址传递过程需要4步才能完成,即4-stepaddressing

如果NANDFlash的容量是32MB以下,那么,blockadress最高位只到bit24,因此寻址只需要3步。



What are the differences between flash devices and block drives?

The following table describes the differences between block devices andraw flashes. Note,SSD,MMC,eMMC,RS-MMC,SD,mini-SD,micro-SD,USB flashdrive,CompactFlash,MemoryStick,MemoryStick Micro,and other FTL devicesareblock devices,not raw flash devices. Of course,hard drives arealso block devices.

Block device MTD device
Consists of sectors Consists of eraseblocks
Sectors are small (512,1024 bytes) Eraseblocks are larger (typically 128KiB)
Maintains 2 main operations: read sector andwrite sector Maintains 3 main operations: read from eraseblock,write to eraseblock,anderase eraseblock
Bad sectors are re-mapped and hidden by hardware (atleast in modern LBA hard drives); in case of FTLdevices it is the responsibility of FTL to provide this Bad eraseblocks are not hidden and should be dealt within software
Sectors are devoid of the wear-out property (in FTL devicesit is the responsibility of FTL to provide this) Eraseblocks wear-out and become bad and unusable afterabout 103 (for MLC NAND) - 105(NOR,SLC NAND) erase cycles


mtd->size : mtd 设备的大小

mtd->erasesize : mtd 设备擦除的单元,也就是 block size

mtd->writesize: mtd 设备flush 写的单元,也就是 nand page


Nand flash OOB 以及 坏块分析的好文章:

转载:

http://czhjchina.blog.163.com/blog/static/20027904720126203271790/

0.NAND的操作管理方式

???? NAND FLASH的管理方式:以三星FLASH为例,一片Nand flash为一个设备(device),1 (Device) = xxxx (Blocks),1 (Block) = xxxx (Pages),1(Page)?=528 (Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes,除OOB第六字节外,通常至少把OOB的前3个字节存放Nand Flash硬件ECC码)。

????? 关于OOB区,是每个Page都有的。Page大小是512字节的NAND每页分配16字节的OOB;如果NAND物理上是2K的Page,则每个Page分配64字节的OOB。如下图:

????????????

以HYNIX为例,图中黑体的是实际探测到的NAND,是个2G bit(256M)的NAND。PgSize是2K字节,PgsPBlk表示每个BLOCK包含64页,那么每个BLOCK占用的字节数是 64X2K=128K字节;该NAND包好2048个BLOCK,那么可以算出NAND占用的字节数是2048X128K=256M,与实际相符。需要注 意的是SprSize就是OOB大小,也恰好是2K页所用的64字节。

?1.为什么会出现坏块
 ?? 由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。坏块的特性是:当编程/擦除这个块时,会造成Page Program和Block Erase操作时的错误,相应地反映到Status Register的相应位。

2.坏块的分类
 ? 总体上,坏块可以分为两大类:(1)固有坏块:这是生产过程中产生的坏块,一般芯片原厂都会在出厂时都会将每个坏块第一个page的spare area的第6个byte标记为不等于0xff的值。(2)使用坏块:这是在NAND Flash使用过程中,如果Block Erase或者Page Program错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和固有坏块信息保持一致,将新发现的坏块的第一个page的 spare area的第6个Byte标记为非0xff的值。

3.坏块管理:(指的是整个块都是坏的)

 ?? 根据上面的这些叙述,可以了解NAND Flash出厂时在spare area中已经反映出了坏块信息,因此,如果在擦除一个块之前,一定要先check一下第一页的spare area的第6个byte是否是0xff,如果是就证明这是一个好块,可以擦除;如果是非0xff,那么就不能擦除,以免将坏块标记擦掉。当然,这样处理可能会犯一个错误―――“错杀伪坏块”,因为在芯片操作过程中可能由于电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可靠性及软件设计的简单化,还是需要遵照这个标准。

????? 可以用BBT:bad block table,即坏块表来进行管理。各家对nand的坏块管理方法都有差异。比如专门用nand做存储的,会把bbt放到block0,因为第0块一定是好 的块。但是如果nand本身被用来boot,那么第0块就要存放程序,不能放bbt了。有的把bbt放到最后一块,当然,这一块坚决不能为坏块。bbt的大小跟nand大小有关,nand越大,需要的bbt也就越大。

????? 需要注意的是:OOB是每个页都有的数据,里面存的有ECC(当然不仅仅);而BBT是一个FLASH才有一个;针对每个BLOCK的坏块识别则是该块第一页spare area的第六个字节。
4.坏块纠正

????? ECC:NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出 错。一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的 错误不保证能检测。
????? ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1。(512生成两组ECC,共6字节)?
????? 当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的OOB(out- of-band)数据区中。其位置就是eccpos[]。校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验 和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠 正;若3个字节异或结果中只存在1个比特位为1,表示OOB区出错;其他情况均表示出现了无法纠正的错误。
5.补充
  (1)需要对前面由于Page Program错误发现的坏块进行一下特别说明。如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,首先就要把块里其他好的 面的内容备份到另外一个空的好块里面,然后,把这个块标记为坏块。当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完块备份之后,再将这个坏块 擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!
  (2)可能有人会问,为什么要使用每个块第一页的spare area的第六个byte作为坏块标记。这是NAND Flash生产商的默认约定,你可以看到Samsung,Toshiba,STMicroelectronics都是使用这个Byte作为坏块标记的。

???? (3)为什么好块用0xff来标记?因为Nand Flash的擦除即是将相应块的位全部变为1,写操作时只能把芯片每一位(bit)只能从1变为0,而不能从0变为1。0XFF这个值就是标识擦除成功,是好块。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读