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这个值就是标识擦除成功,是好块。
- 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",
- i >> 1, (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, from + j * mtd->oobblock, mtd->oobsize, &retlen, 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, col, 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, NAND_CMD_READOOB, 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, 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",
- i >> 1, (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()的工作终于完成咯,好长。
阅读(1553) | 评论(0) | 转发(2) |