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;
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,則證明該塊爲不可修正的壞塊,禁止對其讀和寫操作,這樣既避免了錯殺一千的浪費,同時又保證了數據傳輸的穩定性。