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

uboot移植中对NAND Flash读数据时地址移位的疑惑

发布时间:2020-12-15 07:09:49 所属栏目:百科 来源:网络整理
导读:移植环境: Linux系统:Fedora9 交叉编译环境:arm-linux-gcc4.4.3 目标板:华中科技大学惠世科技S3C2440实验箱,采用SAMSUNG公司型号为K9F2G08U0B 的 256M*8Bit?NAND?Flash?Memory 1.? NAND?Flash的 组织结构 ? NAND? F lash 芯片的存储空间是按照块和页的概

移植环境:

Linux系统:Fedora9

交叉编译环境:arm-linux-gcc4.4.3

目标板:华中科技大学惠世科技S3C2440实验箱,采用SAMSUNG公司型号为K9F2G08U0B256M*8Bit?NAND?Flash?Memory


1.?NAND?Flash的组织结构

?NAND?Flash芯片的存储空间是按照块和页的概念来组织的。现在市面上的NAND主要按大页和小页两种存储类型来进行数据管理。以NAND型号K9F2G08U0B为例,该NAND为大页结构,即芯片每块(Block)64(Page),每页有2K?Byte的数据存储区和64Byte的冗余数据区(用来存放ECC校验码)。2K?Byte的数据存储区作为数据缓冲单元,用来实现I/0缓冲和存储器之间的数据传输。

?NAND?Flash的物理层驱动主要涉及到:NAND?Flash的初始化(ID以及相关属性信息的读取)、擦除(以块作为单位),数据读写(以页作为基本单位)。在这一系列的过程中,会涉及到NAND?Flash命令的发送以及?NAND?Flash地址的发送。其中地址的发送会因NAND?Flash型号的不同而有所区别。一般来说,小页的?NAND?Flash其地址周期通常为4个:1个列地址(Column?Address)和3个行地址(Row?Address),而对大页的NAND?Flash来说,列地址至少是2个周期,行地址会因芯片的容量大小而有所区别。本文采用的三星NAND?Flash芯片是2个列地址和3个行地址行地址就是页地址,列地址为页内地址)。对于?NAND?FLASH?来讲,地址和命令只能在I/O[7:0]上并行传递。NAND?Flash以页为单位读写数据,而以块为单位擦除数据。NAND的数据传输方式有基本的I/O传输方式,即在I/O[7:0]数据线上进行的数据传输。这种操作的缺点是系统CPU要频繁参与控制数据的传输,会影响到数据传输速度。S3C2440芯片集成了?NAND?Flash控制寄存器,大大简化了对?NAND?Flash的操作。

K9F2G08U0B?Array?Organization

2.?NAND?Flash?中的坏块(Bad?Block)?

?NAND?Flash?中,一个块中含有?1?个或多个位是坏的,就称为其为坏块?Bad?Block。坏块的稳定性是无法保证的,也就是说,不能保证你写入的数据是对的,或者写入对了,读出来也不一定对的。与此对应的正常的块,肯定是写入读出都是正常的。坏块有两种:

?(1)?出厂时就有存在的坏块:

?一种是出厂的时候,也就是,你买到的新的,还没用过的?NAND?Flash,就可以包含了坏块。此类出厂时就有的坏块,被称作?factory?(masked)?bad?block?或?initial?bad/invalid?block,在出厂之前,就会做对应的标记,标为坏块。

?(2)?使用过程中产生的坏块:

?第二类叫做在使用过程中产生的,由于使用过程时间长了,在擦块除的时候,出错了,说明此块坏了,也要在程序运行过程中,发现,并且标记成坏块的。具体标记的位置,和上面一样。这类块叫做?worn-out?bad?block。即用坏了的块。

2.1坏块的标记

?具体标记的地方是,对于现在常见的页大小为2K的?NAND?Flash,是块中第一个页的?oob?起始位置的第1个字节(旧的小页面,pagesize是512B至256B的?NAND?Flash,坏块标记是第?6?个字节)。如果不是?0xFF,?就说明是坏块。相对应的是,所有正常的块,好的块,里面所有数据都是?0xFF?的。不过,对于现在新出的有些?Nand?Flash,很多标记方式,有些变化,有的变成该坏块的第一个页或者第二个页,也有的是,倒数最后一个或倒数第二个页,用于标记坏块的。具体的信息,请参考对应的?Nand?Flash?的数据手册,其中会有说明。对于坏块的标记,本质上,也只是对应的?flash?上的某些字节的数据是非?0xFF?而已,所以,只要是数据,就是可以读取和写入的。也就意味着,可以写入其他值,也就把这个坏块标记信息破坏了。对于出厂时的坏块,一般是不建议将标记好的信息擦除掉的。uboot?中有个命令是“nand?scrub”就可以将块中所有的内容都擦除了,包括坏块标记,不论是出厂时的,还是后来使用过程中出现而新标记的。一般来说,不建议用这个。其实最好的做法是,用“nand?erase”只擦除好的块,对于已经标记坏块的块,不要轻易擦除掉,否则就很难区分哪些是出厂时就坏的,哪些是后来使用过程中用坏的了。

