wince系统中对nand坏块的修正
产生坏块的原因是因为NANDFlash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,所以,在NAND
的生产中及使用过程中会产生坏块。
一、坏块的具体表现:
当编程/擦除这个块时,不能将某些位拉高,这会造成Page Program和Block Erase操作时的错误,相应地反映到
Status Register的相应位。
二、坏块的种类:
1.先天性坏块
这种坏块是在生产过程中产生的,一般芯片原厂都会在出厂时都会将坏块第一个page的spare area的第6个byte标记为不等于
0xff的值。
2. 后天性坏块
这种坏块是在NAND Flash使用过程中产生的,如果Block Erase
或者Page Program错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和先天性坏块信息保持一致,
将新发现的坏块的第一个page的spare area的第6个Byte标记为非0xff的值,这个坏块标记字节的位置不具有通用性,具体以nand的datasheet为准。
三、坏块的处理
理解了先天性坏块和后天性坏块后,我们已明白NAND Flash出厂时在spare area中已经反映出了坏块信息,因此,如果在擦除一个块之前,一定要先check一下spare area的第6个byte是否是
0xff,如果是就证明这是一个好块,可以擦除;如果是非0xff,那么就不能擦除。
不过,这样处理可能会错杀伪坏块,因为在芯片操作过程中可能由于电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可靠性及软件设计的简单化,坏块一个也不能放过。
但是wince为了防止这种伪坏块的浪费,采用了一定的补救措施来进一步验证该坏块为物理坏块还是伪坏块。该部分实现在BOOL WriteFlashReserved(PBYTE pBuffer, UINT32 dwLength)函数里,具体代码如下:
for (i=0; i < dwSectorSize; i++)
{
// 写入新的Block时, 作一些判断
if (i%g_FlashInfo.wSectorsPerBlock == 0)
{
if (!FMD_ReadSector(dwCurrentWriteBlock*g_FlashInfo.wSectorsPerBlock,
NULL, g_pSectorInfoBuf, 1))
{
OALLog(L"Fail read info %d\n", dwCurrentWriteBlock);
}
RETAILMSG(1, (TEXT("g_pSectorInfoBuf->bBadBlock1 = %d \r\n", g_pSectorInfoBuf->bBadBlock)));//add by zhang , 20150303
//add end
// 算出这个物理block的值
BadBlockCheck:
dwCurrentWriteBlock = dwStartBlock + dwSkipBlock + i/g_FlashInfo.wSectorsPerBlock;
// 擦除这个block
if (!FMD_EraseBlock(dwCurrentWriteBlock))
{
OALLog(L"Fail erase %d\n", dwCurrentWriteBlock);
goto BadBlock;
}
// 再次读出info
if (!FMD_ReadSector(dwCurrentWriteBlock*g_FlashInfo.wSectorsPerBlock,
NULL, g_pSectorInfoBuf, 1))
{
OALLog(L"Fail read info %d\n", dwCurrentWriteBlock);
goto BadBlock;
}
RETAILMSG(1, (TEXT("g_pSectorInfoBuf->bBadBlock2 = %d \r\n", g_pSectorInfoBuf->bBadBlock)));//add
by zhang , 20150303
// 擦除后, 再次读出, 却发现bBadBlock不为0xFF, 是物理坏块!
if ((g_pSectorInfoBuf->bBadBlock != 0xff) || (g_pSectorInfoBuf->bOEMReserved
!= 0xff))
{
OALLog(L"Fail oxff %d\n", dwCurrentWriteBlock);
goto BadBlock;
}
goto GoodBlock;
BadBlock:
if (dwCurrentWriteBlock >= LOGO_BLOCK)
{
// 在写Logo区时允许坏块
FMD_SetBlockStatus(dwCurrentWriteBlock, BLOCK_STATUS_BAD);
dwSkipBlock++; //移到下一个Block
goto BadBlockCheck;
}
else
{
RETAILMSG(1, (TEXT("WriteFlaseReserved Fail.
#%x\n"), dwCurrentWriteBlock));
return FALSE;
}
GoodBlock:
;
}
以上为部分实现代码,红色的部分为我加的串口打印消息,主要是将处理之前和处理之后该块的坏块标记位的值打印出来,以方便观察是否能够将坏块通过处理之后变为好块。
串口打印的消息如下:
CheckSum: eecf
CheckSum Success, USBDownLoadRaw Done
WriteFlashReserved
g_pSectorInfoBuf->bBadBlock1 = 0
g_pSectorInfoBuf->bBadBlock2 = 255
g_pSectorInfoBuf->bBadBlock1 = 0
g_pSectorInfoBuf->bBadBlock2 = 255
以上部分是下载EBOOT时候打印的消息,EBOOT占两个block;
Send image (DNW->UsbPort->Transmite)
Supported: stepldr.nb0 eboot.nb0 *.bmp nk.bin(<32MB)
ReadCheckSum: 395f
CheckSum: 395f
CheckSum Success, USBDownLoadRaw Done
WriteFlashReserved
g_pSectorInfoBuf->bBadBlock1 = 0
g_pSectorInfoBuf->bBadBlock2 = 255
以上部分是下载STEPLDR时候打印的消息,STEPLDR占一个block;
处理方法从代码部分也能看得出来,首先对将要写的块进行擦除,擦除的时候忽略坏块的标志,也就是说不管好块还是坏块都进行擦除,擦除其实就相当于对当前块的所有地址全部写FF,写完之后会从新生产坏块标记,保存在spare area区域;然后再次读出该spare area区域的数据进行判断,如果为FF则表示修正成功,该块为好块,可以进行读写操作,如果为非FF,则证明该块为不可修正的坏块,禁止对其读和写操作,这样既避免了错杀一千的浪费,同时又保证了数据传输的稳定性。
版权声明:本文为博主原创文章,未经博主允许不得转载。