??NAND flash 坏块管理?? 下面也有内容,有一个新的标题
nand flash坏块管理OOB,BBT,ECC0.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.为什么会出现坏块 2.坏块的分类 3.坏块管理 ????? 可以用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的第六个字节。 ???? ECC: NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。 ???? (3)为什么好块用0xff来标记?因为Nand Flash的擦除即是将相应块的位全部变为1,写操作时只能把芯片每一位(bit)只能从1变为0,而不能从0变为1。0XFF这个值就是标识擦除成功,是好块。 ?
bbt坏块管理 日月 发表于 - 2010-3-2 9:59:00 2 推荐 前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义: 2415 if (!this->scan_bbt) 2416 this->scan_bbt = nand_default_bbt; nand_default_bbt()位于Nand_bbt.c文件中。 1047 /** * nand_default_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure * * This selects the default bad block table * support for the device and calls the nand_scan_bbt **/ int nand_default_bbt (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; 这个函数的作用是建立默认的坏块表。 1059 /* Default for AG-AND. We must use a flash based * bad block table as the devices have factory marked * _good_ blocks. Erasing those blocks leads to loss * of the good / bad information,so we _must_ store * this information in a good / bad table during * startup */ if (this->options & NAND_IS_AND) { /* Use the default pattern deors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } this->options |= NAND_USE_FLASH_BBT; return nand_scan_bbt (mtd,&agand_flashbased); } 如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。 1078 /* Is a flash based bad block table requested ? */ if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern deors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } if (!this->badblock_pattern) { this->badblock_pattern = (mtd->oobblock > 512) ? &largepage_flashbased : &smallpage_flashbased; } } else { this->bbt_td = NULL; this->bbt_md = NULL; if (!this->badblock_pattern) { this->badblock_pattern = (mtd->oobblock > 512) ? &largepage_memorybased : &smallpage_memorybased; } } return nand_scan_bbt (mtd,this->badblock_pattern); 如果Flash芯片需要使用坏块表,对于1208芯片来说是使用smallpage_memorybased。 985 static struct nand_bbt_descr smallpage_memorybased = { .options = NAND_BBT_SCAN2NDPAGE,.offs = 5,.len = 1,.pattern = scan_ff_pattern }; 暂时没看到如何使用这些赋值,先放着。后面检测坏块时用得着。 1099 return nand_scan_bbt (mtd,this->badblock_pattern); 最后将badblock_pattern作为参数,调用nand_can_bbt函数。 844 /** * nand_scan_bbt - [NAND Interface] scan,find,read and maybe create bad block table(s) * @mtd: MTD device structure * @bd: deor for the good/bad block search pattern * * The checks,if a bad block table(s) is/are already * available. If not it scans the device for manufacturer * marked good / bad blocks and writes the bad block table(s) to * the selected place. * * The bad block table memory is allocated here. It must be freed * by calling the nand_free_bbt . * */ int nand_scan_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) { 检测、寻找、读取甚至建立坏块表。函数检测是否已经存在一张坏块表,否则建立一张。坏块表的内存分配也在这个函数中。 860 struct nand_chip *this = mtd->priv; int len,res = 0; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; len = mtd->size >> (this->bbt_erase_shift + 2); /* Allocate memory (2bit per block) */ this->bbt = kmalloc (len,GFP_KERNEL); if (!this->bbt) { printk (KERN_ERR "nand_scan_bbt: Out of memory/n"); return -ENOMEM; } /* Clear the memory bad block table */ memset (this->bbt,0x00,len); 一些赋值、变量声明、内存分配,每个block分配2bit的空间。1208有4096个block,应该分配4096*2bit的空间。 877 /* If no primary table decriptor is given,scan the device * to build a memory based bad block table */ if (!td) { if ((res = nand_memory_bbt(mtd,bd))) { printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT/n"); kfree (this->bbt); this->bbt = NULL; } return res; } 如果没有提供ptd,就扫描设备并建立一张。这里调用了nand_memory_bbt()这个内联函数。 653 /** * nand_memory_bbt - [GENERIC] create a memory based bad block table * @mtd: MTD device structure * @bd: deor for the good/bad block search pattern * * The creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ static inline int nand_memory_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt (mtd,this->data_buf,bd,-1); } 函数的作用是建立一张基于memory的坏块表。 将操作符的NAND_BBT_SCANEMPTY清除,并继续调用creat_bbt()函数。 271 /** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure * @buf: temporary buffer * @bd: deor for the good/bad block search pattern * @chip: create the table for a specific chip,-1 read all chips. * Applies only if NAND_BBT_PERCHIP option is set * * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ static int create_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *bd,int chip) { 真正的建立坏块表函数。chip参数是-1表示读取所有的芯片。 284 struct nand_chip *this = mtd->priv; int i,j,numblocks,len,scanlen; int startblock; loff_t from; size_t readlen,ooblen; printk (KERN_INFO "Scanning device for bad blocks/n"); 一些变量声明,开机时那句话就是在这儿打印出来的。 292 if (bd->options & NAND_BBT_SCANALLPAGES) len = 1 << (this->bbt_erase_shift - this->page_shift); else { if (bd->options & NAND_BBT_SCAN2NDPAGE) len = 2; else len = 1; } 在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2。 304 if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ scanlen = ooblen = 0; readlen = bd->len; } else { /* Full page content should be read */ scanlen = mtd->oobblock + mtd->oobsize; readlen = len * mtd->oobblock; ooblen = len * mtd->oobsize; } 前面已经将NAND_BBT_SCANEMPTY清除了,这里肯定执行else的内容。需要将一页内容都读取出来。 316 if (chip == -1) { /* Note that numblocks is 2 * (real numblocks) here,see i+=2 below as it * makes shifting and masking less painful */ numblocks = mtd->size >> (this->bbt_erase_shift - 1); startblock = 0; from = 0; } else { if (chip >= this->numchips) { printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)/n",chip + 1,this->numchips); return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; from = startblock << (this->bbt_erase_shift - 1); } 前面提到chip为-1,实际上我们只有一颗芯片,numblocks这儿是4096*2。 335 for (i = startblock; i < numblocks;) { int ret; if (bd->options & NAND_BBT_SCANEMPTY) if ((ret = nand_read_raw (mtd,buf,from,readlen,ooblen))) return ret; for (j = 0; j < len; j++) { if (!(bd->options & NAND_BBT_SCANEMPTY)) { size_t retlen; /* Read the full oob until read_oob is fixed to * handle single byte reads for 16 bit buswidth */ ret = mtd->read_oob(mtd,from + j * mtd->oobblock,mtd->oobsize,&retlen,buf); if (ret) return ret; if (check_short_pattern (buf,bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",i >> 1,(unsigned int) from); break; } } else { if (check_pattern (&buf[j * scanlen],scanlen,mtd->oobblock,bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",(unsigned int) from); break; } } } i += 2; from += (1 << this->bbt_erase_shift); } return 0; 检测这4096个block,刚开始的nand_read_raw肯定不会执行。len是2,在j循环要循环2次。 每次循环真正要做的事情是下面的内容: ret = mtd->read_oob(mtd,buf); read_oob()函数在nand_scan()里被指向nand_read_oob(),这个函数在Nand_base.c文件中,看来得回Nand_base.c看看了。 1397 /** * nand_read_oob - [MTD Interface] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * * NAND read out-of-band data from the spare area */ static int nand_read_oob (struct mtd_info *mtd,loff_t from,size_t len,size_t * retlen,u_char * buf) { 才发现oob全称是out-of-band,from是偏移量,len是读取的长度,retlen是存储指针。 1409 int i,col,page,chipnr; struct nand_chip *this = mtd->priv; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; DEBUG (MTD_DEBUG_LEVEL3,"nand_read_oob: from = 0x%08x,len = %i/n",(unsigned int) from,(int) len); /* Shift to get page */ page = (int)(from >> this->page_shift); chipnr = (int)(from >> this->chip_shift); /* Mask to get column */ col = from & (mtd->oobsize - 1); /* Initialize return length value */ *retlen = 0; 一些初始化,blockcheck对于1208应该是(1<<(0xe-0x9)-1)=31。然后通过偏移量计算出要读取oob区的page,chipnr和col。 1425 /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0,"nand_read_oob: Attempt read beyond end of device/n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_device (this,mtd,FL_READING); /* Select the NAND device */ this->select_chip(mtd,chipnr); /* Send the read command */ this->cmdfunc (mtd,NAND_CMD_READOOB,page & this->pagemask); 不允许非法的读取,获取芯片控制权,发送读取OOB命令,这儿会调用具体硬件驱动中相关的Nand控制函数。 1442 /* * Read the data,if we read more than one page * oob data,let the device transfer the data ! */ i = 0; while (i < len) { int thislen = mtd->oobsize - col; thislen = min_t(int,thislen,len); this->read_buf(mtd,&buf[i],thislen); i += thislen; /* Read more ? */ if (i < len) { page++; col = 0; /* Check,if we cross a chip boundary */ if (!(page & this->pagemask)) { chipnr++; this->select_chip(mtd,-1); this->select_chip(mtd,chipnr); } /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check,so no problems * arise if a chip which does auto increment * is marked as NOAUTOINCR by the board driver. */ if (!this->dev_ready) udelay (this->chip_delay); else nand_wait_ready(mtd); /* Check,if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { /* For subsequent page reads set offset to 0 */ this->cmdfunc (mtd,0x0,page & this->pagemask); } } } /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); /* Return happy */ *retlen = len; return 0; 开始读取数据,while循环只要获取到oob区大小的数据即可。注意,read_buf才是最底层的读写Nand的函数,在我们的驱动中根据参数可以实现读取528byte全部内容,或者16byte的oob区。 如果一次没读完,就要继续再读,根据我们实际使用经验好像没出现过这种问题。 最后Return Happy~回到Nand_bbt.c的creat_bbt()函数,348行,好像都快忘记我们还没出creat_bbt()函数呢,我再把他贴一遍吧: 346 /* Read the full oob until read_oob is fixed to * handle single byte reads for 16 bit buswidth */ ret = mtd->read_oob(mtd,(unsigned int) from); break; } } } i += 2; from += (1 << this->bbt_erase_shift); } return 0; } 刚刚如果不是Ruturn Happy,下面的352行就会返回错误了。接着会调用check_short_pattern()这个函数。 113 /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search * @td: search pattern deor * * Check for a pattern at the given place. Used to search bad block * tables and good / bad block identifiers. Same as check_pattern,but * no optional empty check * */ static int check_short_pattern (uint8_t *buf,struct nand_bbt_descr *td) { int i; uint8_t *p = buf; /* Compare the pattern */ for (i = 0; i < td->len; i++) { if (p[td->offs + i] != td->pattern[i]) return -1; } return 0; } 检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct nand_bbt_descr smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。 this->bbt[i >> 3] |= 0x03 << (i & 0x6); 为啥要右移3bit呢?首先i要右移1bit,因为前面乘以了2。由于没个block占用2bit的空间,一个char变量8bit,所以还再要右移2bit吧。 下面的check_pattern()函数调用不到的。 依次检测完所有block,creat_bbt()函数也顺利返回。 这样nand_memory_bbt()函数也正确返回。 接着是nand_scan_bbt()同样顺利结束。 最后nand_default_bbt()完成。 整个nand_scan()的工作终于完成咯,好长。 MTD的坏块管理(一)-快速了解MTD的坏块管理? 由 于NAND Flash的现有工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此在NAND芯片出厂的时候,厂家只能保证block 0不是坏块,对于其它block,则均有可能存在坏块,而且NAND芯片在使用的过程中也很容易产生坏块。因此,我们在读写NAND FLASH 的时候,需要检测坏块,同时还需在NAND驱动中加入坏块管理的功能。? 在struct nand_chip中与bbt相关的结构体如下: struct nand_chip { }; bbt指向 一块在nand_default_bbt函数中分配的内存,若options中没有定义NAND_USE_FLASH_BBT,MTD就直接在bbt指向 的内存中建立bbt,否则就会先从NAND芯片中查找bbt是否存在,若存在,就把bbt的内容读出来并保存到bbt指向的内存中,若不存在,则在bbt 指向的内存中建立bbt,最后把它写入到NAND芯片中去。? bbt_td、bbt_md和badblock_pattern的结构体类型定义如下: struct nand_bbt_descr { }; options:bad block table或者bad block的选项,可用的选择以及各选项具体表示什么含义,可以参考<linux/mtd/nand.h>。? 其二是调用那个search_read_bbts函数试着在NAND芯片的maxblocks(请见上文关于struct nand_bbt_descr中maxblocks的说明)个block中查找bbt是否存在,若找到,就可以读取bbt了。? ? MTD坏块管理(二)-内核获取Nandflash的参数过程? MTD坏块管理机制中,起着核心作用的数据结构是nand_chip,在此以TCC8900-Linux中MTD的坏块管理为例作一次介绍。 MTD在Linux内核中同样以模块的形式被启用,TCC_MTD_IO_Init()函数完成了nand_chip初始化、mtd_info初始注册, 坏块表的管理机制建立等工作。 nand_chip在TCC_MTD_IO_Init函数中的实例名称是this,mtd_info 的实例名称为TCC_mtd,这里有一个比较巧妙的处理方法: TCC_mtd=kmalloc(sizeof(struct mtd_info)+sizeof(struct nand_chip),GFP_KERNEL); this=(struct nand_chip*)(&TCC_mtd[1]); 在以后的操作中,只需得知TCC_mtd即可找到对应的nan_chip实例。 获得必要的信息后(包括nand_chip方法的绑定),流程进入nand_scan(TCC_mtd,1). nand_scan(struct mdt_info *mtd,int maxchips); 调用nand_scan_ident(mtd,maxchips)和nand_scan_tail(mtd); nand_scan_ident(...)调用了一个很重要的函数:nand_get_flash_type(...) *从nand_get_flash_type(...)函数中可以看出每个nandflash前几个字节所代表的意思都是约定好了的: 第一个字节:制造商ID 第二个字节:设备ID 第三个字节:MLC 数据 第四个字节:extid (比较总要) 其中设备ID是访问nand_flash_ids表的参照,该表在drivers/mtd/nand/nand_ids.c中定义 Linux内核在nand_flash_ids参照表中,通过匹配上述设备ID来查找nandflash的详细信息, nand_flash_ids中的举例如下: struct nand_flash_dev nand_flash_ids[]={ ...... {"NAND 16MiB 1,8V 8-bit",?? 0x33,512,16,0x4000,0}, {"NAND 16MiB 3,3V 8-bit",?? 0x73, {"NAND 16MiB 1,8V 16-bit",? 0x43,NAND_BUSWIDTH_16},3V 16-bit",? 0x53, ...... } 466 struct nand_flash_dev { 467???? char *name; 468???? int id; 469???? unsigned long pagesize; 470???? unsigned long chipsize; 471???? unsigned long erasesize;??? 472???? unsigned long options;????? 473 }; 值得一提的是,MTD子系统会把从nand_flash_ids表中找到的chipsize复制给mtd->size,这在有些应用中显得不合适, 在有些方案中,并不是把nandflash的所有存储空间都划分为MTD分区,Telechips的TCC89XX方案就是这样,4G的nandflash 上,可以划分任意大小的MTD分区,错误的mtd->size的后果非常严重,造成系统启动慢,整个MTD的坏块管理机制瘫痪等等。 随后,nand_get_flash_type通过extid计算出了以下信息: mtd可写区大小:mtd->writesize=1024<<(extid&0x03); 这里可以看成1024*(1*2的(extid&0x03)次方), mtdoob区大小:extid>>=2;mtd->oobsize = (8<<(extid&0x1))*(mtd->writesize>>9); 每512字节对应(8*2的(extid&0x1)次方)字节oob数据 mtd擦写块大小:extid>>=2;mtd->erasesize=(64*1024)<<(extid&0x03); nand数据宽度 :extid>>=2;busw=(extid&0x01)?NAND_BUSEWIDTH_16:0; 现在大多为8位数据宽度 可以看出第四个字节extid的意义: 高|0??? |? 0??????? |?? 00??????? | 0?? | 0???????? |? 00?????????? |低 ?? |无用|数据宽度|擦写块算阶|无用|oob算阶|? 可写区算阶| nand_get_flash_type(...)还确立了nandflash中的坏块标记在oob信息中的位置: if(mtd->writesize>512||(busw&NAND_BUSWIDTH_16)) ??? chip->badblockpos = NAND_LARGE_BADBLOCKS_POS;//大页面flash的坏块信息存储地址为oob信息中的第1个字节开始处 else ??? chip->badblockpos = NAND_SMALL_BADBLOCKS_POS;//大页面flash的坏块信息存储地址为oob信息中的第6个字节开始处 对于Samsun和Hynix的MLC型nandflash,坏块标记所在的页是每块的最后一个页,而Samsung,Hynix,和AMD的SLC型nandflash 中,坏块标记分别保存在每块开始的第1,2个页中,其他型号的nandflash大多都保存在第一个也中,为此需要作下标记: 坏块标记保存在块的最后一页中:chip->options |= NAND_BBT_SCANLASTPAGE; 坏块标记保存在块的第1,2页中 :chip->options |= NAND_BBT_SCAN2NDPAGE; nand_scan之后调用nand_scan_tail(mtd)函数, nand_scan_tail(...)函数主要完成MTD实例中各种方法的绑定,例如: 3338???? mtd->read = nand_read; 3339???? mtd->write = nand_write; 3340???? mtd->panic_write = panic_nand_write; 3341???? mtd->read_oob = nand_read_oob; 3342???? mtd->write_oob = nand_write_oob; 3343???? mtd->sync = nand_sync; nand_scan_tail(...)调用chip->scan_bbt()完成坏块表的有关操作。 chip->scan_bbt的绑定过程是在nand_scan_ident()->nand_set_defaults():chip->scan_bbt = nand_default_bbt. 即真正用于坏块操作的是nand_default_bbt函数,该函数在nand_bbt.c中被定义。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- jQuery ajax调用webservice注意事项
- ruby-on-rails – On Migration:未定义的方法`to_sym’为n
- c# – 将DateTimePicker值传递给存储过程
- DWR于spring结合问题——在dwr的service中调用spring生命的
- 开源flashplayer汇总.
- flutter是否像React Native一样使用Javascript Engine之类的
- ajax接收服务端数据中文显示为?的解决办法
- ruby – 将字符串对象转换为活动记录类
- ruby-on-rails – 在RSpec中有一个等同于Cucumber的“场景”
- c# – 如何从头开始实现异步I / O绑定操作?