2.2坏块的管理

?对于坏块的管理,在Linux系统中,叫做坏块管理(BBM,Bad?Block?Management),对应的会有一个表去记录好块,坏块的信息,以及坏块是出厂就有的,还是后来使用产生的,这个表叫做坏块表(BBT,Bad?Block?Table)?。在?Linux?内核?MTD?架构下的Nand?Flash驱动,和?Uboot?中?NAND?Flash?驱动中,在加载完驱动之后,如果你没有加入参数主动要求跳过坏块扫描的话,那么都会去主动扫描坏块,建立必要的?BBT?的,以备后面坏块管理所使用。K9F2G08U0B建立BBT的流程图如下所示:

2.3坏块的比例

?而关于好块和坏块,Nand?Flash?在出厂的时候,会做出保证:

?1.关于好的,可以使用的块的数目达到一定的数目,比如三星的K9F2G08U0B,整个?Flash一共有2048个块,出厂的时候,保证好的块至少大于2008个,也就是意思是,你新买到这个型号的Nand?Flash,最坏的可能,?有?2048-2008=40个坏块。不过,事实上,现在出厂时的坏块,比较少,绝大多数,都是使用时间长了,在使用过程中出现的。

2.保证第一个块是好的,并且一般相对来说比较耐用。做此保证的主要原因是,很多?NandFlash?坏块管理方法中,就是将第一个块,用来存储上面提到的?BBT,否则,都是出错几率一样的块,那么也就不太好管理了,连放?BBT?的地方,都不好找了。一般来说,不同型号的Nand?Flash的数据手册中,也会提到自己的这个NAND?Flash,最多允许多少个坏块。

3.?移植uboot?NAND?Flash代码的疑问

参考Mini2440之U-boot使用及移植详细手册.pdf,移植NAND?Flash代码重定向时,新添加了nand_read.c源文件。摘录读取NAND?Flash读取数据函数如下:

点击(此处)折叠或打开

  1. static int nand_read_page_ll(struct boot_nand_t * nand,unsigned char *buf,?
  2. ?? ??unsigned long addr)
  3. {
  4. unsigned short *ptr16 = (unsigned short *)buf;
  5. unsigned int i,page_num;
  6. nand_clear_RnB();
  7. NFCMD = NAND_CMD_READ0;//发送读数据命令
  8. if (nand->page_size == 512) {
  9. /* Write Address */
  10. NFADDR = addr & 0xff;
  11. NFADDR = (addr >> 9) & 0xff;
  12. NFADDR = (addr >> 17) & 0xff;
  13. NFADDR = (addr >> 25) & 0xff;
  14. }?else if (nand->page_size == 2048) {
  15. page_num = addr >> 11; /* addr / 2048 */
  16. /* Write Address */
  17. NFADDR = 0;
  18. NFADDR = 0;
  19. NFADDR = page_num & 0xff;
  20. NFADDR = (page_num >> 8) & 0xff;
  21. NFADDR = (page_num >> 16) & 0xff;
  22. NFCMD = NAND_CMD_READSTART;
  23. }?else {
  24. return -1;
  25. }
  26. nand_wait();
  27. #if defined(CONFIG_S3C2410)
  28. for (i = 0; i < nand->page_size; i++) {
  29. *buf = (NFDATA & 0xff);
  30. buf++;
  31. }
  32. #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
  33. for (i = 0;?i < (nand->page_size>>1); i++) {//读2K字节数据
  34. *ptr16 = NFDATA16;
  35. ptr16++;
  36. }
  37. #endif
  38. return nand->page_size;
  39. }

参照K9F2G08U0B芯片手册,发送地址的时序如下图所示:

前两个周期发送A0~A11,为列地址,即页内地址。后三个周期发送A12~A28,为行地址,即页地址。但是程序中获取页地址是把地址addr右移11位,而不是12位。开始一直很困惑。经过一番探索之后才理解了其中缘由。首先需要了解NAND?Flash页内地址的结构,它分为Main?Field(2K)和Spare?Field(64Byte)两部分,而我们的数据是存放在2K当中,普通的数据读写不对后面64Byte操作,使用A0~A10地址就可实现对2K数据存储区的寻址。下图为K9F2G08U0B页内结构:

