作者:LoongEmbedded(kandi)
类别:WINCE bootloader开发
注:本文nand flash 是基于K9F1G08U0B
K9F1G08U0B的阵列结构图如下
图1
Nand flash存储操作特点:
以页为单位进行读写,以block为单位进行擦除
我们基于usb来download镜像的eboot来学习nandflash的应用,download的菜单一般如下所示:
图2
基于usb来download内核映像的步骤是:F -> 9 -> U,那么我们先来看F对应的功能
1. 格式化nandflash
1.1 ECC校验
nandflash的每一页有两区:main区和spare区,main区用于存储正常的数据,spare区用于存储其他附加信息,其中就包括ECC校验码。当我们在写入数据的时候,我们就计算这一页数据的ECC校验码,然后把校验码存储到spare区的特定位置中,在下次读取这一页数据的时候,同样我们也计算ECC校验码,然后与spare区中的ECC校验码比较,如果一致则说明读取的数据正确,如果不一致则不正确。ECC的算法较为复杂,好在s3c2440能够硬件产生ECC校验码,这样就省去了不少的麻烦事。s3c2443即可以产生main区的ECC校验码,也可以产生spare区的ECC校验码。因为K9F1G08U0B是8位IO口,因此s3c2443共产生4个字节的main区ECC码和2个字节的spare区ECC码。在这里我们规定,在每一页的spare区的第0个地址到第3个地址存储main区ECC,第4个地址和第5个地址存储spare区ECC。产生ECC校验码的过程为:在读取或写入哪个区的数据之前,先解锁该区的ECC,以便产生该区的ECC。在读取或写入完数据之后,再锁定该区的ECC,这样系统就会把产生的ECC码保存到相应的寄存器中。main区的ECC保存到NFMECC0/1中(因为K9F1G08U0B是8位IO口,因此这里只用到了NFMECC0),spare区的ECC保存到NFSECC中。对于读操作来说,我们还要继续读取spare区的相应地址内容,已得到上次写操作时所存储的main区和spare区的ECC,并把这些数据分别放入NFMECCD0/1和NFSECCD的相应位置中。最后我们就可以通过读取NFESTAT0/1(因为K9F1G08U0B是8位IO口,因此这里只用到了NFESTAT0)中的低4位来判断读取的数据是否正确,其中第0位和第1位为main区指示错误,第2位和第3位为spare区指示错误。
1.2 写sector的SectorInfo结构数据到spare field中。
对应于代码的实现部分如下所示:
图3
下面我们来看看这部分是如何格式化nand flash的,先看结构体SectorInfo的定义:
typedef struct _SectorInfo
{
DWORD dwReserved1; 2 // Reserved - used by FAL,为FAL保留的字节
BYTE bOEMReserved; 3 // For use by OEM,为OEM保留的字节
BYTE bBadBlock; 1 // Indicates if block is BAD,坏块标识
WORD wReserved2; 4 // Reserved - used by FAL,为FAL保留的字节
}SectorInfo, *PSectorInfo;
对这个结构体的初始化代码如下:
图4
我们知道nandflash主要以page(页)为单位进行读写,以block(块)为单位进行擦除。每一页中又分为main区和spare区,main区用于正常数据的存储,spare区用于存储一些附加信息,如块好坏的标记、块的逻辑地址、页内数据的ECC校验和等。K9F1G08U0B中关于一页中main field和spare field的结构如下:
图5
下面接下来通过调用函数来把第0到第3个block标识为只读与保留的坏块,如下代码如下:
图6
这里主要是通过调用函数FMD_WriteSector来实现的,这个函数会调用FMD_LB_WriteSector()
主要部分如下所示:
图7
下面就来看看NAND_LB_WriteSectorInfo的函数体吧
图8
下面合适接着看NAND_LB_WriteSectorInfo函数的后部分
图9
下图是大页面的nandflash一页的结构图:
10
下图是K9F1G08U0B编程和读状态的操作时序图
图11
1.3 调用函数FMD_GetBlockStatus来获取存放eboot之后的所有的blcok的状态
该函数获得nandflash中某一个block的状态。参数为nandflash的block地址。由于nandflash中可能有坏块,所以针对nandflash,这个函数首先会检查当前块是否是坏块,这个一般通过读取当前block的第0个page和第1个page的带外数据。对于小page nandflash一般是读取第5个byte,对于大page nandflash一般读取第0个byte,如果不为0xff表示该块是坏块。当然,至于具体该读哪个byte,最好还是看一下所用nandflash的datasheet,确认一下,不同的厂家可能有所不同。如果发现该块是坏块,应该返回BLOCK_STATUS_BAD。如果不是坏块,需要读取这个块的起始扇区的扇区信息。如果读该扇区信息出错,应该返回BLOCK_STATUS_UNKNOWN,否则,判断独到的信息,返回相应结果。
具体是如何实现的呢,我们先来看函数的调用关系:
FMD_GetBlockStatus() -> FMD_LB_GetBlockStatus() -> FMD_LB_ReadSector() -> NAND_LB_ReadSectorInfo(),主要是通过NAND_LB_ReadSectorInfo函数来实现的,下面就来看看这个函数的内容:
图12
下图是读操作的时序图中的一部分:
图13
接着看NAND_LB_ReadSectorInfo函数的后半部分的实现:
图14
下面接着看ECC_CorrectData函数的实现
图15
下面我们回到FMD_LB_GetBlockStatus函数中来
图16
1.4 擦除eboot之后的非坏块
图17
下图是判读K9F1G08U0B某一块为坏块的根据和流程图
图18
下面来看看是如何擦除块的,函数的调用关系是:FMD_EraseBlock() -> FMD_LB_EraseBlock(),在看FMD_LB_EraseBlock函数之前,先来看看K9F1G08U0B对擦除块的操作时序图
图19
擦除是以块为单位进行的,因此在写地址周期是,只需写两个行周期,在擦除结束前还要判断是否擦除操作成功,FMD_LB_EraseBlock函数体如下:
图20
到此在下载NK.bin之前的格式化动作已经完成。
2. 在nandflash上建立BINFS分区,通过USB下载的NK.bin会保存在这个分区中。
代码入下图所示:
图21
这部分的内容见我的另一篇博文:
一些概念说明:
FAL及FMD做基本定义的說明如下:
FMD(FLASH Media Driver)可针对特定厂商的Flash作Driven、Read、Write、Erase等动作,实际上去对Flash做读写的操作。
FAL(FLASH Abstraction Layer):File System对Flash读写,必須透过此层操作。而此层则再更加FMD所提供的Interface再对Flash做读写。
NFSECC:Whenever data is read or written, the spare area ECC module generates ECC parity code on register
NFSECCD is for ECC in the spare area (Usually, the user will write the ECC value generated from main data area to Spare area, which value will be the same as NFMECC0/1) and which is generated from the main data area.
NFSECCD(nandflash的spare区ECC寄存器),NFMECCD0/1(nandflash的main区ECC寄存器),NFSECC(nandflash用于IO的ECC寄存器)。