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

nand flash 的oob 及坏块管理

发布时间:2020-12-15 19:52:22 所属栏目:百科 来源:网络整理
导读:转载出处:http://blog.csdn.net/liukun321/article/details/6598921#comments 0.NAND的操作管理方式 ????? NAND FLASH的管理方式:以三星FLASH为例,一片Nand flash为一个设备(device),1 (Device) = xxxx (Blocks),1 (Block) = xxxx (Pages),1(Page)?=52

转载出处:http://blog.csdn.net/liukun321/article/details/6598921#comments

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操作的错误。但是,为了数据的可靠性及软件设计的简单化,还是需要遵照这个标准。

??????需要注意的是: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作为坏块标记的。

?

view plain copy to clipboard
  1. bbt坏块管理??
  2. 日月?发表于?-?2010-3-2?9:59:00??
  3. 2??
  4. 推荐??
  5. 前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义:??
  6. 2415?if?(!this->scan_bbt)??
  7. 2416?this->scan_bbt?=?nand_default_bbt;??
  8. nand_default_bbt()位于Nand_bbt.c文件中。??
  9. 1047?/**?
  10. ????*?nand_default_bbt?-?[NAND?Interface]?Select?a?default?bad?block?table?for?the?device?
  11. ????*?@mtd:?MTD?device?structure?
  12. ????*?
  13. ????*?This?selects?the?default?bad?block?table?
  14. ????*?support?for?the?device?and?calls?the?nand_scan_bbt?
  15.   **/??
  16.   int?nand_default_bbt?(struct?mtd_info?*mtd)??
  17.   {??
  18.   ?struct?nand_chip?*this?=?mtd->priv;??
  19. 这个函数的作用是建立默认的坏块表。??
  20. 1059?/*?Default?for?AG-AND.?We?must?use?a?flash?based?
  21. ???*?bad?block?table?as?the?devices?have?factory?marked?
  22. ???*?_good_?blocks.?Erasing?those?blocks?leads?to?loss?
  23. ???*?of?the?good?/?bad?information,?so?we?_must_?store?
  24. *?this?information?in?a?good?/?bad?table?during?
  25. *?startup?
  26. ???*/??
  27. ???if?(this->options?&?NAND_IS_AND)?{??
  28. ???/*?Use?the?default?pattern?deors?*/??
  29. this->bbt_td)?{??
  30. ????this->bbt_td?=?&bbt_main_descr;??
  31. ????this->bbt_md?=?&bbt_mirror_descr;??
  32. ???}??
  33. this->options?|=?NAND_USE_FLASH_BBT;??
  34. return?nand_scan_bbt?(mtd,?&agand_flashbased);??
  35. ???}??
  36. 如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。??
  37. 1078?/*?Is?a?flash?based?bad?block?table?requested???*/??
  38. ???this->options?&?NAND_USE_FLASH_BBT)?{??
  39. ???/*?Use?the?default?pattern?deors?*/??
  40. this->bbt_td)?{??
  41. this->bbt_td?=?&bbt_main_descr;??
  42. this->bbt_md?=?&bbt_mirror_descr;??
  43. this->badblock_pattern)?{??
  44. this->badblock_pattern?=?(mtd->oobblock?>?512)????
  45. ?????&largepage_flashbased?:?&smallpage_flashbased;??
  46. ???}?else?{??
  47. this->bbt_td?=?NULL;??
  48. this->bbt_md?=?NULL;??
  49. this->badblock_pattern)?{??
  50. this->badblock_pattern?=?(mtd->oobblock?>?512)????
  51. ?????&largepage_memorybased?:?&smallpage_memorybased;??
  52. ????
  53. this->badblock_pattern);??
  54. 如果Flash芯片需要使用坏块表,对于1208芯片来说是使用smallpage_memorybased。??
  55. 985???static?struct?nand_bbt_descr?smallpage_memorybased?=?{??
  56. ???.options?=?NAND_BBT_SCAN2NDPAGE,??
  57. ???.offs?=?5,??
  58. ???.len?=?1,108); list-style-type:decimal-leading-zero; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> ???.pattern?=?scan_ff_pattern??
  59.   };??
  60. 暂时没看到如何使用这些赋值,先放着。后面检测坏块时用得着。??
  61. 1099?this->badblock_pattern);??
  62. 最后将badblock_pattern作为参数,调用nand_can_bbt函数。??
  63. 844???/**?
  64.   *?nand_scan_bbt?-?[NAND?Interface]?scan,?find,?read?and?maybe?create?bad?block?table(s)?
  65. ???*?@mtd:?MTD?device?structure?
  66. ???*?@bd:???deor?for?the?good/bad?block?search?pattern?
  67. ???*?
  68. ???*?The?checks,?if?a?bad?block?table(s)?is/are?already?
  69. ???*?available.?If?not?it?scans?the?device?for?manufacturer?
  70. ???*?marked?good?/?bad?blocks?and?writes?the?bad?block?table(s)?to?
  71. ???*?the?selected?place.?
  72. ???*?
  73. ???*?The?bad?block?table?memory?is?allocated?here.?It?must?be?freed?
  74. ???*?by?calling?the?nand_free_bbt?.?
  75.   */??
  76. int?nand_scan_bbt?(struct?mtd_info?*mtd,153); background-color:inherit; font-weight:bold">struct?nand_bbt_descr?*bd)??
  77. 检测、寻找、读取甚至建立坏块表。函数检测是否已经存在一张坏块表,否则建立一张。坏块表的内存分配也在这个函数中。??
  78. 860?this?=?mtd->priv;??
  79. int?len,?res?=?0;??
  80. uint8_t?*buf;??
  81. struct?nand_bbt_descr?*td?=?this->bbt_td;??
  82. struct?nand_bbt_descr?*md?=?this->bbt_md;??
  83. len?=?mtd->size?>>?(this->bbt_erase_shift?+?2);??
  84. /*?Allocate?memory?(2bit?per?block)?*/??
  85. this->bbt?=?kmalloc?(len,?GFP_KERNEL);??
  86. this->bbt)?{??
  87. ???printk?(KERN_ERR?"nand_scan_bbt:?Out?of?memory/n");??
  88. return?-ENOMEM;??
  89. }??
  90. /*?Clear?the?memory?bad?block?table?*/??
  91. memset?(this->bbt,?0x00,?len);??
  92. 一些赋值、变量声明、内存分配,每个block分配2bit的空间。1208有4096个block,应该分配4096*2bit的空间。??
  93. 877?/*?If?no?primary?table?decriptor?is?given,?scan?the?device?
  94. *?to?build?a?memory?based?bad?block?table?
  95. */??
  96. if?(!td)?{??
  97. if?((res?=?nand_memory_bbt(mtd,?bd)))?{??
  98.     printk?(KERN_ERR?"nand_bbt:?Can't?scan?flash?and?build?the?RAM-based?BBT/n");??
  99. ????kfree?(this->bbt);??
  100. this->bbt?=?NULL;??
  101. return?res;??
  102. 如果没有提供ptd,就扫描设备并建立一张。这里调用了nand_memory_bbt()这个内联函数。??
  103. 653????*?nand_memory_bbt?-?[GENERIC]?create?a?memory?based?bad?block?table?
  104. ???*?The?creates?a?memory?based?bbt?by?scanning?the?device?
  105. ???*?for?manufacturer?/?software?marked?good?/?bad?blocks?
  106. static?inline?int?nand_memory_bbt?( ???bd->options?&=?~NAND_BBT_SCANEMPTY;??
  107. return?create_bbt?(mtd,153); background-color:inherit; font-weight:bold">this->data_buf,?bd,?-1);??
  108.   }??
  109. 函数的作用是建立一张基于memory的坏块表。??
  110. 将操作符的NAND_BBT_SCANEMPTY清除,并继续调用creat_bbt()函数。??
  111. 271?  *?create_bbt?-?[GENERIC]?Create?a?bad?block?table?by?scanning?the?device?
  112. ???*?@buf:?temporary?buffer?
  113. ???*?@bd:???deor?for?the?good/bad?block?search?pattern?
  114. ???*?@chip:?create?the?table?for?a?specific?chip,?-1?read?all?chips.?
  115. ???*???Applies?only?if?NAND_BBT_PERCHIP?option?is?set?
  116. ???*?Create?a?bad?block?table?by?scanning?the?device?
  117. ???*?for?the?given?good/bad?block?identify?pattern?
  118.   int?create_bbt?(struct?nand_bbt_descr?*bd,153); background-color:inherit; font-weight:bold">int?chip)??
  119.   {??
  120. 真正的建立坏块表函数。chip参数是-1表示读取所有的芯片。??
  121. 284?int?i,?j,?numblocks,?len,?scanlen;??
  122. int?startblock;??
  123. loff_t?from;??
  124. size_t?readlen,?ooblen;??
  125. printk?(KERN_INFO?"Scanning?device?for?bad?blocks/n");??
  126. 一些变量声明,开机时那句话就是在这儿打印出来的。??
  127. 292?if?(bd->options?&?NAND_BBT_SCANALLPAGES)??
  128. len?=?1?<<?(this->bbt_erase_shift?-?this->page_shift);??
  129. else?{??
  130. if?(bd->options?&?NAND_BBT_SCAN2NDPAGE)??
  131. ????len?=?2;??
  132. else??
  133. ????len?=?1;??
  134. 在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2。??
  135. 304?if?(!(bd->options?&?NAND_BBT_SCANEMPTY))?{??
  136. /*?We?need?only?read?few?bytes?from?the?OOB?area?*/??
  137. ???scanlen?=?ooblen?=?0;??
  138. ???readlen?=?bd->len;??
  139. }?/*?Full?page?content?should?be?read?*/??
  140. ???scanlen?=?mtd->oobblock?+?mtd->oobsize;??
  141. ???readlen?=?len?*?mtd->oobblock;??
  142. ???ooblen?=?len?*?mtd->oobsize;??
  143. }??
  144. 前面已经将NAND_BBT_SCANEMPTY清除了,这里肯定执行else的内容。需要将一页内容都读取出来。??
  145. 316?if?(chip?==?-1)?{??
  146. /*?Note?that?numblocks?is?2?*?(real?numblocks)?here,?see?i+=2?below?as?it?
  147. ???*?makes?shifting?and?masking?less?painful?*/??
  148. ???numblocks?=?mtd->size?>>?(this->bbt_erase_shift?-?1);??
  149. ???startblock?=?0;??
  150. ???from?=?0;??
  151. }?if?(chip?>=?this->numchips)?{??
  152. ????printk?(KERN_WARNING?"create_bbt():?chipnr?(%d)?>?available?chips?(%d)/n",248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> ?????chip?+?1,153); background-color:inherit; font-weight:bold">this->numchips);??
  153. return?-EINVAL;??
  154. ???numblocks?=?this->chipsize?>>?(this->bbt_erase_shift?-?1);??
  155. ???startblock?=?chip?*?numblocks;??
  156. ???numblocks?+=?startblock;??
  157. ???from?=?startblock?<<?( 前面提到chip为-1,实际上我们只有一颗芯片,numblocks这儿是4096*2。??
  158. 335?for?(i?=?startblock;?i?<?numblocks;)?{??
  159. int?ret;??
  160. if?(bd->options?&?NAND_BBT_SCANEMPTY)??
  161. if?((ret?=?nand_read_raw?(mtd,?buf,?from,?readlen,?ooblen)))??
  162. ?????return?ret;??
  163. for?(j?=?0;?j?<?len;?j++)?{??
  164. if?(!(bd->options?&?NAND_BBT_SCANEMPTY))?{??
  165. ?????size_t?retlen;??
  166. ?????/*?Read?the?full?oob?until?read_oob?is?fixed?to?
  167. ?????*?handle?single?byte?reads?for?16?bit?buswidth?*/??
  168. ?????ret?=?mtd->read_oob(mtd,?from?+?j?*?mtd->oobblock,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> ????????mtd->oobsize,?&retlen,?buf);??
  169. if?(ret)??
  170. ??????return?ret;??
  171. if?(check_short_pattern?(buf,?bd))?{??
  172. ???????this->bbt[i?>>?3]?|=?0x03?<<?(i?&?0x6);??
  173. ??????printk?(KERN_WARNING?"Bad?eraseblock?%d?at?0x%08x/n",248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> ???????i?>>?1,?(unsigned?int)?from);??
  174. ????????break;??
  175. ?????}??
  176. ????}? ?????if?(check_pattern?(&buf[j?*?scanlen],?scanlen,?mtd->oobblock,?bd))?{??
  177. ?????????this->bbt[i?>>?3]?|=?0x03?<<?(i?&?0x6);??
  178. ??????printk?(KERN_WARNING?"Bad?eraseblock?%d?at?0x%08x/n",108); list-style-type:decimal-leading-zero; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> ???????i?>>?1,153); background-color:inherit; font-weight:bold">int)?from);??
  179. ?????????break;??
  180. ?????}??
  181. ????}??
  182. ???i?+=?2;??
  183. ???from?+=?(1?<<?this->bbt_erase_shift);??
  184. return?0;??
  185. 检测这4096个block,刚开始的nand_read_raw肯定不会执行。len是2,在j循环要循环2次。??
  186. 每次循环真正要做的事情是下面的内容:??
  187. ret?=?mtd->read_oob(mtd,?mtd->oobsize,108); list-style-type:decimal-leading-zero; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> read_oob()函数在nand_scan()里被指向nand_read_oob(),这个函数在Nand_base.c文件中,看来得回Nand_base.c看看了。??
  188. 1397????*?nand_read_oob?-?[MTD?Interface]?NAND?read?out-of-band?
  189. ???*?@from:?offset?to?read?from?
  190. ???*?@len:?number?of?bytes?to?read?
  191. ???*?@retlen:?pointer?to?variable?to?store?the?number?of?read?bytes?
  192. ???*?@buf:?the?databuffer?to?put?data?
  193. ???*?NAND?read?out-of-band?data?from?the?spare?area?
  194. ???*/??
  195. int?nand_read_oob?( 才发现oob全称是out-of-band,?from是偏移量,len是读取的长度,retlen是存储指针。??
  196. 1409?int?blockcheck?=?(1?<<?(this->phys_erase_shift?-?this->page_shift))?-?1;??
  197. DEBUG?(MTD_DEBUG_LEVEL3,?"nand_read_oob:?from?=?0x%08x,?len?=?%i/n",153); background-color:inherit; font-weight:bold">int)?from,?(int)?len);??
  198. /*?Shift?to?get?page?*/??
  199. page?=?(int)(from?>>? chipnr?=?(this->chip_shift);??
  200. /*?Mask?to?get?column?*/??
  201. col?=?from?&?(mtd->oobsize?-?1);??
  202. /*?Initialize?return?length?value?*/??
  203. *retlen?=?0;??
  204. 一些初始化,blockcheck对于1208应该是(1<<(0xe-0x9)-1)=31。然后通过偏移量计算出要读取oob区的page,chipnr和col。??
  205. 1425?/*?Do?not?allow?reads?past?end?of?device?*/??
  206. if?((from?+?len)?>?mtd->size)?{??
  207. ???DEBUG?(MTD_DEBUG_LEVEL0,?"nand_read_oob:?Attempt?read?beyond?end?of?device/n");??
  208. ???*retlen?=?0;??
  209. /*?Grab?the?lock?and?see?if?the?device?is?available?*/??
  210. nand_get_device?(this,?mtd?,?FL_READING);??
  211. /*?Select?the?NAND?device?*/??
  212. this->select_chip(mtd,?chipnr);??
  213. /*?Send?the?read?command?*/??
  214. this->cmdfunc?(mtd,?NAND_CMD_READOOB,?page?&?this->pagemask);??
  215. 不允许非法的读取,获取芯片控制权,发送读取OOB命令,这儿会调用具体硬件驱动中相关的Nand控制函数。??
  216. 1442?/*?
  217. *?Read?the?data,?if?we?read?more?than?one?page?
  218. *?oob?data,?let?the?device?transfer?the?data?!?
  219. */??
  220. i?=?0;??
  221. while?(i?<?len)?{??
  222. int?thislen?=?mtd->oobsize?-?col;??
  223. ???thislen?=?min_t(int,?thislen,?len);??
  224. this->read_buf(mtd,?&buf[i],?thislen);??
  225. ???i?+=?thislen;??
  226. /*?Read?more???*/??
  227. if?(i?<?len)?{??
  228. ????page++;??
  229. ????col?=?0;??
  230. ????/*?Check,?if?we?cross?a?chip?boundary?*/??
  231. if?(!(page?&?this->pagemask))?{??
  232. ?????chipnr++;??
  233. ????}??
  234. /*?Apply?delay?or?wait?for?ready/busy?pin?
  235. ????*?Do?this?before?the?AUTOINCR?check,?so?no?problems?
  236. ????*?arise?if?a?chip?which?does?auto?increment?
  237. ????*?is?marked?as?NOAUTOINCR?by?the?board?driver.?
  238. ????*/??
  239. this->dev_ready)??
  240. ?????udelay?(this->chip_delay);??
  241. else??
  242. ?????nand_wait_ready(mtd);??
  243. ????????*?or?if?we?have?hit?a?block?boundary.?
  244. ????*/??
  245. if?(!NAND_CANAUTOINCR(this)?||?!(page?&?blockcheck))?{??
  246. /*?For?subsequent?page?reads?set?offset?to?0?*/??
  247. ???????????/*?Deselect?and?wake?up?anyone?waiting?on?the?device?*/??
  248. nand_release_device(mtd);??
  249. /*?Return?happy?*/??
  250. *retlen?=?len;??
  251. return?0;??
  252. 开始读取数据,while循环只要获取到oob区大小的数据即可。注意,read_buf才是最底层的读写Nand的函数,在我们的驱动中根据参数可以实现读取528byte全部内容,或者16byte的oob区。??
  253. 如果一次没读完,就要继续再读,根据我们实际使用经验好像没出现过这种问题。??
  254. 最后Return?Happy~回到Nand_bbt.c的creat_bbt()函数,348行,好像都快忘记我们还没出creat_bbt()函数呢,我再把他贴一遍吧:??
  255. 346???/*?Read?the?full?oob?until?read_oob?is?fixed?to?
  256. ???*?handle?single?byte?reads?for?16?bit?buswidth?*/??
  257. ???ret?=?mtd->read_oob(mtd,108); list-style-type:decimal-leading-zero; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important; list-style-position:outside!important"> ????????mtd->oobsize,?buf);??
  258. if?(ret)??
  259. ?????? ??????? ???????? ????}? ???i?+=?2;??
  260. ???from?+=?(1?<<?this->bbt_erase_shift);??
  261. 刚刚如果不是Ruturn?Happy,下面的352行就会返回错误了。接着会调用check_short_pattern()这个函数。??
  262. 113????*?check_short_pattern?-?[GENERIC]?check?if?a?pattern?is?in?the?buffer?
  263. ???*?@buf:?the?buffer?to?search?
  264. ???*?@td:???search?pattern?deor?
  265. ???*?Check?for?a?pattern?at?the?given?place.?Used?to?search?bad?block?
  266. ???*?tables?and?good?/?bad?block?identifiers.?Same?as?check_pattern,?but?
  267. ???*?no?optional?empty?check?
  268.   */??
  269. int?check_short_pattern?(uint8_t?*buf,153); background-color:inherit; font-weight:bold">struct?nand_bbt_descr?*td)??
  270. {??
  271. int?i;??
  272. uint8_t?*p?=?buf;??
  273. /*?Compare?the?pattern?*/??
  274. for?(i?=?0;?i?<?td->len;?i++)?{??
  275. if?(p[td->offs?+?i]?!=?td->pattern[i])??
  276. return?-1;??
  277. 检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct?nand_bbt_descr?smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。??
  278. 为啥要右移3bit呢?首先i要右移1bit,因为前面乘以了2。由于没个block占用2bit的空间,一个char变量8bit,所以还再要右移2bit吧。??
  279.   下面的check_pattern()函数调用不到的。??
  280. 依次检测完所有block,creat_bbt()函数也顺利返回。??
  281. 这样nand_memory_bbt()函数也正确返回。??
  282. 接着是nand_scan_bbt()同样顺利结束。??
  283. 最后nand_default_bbt()完成。??
  284. 整个nand_scan()的工作终于完成咯,好长。??
);????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?nand_bbt_descr?*bd)  {???this?=?mtd->priv;???bd->options?&=?~NAND_BBT_SCANEMPTY;???return?create_bbt?(mtd,153); background-color:inherit; font-weight:bold">this->data_buf,?-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???*/  int?create_bbt?(struct?nand_bbt_descr?*bd,153); background-color:inherit; font-weight:bold">int?chip)  {真正的建立坏块表函数。chip参数是-1表示读取所有的芯片。284?int?i,?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);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)?{??????numblocks?=?mtd->size?>>?(this->bbt_erase_shift?-?1);???startblock?=?0;???from?=?0;}?if?(chip?>=?this->numchips)?{????printk?(KERN_WARNING?"create_bbt():?chipnr?(%d)?>?available?chips?(%d)/n",?????chip?+?1,153); background-color:inherit; font-weight:bold">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,?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,????????mtd->oobsize,?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,153); background-color:inherit; font-weight:bold">int)?from);????????break;?????}????}?else?{?????if?(check_pattern?(&buf[j?*?scanlen],?bd))?{?????????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???*/int?nand_read_oob?(out-of-band,?from是偏移量,len是读取的长度,retlen是存储指针。1409?int?blockcheck?=?(1?<<?(this->phys_erase_shift?-?this->page_shift))?-?1;DEBUG?(MTD_DEBUG_LEVEL3,?len?=?%i/n",153); background-color:inherit; font-weight:bold">int)?from,153); background-color:inherit; font-weight:bold">int)?len);/*?Shift?to?get?page?*/page?=?(int)(from?>>?this->page_shift);chipnr?=?(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,?FL_READING);/*?Select?the?NAND?device?*/this->select_chip(mtd,?chipnr);/*?Send?the?read?command?*/this->cmdfunc?(mtd,153); background-color:inherit; font-weight:bold">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,?len);???this->read_buf(mtd,?thislen);???i?+=?thislen;???/*?Read?more???*/???if?(i?<?len)?{????page++;????col?=?0;????????if?(!(page?&?this->pagemask))?{?????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.????*/????this->dev_ready)?????udelay?(this->chip_delay);????else?????nand_wait_ready(mtd);????????if?(!NAND_CANAUTOINCR(this)?||?!(page?&?blockcheck))?{?????/*?For?subsequent?page?reads?set?offset?to?0?*/???????????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,153); background-color:inherit; font-weight:bold">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???*  */  int?check_short_pattern?(uint8_t?*buf,153); background-color:inherit; font-weight:bold">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的坏块管理

1?人收藏此文章,?收藏此文章?发表于2个月前,已有 65?次阅读 共 0?个评论? 1?人收藏此文章

由于NAND Flash的现有工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此在NAND芯片出厂的时候,厂家只能保证block 0不是坏块,对于其它block,则均有可能存在坏块,而且NAND芯片在使用的过程中也很容易产生坏块。因此,我们在读写NAND FLASH 的时候,需要检测坏块,同时还需在NAND驱动中加入坏块管理的功能。??
???
NAND驱动在加载的时候,会调用nand_scan函数,对bad block table的搜寻,建立等操作就是在这个函数的第二部分,即nand_scan_tail函数中完成的。??

在 nand_scan_tail函数中,会首先检查struct nand_chip结构体中的options成员变量是否被赋上了NAND_SKIP_BBTSCAN,这个宏表示跳过扫描bbt。所以,只有当你的 driver中没有为options定义NAND_SKIP_BBTSCAN时,MTD才会继续与bbt相关工作,即调用struct nand_chip中的scan_bbt函数指针所指向的函数,在MTD中,这个函数指针指向nand_default_bbt函数。??

bbt有两种存储方式,一种是把bbt存储在NAND芯片中,另一种是把bbt存储在内存中。对于前者,好处是驱动加载更快,因为它只会在第一次加载NAND驱动时扫描整个NAND芯片,然后在NAND芯片的某个block中建立bbt,坏处是需要至少消耗NAND芯片一个block的存储容量;而对于后者,好处是不会耗用NAND芯片的容量,坏处是驱动加载稍慢,因为存储在内存中的bbt每次断电后都不会保存,所以在每次加载NAND驱动时,都会扫描整个NAND芯片,以便建立bbt。??

如果你系统中的NAND芯片容量不是太大的话,我建议还是把bbt存储在内存中比较好,因为根据本人的使用经验,对一块容量为2G bits的NAND芯片,分别采用这两种存储方式的驱动的加载速度相差不大,甚至几乎感觉不出来。??

建立bbt后,以后在做擦除等操作时,就不用每次都去验证当前block是否是个坏块了,因为从bbt中就可以得到这个信息。另外,若在读写等操作时,发现产生了新的坏块,那么除了标志这个block是个坏块外,也还需更新bbt。??

接下来,介绍一下MTD是如何查找或者建立bbt的。??

1、MTD中与bbt相关的结构体??

struct nand_chip中的scan_bbt函数指针所指向的函数,即nand_default_bbt函数会首先检查struct nand_chip中options成员变量,如果当前NAND芯片是AG-AND类型的,会强制把bbt存储在NAND芯片中,因为这种类型的NAND 芯片中含有厂家标注的“好块”信息,擦除这些block时会导致丢失坏块信息。??

接着 nand_default_bbt函数会再次检查struct nand_chip中options成员变量,根据它是否定义了NAND_USE_FLASH_BBT,而为struct nand_chip中3个与bbt相关的结构体附上不同的值,然后再统一调用nand_scan_bbt函数,nand_scan_bbt函数会那3个结构体的不同的值做不同的动作,或者把bbt存储在NAND芯片中,或者把bbt存储在内存中。?

struct?nand_chip?{?
????……?
????uint8_t?????*bbt?
????struct?nand_bbt_descr????*bbt_td;?
????struct?nand_bbt_descr????*bbt_md;?
????struct?nand_bbt_descr????*badblock_pattern;?
????……

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就是在nand_default_bbt函数中赋值的3个结构体。它们虽然是相同的结构体类型,但却有不同的作用和含义。?
其中bbt_td和bbt_md是主bbt和镜像bbt的描述符(镜像bbt主要用来对bbt的update和备份),它们只在把bbt存储在NAND芯片的情况下使用,用来从NAND芯片中查找bbt。若bbt存储在内存中,bbt_td和bbt_md将会被赋值为NULL。??

badblock_pattern就是坏块信息的pattern,其中定义了坏块信息在oob中的存储位置,以及内容(即用什么值表示这个block是个坏块)。??

通常用1或2个字节来标志一个block是否为坏块,这1或2个字节就是坏块信息,如果这1或2个字节的内容是0xff,那就说明这个block是好的,否则就是坏块。对于坏块信息在NAND芯片中的存储位置,small page(每页512 Byte)和big page(每页2048 Byte)的两种NAND芯片不尽相同。一般来说,small page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第六个字节中,而big page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第1和第2个字节中。??

我不能确定是否所有的NAND芯片都是如此布局,但应该绝大多数NAND芯片是这样的,不过,即使某种NAND芯片的坏块信息不是这样的存储方式也没关系,因为我们可以在badblock_pattern中自己指定坏块信息的存储位置,以及用什么值来标志坏块(其实这个值表示的应该是“好块”,因为MTD会把从oob中坏块信息存储位置读出的内容与这个值做比较,若相等,则表示是个“好块”,否则就是坏块)。?

struct?nand_bbt_descr?{?
????int????options;?
????int????pages[NAND_MAX_CHIPS];?
????int????offs;?
????int????veroffs;?
????uint8_t????version[NAND_MAX_CHIPS];?
????int????len;?
????int????maxblocks;?
????int????reserved_block_code;?
????uint8_t????*pattern;


其二是调用那个search_read_bbts函数试着在NAND芯片的maxblocks(请见上文关于struct nand_bbt_descr中maxblocks的说明)个block中查找bbt是否存在,若找到,就可以读取bbt了。??

MTD 查找bbt的过程为:如果你在bbt_td和bbt_md的options 成员变量中定义了 NAND_BBT_LASTBLOCK,那么MTD就会从NAND芯片的最后一个block开始查找(在default情况下,MTD就是这么干的),否则就从第一个block开始查找。??

与查找oob中的坏块信息时类似,MTD会从所查找block的第一个page的oob中读取内容,然后与bbt_td或bbt_md中patter指向的内容做比较,若相等,则表示找到了bbt,否则就继续查找下一个block。顺利的情况下,只需查找一个block中就可以找到bbt,否则MTD最多会查找maxblocks个block。?
若找到了bbt,就把该bbt所在的page地址保存到bbt_td或bbt_md的pages成员变量中,否则pages的值为-1。??

如果系统中有多片NAND芯片,并且为每一片NAND芯片都建立一个bbt,那么就会在每片NAND芯片上重复以上过程。??

接着,nand_scan_bbt函数会调用check_create函数,该函数会判断是否找到了bbt,其实就是判断bbt_td或者bbt_md中 pages成员变量的值是否有效。若找到了bbt,就会把bbt从NAND芯片中读取出来,并保存到struct nand_chip中bbt指针指向的内存中;若没找到,就会调用create_bbt函数建立bbt(与bbt存储在内存中时情况一样),同时把bbt 写入到NAND芯片中去。

MTD坏块管理(二)-内核获取Nandflash的参数过程

1?人收藏此文章,?133?次阅读 共

MTD在Linux内核中同样以模块的形式被启用,TCC_MTD_IO_Init()函数完成了nand_chip初始化、mtd_info初始注册,

nand_chip在TCC_MTD_IO_Init函数中的实例名称是this,mtd_info 的实例名称为TCC_mtd,这里有一个比较巧妙的处理方法:

this=(struct nand_chip*)(&TCC_mtd[1]);

获得必要的信息后(包括nand_chip方法的绑定),流程进入nand_scan(TCC_mtd,1).

调用nand_scan_ident(mtd,maxchips)和nand_scan_tail(mtd);

*从nand_get_flash_type(...)函数中可以看出每个nandflash前几个字节所代表的意思都是约定好了的:

第二个字节:设备ID

第四个字节:extid (比较总要)

Linux内核在nand_flash_ids参照表中,通过匹配上述设备ID来查找nandflash的详细信息,

struct nand_flash_dev nand_flash_ids[]={

{"NAND 16MiB 1,8V 8-bit",? 0x33,512,16,0x4000,0},

466 struct nand_flash_dev {

468 ? ? int id;

470 ? ? unsigned long chipsize;

472 ? ? unsigned long options; ? ? ?

值得一提的是,MTD子系统会把从nand_flash_ids表中找到的chipsize复制给mtd->size,这在有些应用中显得不合适,

上,可以划分任意大小的MTD分区,错误的mtd->size的后果非常严重,造成系统启动慢,整个MTD的坏块管理机制瘫痪等等。

mtd可写区大小:mtd->writesize=1024<<(extid&0x03);

mtdoob区大小:extid>>=2;mtd->oobsize = (8<<(extid&0x1))*(mtd->writesize>>9);

mtd擦写块大小:extid>>=2;mtd->erasesize=(64*1024)<<(extid&0x03);

可以看出第四个字节extid的意义:

?? |无用|数据宽度|擦写块算阶|无用|oob算阶| ?可写区算阶|

if(mtd->writesize>512||(busw&NAND_BUSWIDTH_16))

else

对于Samsun和Hynix的MLC型nandflash,坏块标记所在的页是每块的最后一个页,而Samsung,Hynix,和AMD的SLC型nandflash

坏块标记保存在块的最后一页中:chip->options |= NAND_BBT_SCANLASTPAGE;

nand_scan之后调用nand_scan_tail(mtd)函数,

3338 ? ? mtd->read = nand_read;

3340 ? ? mtd->panic_write = panic_nand_write;

3342 ? ? mtd->write_oob = nand_write_oob;

nand_scan_tail(...)调用chip->scan_bbt()完成坏块表的有关操作。

即真正用于坏块操作的是nand_default_bbt函数,该函数在nand_bbt.c中被定义。

(编辑:李大同)

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

    推荐文章
      热点阅读