函数nand_read_page_ll传递进来的addr地址没有包含对Sapre?Field的寻址。所以只移11位,而不是移12位。NAND?Flash读取数据是以页为单位,当NAND?Flash接收到地址后,会把该页内addr&0x7F地址之后的数据依次输出包括Spare?Field。在nand_read_page_ll函数里,列地址为0,读取此页所有数据。所以for循环读数据时,只读前2K的内容后就跳出循环。

那如何只读取Sapre?Field中的内容呢?以检查K9F2G08U0B中某一块是否是坏块为例,我们需要读取该块第一页地址为2048处的数据是否为0xFF。摘录nand_read.c源文件中检查坏块函数is_bad_block如下:

点击(此处)折叠或打开

  • static int is_bad_block(struct boot_nand_t * nand,unsigned long i)
  • {
  • unsigned char data;
  • unsigned long page_num;
  • nand_clear_RnB();
  • if (nand->page_size == 512) {
  • NFCMD = NAND_CMD_READOOB; /* 0x50 */
  • NFADDR = nand->bad_block_offset & 0xf;
  • NFADDR = (i >> 9) & 0xff;
  • NFADDR = (i >> 17) & 0xff;
  • NFADDR = (i >> 25) & 0xff;
  • }?else?if?(nand->page_size == 2048)?{?????//K9F2G08U0B执行这里
  • page_num = i >> 11; /* addr / 2048 */
  • NFCMD = NAND_CMD_READ0;
  • NFADDR = nand->bad_block_offset & 0xff;
  • NFADDR = (nand->bad_block_offset >> 8) & 0xff;
  • NFADDR = page_num & 0xff;
  • NFADDR = (page_num >> 8) & 0xff;
  • NFADDR = (page_num >> 16) & 0xff;
  • NFCMD = NAND_CMD_READSTART;
  • } else {
  • return -1;
  • }
  • nand_wait();
  • data = (NFDATA & 0xff);????//读2048处一个字节数据
  • if (data != 0xff)
  • return 1;
  • return 0;
  • }
  • 在这里只需把列地址的值设为2048,即0x80。此时就需要用到A11来寻址了。这里只需要读取第一个字节就可以了。

    小结:

    这里开始无法理解的原因是不知道传递进来的地址addr不包括对Spare?Feild的寻址,只有对2K数据的寻址。检查是否是坏块,是直接给列地址赋值为0x80,并没有用到传递进来的地址参数。

    附nand_read.c源码(摘自Mini2440之U-boot使用及移植详细手册.pdf):

    点击(此处)折叠或打开

  • /*
  • *?nand_read.c:?Simple NAND read functions?for?booting from NAND
  • *
  • *?This?is?used by cpu/arm920/start.S assembler code,
  • *?and?the board-specific linker script must make sure this
  • *?file?is?linked within the first 4kB of NAND flash.
  • *
  • *?Taken from GPLv2 licensed vivi bootloader,
  • *?Copyright?(C)?2002 MIZI Research,?Inc.
  • *
  • *?Author:?Hwang,?Chideok?<hwang@mizi.com>
  • *?Date?:?$Date:?2004/02/04 10:37:37 $
  • *
  • *?u-boot integration?and?bad-block skipping?(C)?2006 by OpenMoko,?Inc.
  • *?Author:?Harald Welte?<laforge@openmoko.org>
  • */
  • #include?<common.h>
  • #include?<linux/mtd/nand.h>
  • #define __REGb(x)?(*(volatile unsigned char?*)(x))
  • #define __REGw(x)?(*(volatile unsigned short?*)(x))
  • #define __REGi(x)?(*(volatile unsigned?int?*)(x))
  • #define NF_BASE 0x4e000000
  • #if?defined(CONFIG_S3C2410)
  • #define NFCONF __REGi(NF_BASE?+?0x0)
  • #define NFCMD __REGb(NF_BASE?+?0x4)
  • #define NFADDR __REGb(NF_BASE?+?0x8)
  • #define NFDATA __REGb(NF_BASE?+?0xc)
  • #define NFSTAT __REGb(NF_BASE?+?0x10)
  • #define NFSTAT_BUSY 1
  • #define nand_select()?(NFCONF?&=?~0x800)
  • #define nand_deselect()?(NFCONF?|=?0x800)
  • #define nand_clear_RnB()?do?{}?while?(0)
  • #elif defined(CONFIG_S3C2440)?||?defined(CONFIG_S3C2442)
  • #define NFCONF __REGi(NF_BASE?+?0x0)
  • #define NFCONT __REGi(NF_BASE?+?0x4)
  • #define NFCMD __REGb(NF_BASE?+?0x8)
  • #define NFADDR __REGb(NF_BASE?+?0xc)
  • #define NFDATA __REGb(NF_BASE?+?0x10)
  • #define NFDATA16 __REGw(NF_BASE?+?0x10)
  • #define NFSTAT __REGb(NF_BASE?+?0x20)
  • #define NFSTAT_BUSY 1
  • #define nand_select()?(NFCONT?&=?~(1?<<?1))
  • #define nand_deselect()?(NFCONT?|=?(1?<<?1))
  • #define nand_clear_RnB()?(NFSTAT?|=?(1?<<?2))
  • #endif
  • static inline void nand_wait(void)
  • {
  • ????int?i;
  • ????while?(!(NFSTAT?&?NFSTAT_BUSY))
  • ????for?(i=0;?i<10;?i++);
  • }
  • struct boot_nand_t?{
  • ????int?page_size;
  • ????int?block_size;
  • ????int?bad_block_offset;
  • ????//?unsigned long size;
  • };
  • #if?0
  • #if?defined(CONFIG_S3C2410)?||?defined(CONFIG_MINI2440)
  • /*?configuration?for?2410 with 512byte sized flash?*/
  • #define NAND_PAGE_SIZE 512
  • #define BAD_BLOCK_OFFSET 5
  • #define NAND_BLOCK_MASK?(NAND_PAGE_SIZE?-?1)
  • #define NAND_BLOCK_SIZE 0x4000
  • #else
  • /*?configuration?for?2440 with 2048byte sized flash?*/
  • #define NAND_5_ADDR_CYCLE
  • #define NAND_PAGE_SIZE 2048
  • #define BAD_BLOCK_OFFSET NAND_PAGE_SIZE
  • #define NAND_BLOCK_MASK?(NAND_PAGE_SIZE?-?1)
  • #define NAND_BLOCK_SIZE?(NAND_PAGE_SIZE?*?64)
  • #endif
  • /*?compile?time?failure?in?case?of an invalid configuration?*/
  • #if?defined(CONFIG_S3C2410)?&&?(NAND_PAGE_SIZE?!=?512)
  • #error?"S3C2410 does not support nand page size != 512"
  • #endif
  • #endif
  • static?int?is_bad_block(struct boot_nand_t?*?nand,?unsigned long i)
  • {
  • ????unsigned char data;
  • ????unsigned long page_num;
  • ????nand_clear_RnB();
  • ????if?(nand->page_size?==?512)?{
  • ????????NFCMD?=?NAND_CMD_READOOB;?/*?0x50?*/
  • ????????NFADDR?=?nand->bad_block_offset?&?0xf;
  • ????????NFADDR?=?(i?>>?9)?&?0xff;
  • ????????NFADDR?=?(i?>>?17)?&?0xff;
  • ????????NFADDR?=?(i?>>?25)?&?0xff;
  • ????}?else?if?(nand->page_size?==?2048)?{
  • ????????page_num?=?i?>>?11;?/*?addr?/?2048?*/
  • ????????NFCMD?=?NAND_CMD_READ0;
  • ????????NFADDR?=?nand->bad_block_offset?&?0xff;
  • ????????NFADDR?=?(nand->bad_block_offset?>>?8)?&?0xff;
  • ????????NFADDR?=?page_num?&?0xff;
  • ????????NFADDR?=?(page_num?>>?8)?&?0xff;
  • ????????NFADDR?=?(page_num?>>?16)?&?0xff;
  • ????????NFCMD?=?NAND_CMD_READSTART;
  • ????}?else?{
  • ????????return?-1;
  • ????}
  • ????nand_wait();
  • ????data?=?(NFDATA?&?0xff);
  • ????if?(data?!=?0xff)
  • ????????return 1;
  • ????return 0;
  • }
  • static?int?nand_read_page_ll(struct boot_nand_t?*?nand,?unsigned char?*buf,?unsigned long addr)
  • {
  • ????unsigned short?*ptr16?=?(unsigned short?*)buf;
  • ????unsigned?int?i,?page_num;
  • ????nand_clear_RnB();
  • ????NFCMD?=?NAND_CMD_READ0;
  • ????if?(nand->page_size?==?512)?{
  • /*?Write Address?*/
  • ????NFADDR?=?addr?&?0xff;
  • ????NFADDR?=?(addr?>>?9)?&?0xff;
  • ????NFADDR?=?(addr?>>?17)?&?0xff;
  • ????NFADDR?=?(addr?>>?25)?&?0xff;
  • ????}?else?if?(nand->page_size?==?2048)?{
  • ????page_num?=?addr?>>?11;?/*?addr?/?2048?*/
  • /*?Write Address?*/
  • ????NFADDR?=?0;
  • ????NFADDR?=?0;
  • ????NFADDR?=?page_num?&?0xff;
  • ????NFADDR?=?(page_num?>>?8)?&?0xff;
  • ????NFADDR?=?(page_num?>>?16)?&?0xff;
  • ????NFCMD?=?NAND_CMD_READSTART;
  • ????}?else?{
  • ????????return?-1;
  • ????}
  • ????nand_wait();
  • #if?defined(CONFIG_S3C2410)
  • ????for?(i?=?0;?i?<?nand->page_size;?i++)?{
  • ????????*buf?=?(NFDATA?&?0xff);
  • ????????buf++;
  • ????}
  • #elif defined(CONFIG_S3C2440)?||?defined(CONFIG_S3C2442)
  • ????for?(i?=?0;?i?<?(nand->page_size>>1);?i++)?{
  • ????????*ptr16?=?NFDATA16;
  • ????????ptr16++;
  • ????}
  • #endif
  • ????return nand->page_size;
  • }
  • static unsigned short nand_read_id()
  • {
  • ????unsigned short res?=?0;
  • ????NFCMD?=?NAND_CMD_READID;
  • ????NFADDR?=?0;
  • ????res?=?NFDATA;
  • ????res?=?(res?<<?8)?|?NFDATA;
  • ????return res;
  • }
  • extern unsigned?int?dynpart_size[];
  • /*?low level nand read?function?*/
  • int?nand_read_ll(unsigned char?*buf,?unsigned long start_addr,?int?size)
  • {
  • ????int?i,?j;
  • ????unsigned short nand_id;
  • ????struct boot_nand_t nand;
  • ????/*?chip Enable?*/
  • ????nand_select();
  • ????nand_clear_RnB();
  • ????for?(i?=?0;?i?<?10;?i++)
  • ????????;
  • ????nand_id?=?nand_read_id();
  • ????if?(0)?{?/*?dirty little hack?to?detect?if?nand id?is?misread?*/
  • ????????unsigned short?*?nid?=?(unsigned short?*)0x31fffff0;
  • ????????*nid?=?nand_id;
  • ????}
  • ????if?(nand_id?==?0xec76?||?/*?Samsung K91208?*/
  • ????????nand_id?==?0xad76?)?{?/*Hynix HY27US08121A*/
  • ????????nand.page_size?=?512;
  • ????????nand.block_size?=?16?*?1024;
  • ????????nand.bad_block_offset?=?5;
  • ????????//?nand.size?=?0x4000000;
  • ????}?else?if?(nand_id?==?0xecf1?||?/*?Samsung K9F1G08U0B?*/
  • ????????nand_id?==?0xecda?||?/*?Samsung K9F2G08U0B?*/
  • ????????nand_id?==?0xecd3?)?{?/*?Samsung K9K8G08?*/
  • ????????nand.page_size?=?2048;
  • ????????nand.block_size?=?128?*?1024;
  • ????????nand.bad_block_offset?=?nand.page_size;
  • ????????//?nand.size?=?0x8000000;
  • ????}?else?{
  • ????????return?-1;?//?hang
  • }
  • ????if?((start_addr?&?(nand.block_size-1))?||?(size?&?(nand.block_size-1)))
  • ????????return?-1;?/*?invalid alignment?*/
  • ????for?(i=start_addr;?i?<?(start_addr?+?size);)?{
  • ????#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
  • ????????if?(i?&?(nand.block_size-1)==?0)?{
  • ????????????if?(is_bad_block(&nand,?i)?||
  • ????????????is_bad_block(&nand,?i?+?nand.page_size))?{
  • ????????????/*?Bad block?*/
  • ????????????i?+=?nand.block_size;
  • ????????????size?+=?nand.block_size;
  • ????????????continue;
  • ????????????}
  • ????????}
  • ????#endif
  • ????????j?=?nand_read_page_ll(&nand,?buf,?i);
  • ????????i?+=?j;
  • ????????buf?+=?j;
  • ????}
  • /*?chip Disable?*/
  • ????nand_deselect();
  • ????return 0;
  • }
  • (编辑:李大同)

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

      推荐文章
        热点阅读