基于NAND Flash的轉譯層的設計
目前市場(chǎng)上閃存芯片主要有兩類(lèi),即NAND Flash(Not And Flash ROM)和NOR Flash(Not Or Flash ROM)。前者具有容量大、讀寫(xiě)速度快、芯片面積小、單元密度高、擦除速度快、成本低等特點(diǎn),更適合于大批量數據存儲的嵌入式系統。如今Windows仍是桌面系統的主流,對FAT文件系統提供了天然的支持。然而就技術(shù)而言,FAT文件系統并不適合Flash,因為Flash設備并不是塊設備[1],為了不破壞兼容性,并在NAND型閃存中應用FAT文件系統,國際上提出了閃存轉譯層FTL(Flash Translation Layer)的解決方案。
1 NAND Flash嵌入式存儲系統結構
基于NAND Flash的存儲系統的設計首先要解決壞塊問(wèn)題。由于NAND Flash自身存在固有壞塊并在擦除和編程中又隨機產(chǎn)生壞塊,因此為了提高設備的可靠性應該將這兩種操作分散在閃存不同的塊中,以避免對某塊的過(guò)度操作。
一般的基于NAND Flash嵌入式存儲系統驅動(dòng)結構分為三個(gè)層次:最底層是硬件操作接口,負責將主控芯片與Flash的控制管腳相連,這方面的固件主要實(shí)現對NAND Flash的物理操作;中間層是閃存轉譯層NFTL(NAND FTL),是封裝在Flash驅動(dòng)中的軟件模塊,其作用是將Flash模擬成與磁盤(pán)相類(lèi)似的塊設備,使對上層操作系統而言,NAND Flash就像普通磁盤(pán)一樣被訪(fǎng)問(wèn)。這一層主要是封裝一些特殊的復雜管理控制功能;最上面的層就是文件管理層,功能類(lèi)似于普通磁盤(pán)上的通用文件系統,向上層提供標準的文件操作接口?;贜AND Flash的嵌入式系統存儲結構原理圖如圖1所示。
根據以上兩個(gè)方面,既要在驅動(dòng)中實(shí)現壞塊管理,又要進(jìn)行塊模擬,所以可用的方法有兩種[2]:一是在上層文件系統中解決壞塊問(wèn)題,驅動(dòng)層只實(shí)現本身的功能,文件系統為驅動(dòng)層提供不變的接口,為上層應用程序提供可靠透明的服務(wù)。這種方法較簡(jiǎn)單,開(kāi)發(fā)周期比較短,但只對特定應用的嵌入式系統有很強的適應性;第二種方法是在驅動(dòng)層的NFTL中解決壞塊問(wèn)題,將不可靠的NAND Flash虛擬成可靠的存儲設備,為上層文件系統提供可靠透明服務(wù),這種方法較第一種更復雜,但是此法具有較強的可移植性并能徹底斷絕與文件系統的聯(lián)系,其他文件系統也同樣適用。
本文是以Samsung的NAND Flash K9F2808U0C作為存儲芯片,設計了一種在NFTL上實(shí)現壞塊管理并且實(shí)現連續數據讀取的方法。
2 設計思想
2.1 閃存空間劃分
K9F2808U0C是16 MB×8 bit的NAND Flash,共有1 024個(gè)Block,1 Block=16 KB,32 Page/Block,1 Page=528 B=(512 B+16 B),其中16 B為備用區,主要存放NAND Flash出廠(chǎng)壞塊標記、ECC校驗碼以及用戶(hù)自定義區。K9F2808U0C地址空間是24 bit,分三個(gè)周期依次送入NAND Flash的地址鎖存器。本文使用的地址均為字節地址,數據類(lèi)型為DWORD(4 B)。
將K9F2808U0C的存儲空間劃分為四個(gè)區:壞塊映射表存放區、交換塊區、壞塊映射區和實(shí)際數據存放區。文件系統管理的空間就是實(shí)際的數據存放空間,如圖2所示。
2.2 各分區宏定義
#define FLASH_BLOCK_SIZE 0x40000 //16 KB/Block
#define FLASH_PAGE_SIZE 0x200 //512 B/Page
#define FLASH_SECTOR_SIZE 0x200
//1Page=1Sector(only K9F2808U0C)
#define FLASH_BLOCKS_TABLE 3//壞塊映射表存放塊數
#define FLASH_SWAP_BLOCKS 5 //交換區的塊數
#define FLASH_BAD_BLOCKS_REMAP 50
//壞簇重映區的塊數
#define FLASH_MAX_ADDR 0xFFFFFF
//Flash最大字節地址
各分區首地址計算公式:
FLASH_BLOCK_TABLE_ADDR=FLASH_MAX_ADDR+
1-3*FLASH_BLOCK_SIZE);
FLASH_SWAP_BLOCK_ADDR=(FLASH_BLOCK_
TABLE_ADDR-5*FLASH_BLOCK_SIZE);
FLASH_BAD_BLOCK_REMAP_ADDR=(FLASH_SWAP_
BLOCK_ADDR-50*FLASH_BLOCK_SIZE);
FLASH_MAX_SECTOR_ADDR=(FLASH_MAX_ADDR-
3*FLASH_BLOCK_TABLE_ADDR-5*FLASH_SWAP_
BLOCK_ADDR-50*FLASH_BAD_BLOCK_REMAP_ADDR);
文件系統管理的最大字節地址。
任意地址Addr:
所在塊地址:Addr(~(FLASH_BLOCK_SIZE-1));
塊內偏移地址:Addr(FLASH_BLOCK_SIZE-1);
塊中的頁(yè):(Addr(FLASH_BLOCK_SIZE-1))/FLASH_
PAGE_SIZE;
2.3 分區功能設計
壞塊映射區存放復制3份的壞塊信息BBI(Bad Block Information)表。復制3份是預防系統突然斷電,造成BBI表數據丟失。選擇最后3個(gè)塊,主要是出于固件設計。當Flash首次上電,固件程序通過(guò)讀取Flash ID,獲得設備的容量等信息,然后從Flash的最后一塊中尋找BBI表,如果最后一塊沒(méi)有發(fā)現BBI表,則認為此塊為壞塊,繼續前移尋找,依此類(lèi)推,直到在預留的3個(gè)塊中找到,并將其數據讀入到在主控芯片為其開(kāi)設的RAM中。如果還找不到,則固件認為該片Flash沒(méi)有BBI表。
交換塊區是對NAND Flash進(jìn)行擦除或寫(xiě)操作時(shí)用來(lái)臨時(shí)存放數據,共分配5個(gè)塊。選取5塊是出于可靠性設計。用一個(gè)數組FlashSwapBlockStatus[FLASH_SWAP_BLOCKS]記錄交換塊狀態(tài):有效還是已經(jīng)損壞。初始化時(shí),固件認為所有的交換塊都是有效塊,在隨后對其進(jìn)行擦除或寫(xiě)操作時(shí),通過(guò)讀Flash狀態(tài)寄存器判斷該交換塊的真實(shí)狀態(tài),并記錄在數組中。交換塊的管理圍繞固件請求返回當前可用交換塊地址或當前正在使用的交換塊地址,并判斷標記當前使用的交換塊狀態(tài)為壞。
壞塊映射區是當主機向數據區寫(xiě)數據時(shí),檢測到當前塊(數據區)為壞塊時(shí),將數據寫(xiě)到壞塊映射區中的相應好塊中,并且將這兩個(gè)塊的塊地址記錄到BBI表中,以后主機若要對當前塊(數據區)訪(fǎng)問(wèn)時(shí),只需讀BBI表就可以找到相應映射塊,從而代替壞塊的訪(fǎng)問(wèn)。這樣就使文件系統所見(jiàn)邏輯塊地址LBA(Logical Block Address)變成連續的,但實(shí)際上物理塊地址PBA(Physical Block Address)可能并不連續。上述方法就是壞塊管理的精髓。出于保守設計本文共選50塊作為重映塊。用數組FlashRemapBlockStatus[FLASH_BAD_BLOCKS_REMAP]標識壞塊映射區的狀態(tài):未使用、已使用還是已經(jīng)損壞。初始化時(shí)認為壞塊映射區中所有塊都是好塊。
3 NFTL壞塊管理設計
3.1 構建BBI表
用一數組FlashBadBlockTable[2][FLASH_BAD_BLOCKS_REMAP]存放BBI表。第一維為壞塊(數據區)的塊地址,第二維為映射塊地址(重映塊)。
BBI表的構建可在Flash首次上電時(shí),通過(guò)讀取廠(chǎng)商設置在Flash備用區中的壞塊標記識別壞塊,建立壞塊映射表,此法初始化時(shí)間與Flash的容量成正比;或通過(guò)讀NAND Flash所有塊內容并和0xFF作比較[3],如果不相同,則表示壞塊。這種方法只針對新閃存,并且構建BBI表的時(shí)間長(cháng),主控芯片的占用率高;另一種方法是在Flash初次上電時(shí)先不建立壞塊表,認為當前所有的塊都是好塊,在隨后操作中發(fā)現壞塊,并更新BBI表。本文選擇后一種方法。
3.2 壞塊映射表區的設計
將BBI表通過(guò)特殊的方式保存,以后通過(guò)此種方式的逆向來(lái)識別BBI表。壞塊映射表區的3個(gè)塊的設計方法如下:
(1)在每塊最后一頁(yè)的首字節處做特殊標記,用于標識該塊有沒(méi)有準備被擦除:0xFF表示沒(méi)有準備被擦除;0x00表示該塊已經(jīng)準備被擦除[4]。
(2)在每塊的倒數第二頁(yè)的首字節處寫(xiě)0x00,表示該塊存放BBI表數據;并在第2、3、4、5個(gè)字節處以大端模式存放校驗和,用于校驗該塊中存放的所有數據的正確性。該頁(yè)的剩余字節寫(xiě)0xFF。
(3)在第一頁(yè)起依次寫(xiě):表頭特殊標記(0x0055AAFF)、壞塊總數(FlashBadBlockCount)、BBI表(FlashBadBlockTable)、重映塊狀態(tài)(FlashRemapBlockStatus),其結構如圖3所示。
3.3 邏輯地址映射
在與主機批量數據傳輸時(shí),首先對主機發(fā)送的CBW命令解析,從而固件獲得主機請求數據傳輸的LBA,再對LBA地址映射以此獲得相應的PBA,然后再執行主機命令。具體方法:首先讀取當前總的壞塊數FlashBadBlocksCount,如果為0,表示無(wú)壞塊,LBA就不用映射,直接返回;如果最后一次訪(fǎng)問(wèn)地址與LBA同屬于一個(gè)塊,那么也不用地址映射;如果當前壞塊的數量為1,判斷LBA塊地址與BBI表中壞塊地址是否相同,從而決定是否采取地址映射;如果當前壞塊的數量不為1,就需要查找BBI表判斷是不是含有LBA的塊地址,如果存在,則要地址重新映射,如果不存在,就不用重新映射??梢圆捎枚址ú楸韺?shí)現兩地址快速比較[4]。
3.4 壞塊處理
對映射后的地址進(jìn)行寫(xiě)或擦除過(guò)程中發(fā)現當前塊變壞的處理方法:首先查找映射區中的塊狀態(tài)FlashRemapBlockStatus[FLASH_BAD_BLOCKS_REMAP],尋找可用的映射塊。如果50個(gè)重映塊都被標記為壞或已使用,則程序進(jìn)入死循環(huán);如果找到可用的映射塊,則固件對可能出現壞塊的三種情況進(jìn)行散轉:(1)擦除當前塊時(shí)出錯:將當前塊地址與可用的重映塊地址寫(xiě)入BBI表中,按地址大小排列有利于二分法查表,并返回重映塊地址。(2)復制某頁(yè)從交換區到操作地址時(shí)失?。菏紫全@得當前使用的交換塊的塊地址,然后判斷操作地址是在數據存儲區還是在重映區。如果在重映區,說(shuō)明當前操作地址所對應的在數據存儲區中的塊已經(jīng)是壞塊,并且在這次操作中重映塊也變壞,此時(shí)就應該標記當前映射塊狀態(tài)為壞,并在重映區中尋找下一個(gè)可用重映塊,將原來(lái)在數據存儲區中的塊地址與更新后的映射塊地址寫(xiě)入BBI表中,并返回新的映射后的地址;如果在數據存儲區,就進(jìn)行第一次重映射,更新壞塊表,并且將映射地址返回。然后再完成復制工作。(3)向操作地址寫(xiě)一頁(yè)數據時(shí)出錯:將當前地址所在塊中的前面頁(yè)從交換塊中相應地址復制到重映塊,然后將操作地址所在當前頁(yè)中的沒(méi)有寫(xiě)到的數據從交換塊中復制,并將緩沖區中的數據重新寫(xiě)到重映射的地址中,并返回重映射地址。函數實(shí)現如下:
DWORD FlashDealBadBlock(DWORD Addr,DWORD Type)
{
DWORD i;
DWORD RemapBlockAddr;
DWORD SwapBlockAddr;
while(1)
{
RemapBlockAddr=FlashGetNewRemapBlock();
if(RemapBlockAddr==-1)
return Addr;
switch(Type)
{ case 1:
goto Exit;
break;
case 2:
SwapBlockAddr=FlashGetCurrentSwapBlock();
for(i=0;i(Addr(FLASH_BLOCK_SIZE-1))/
FLASH_PAGE_SIZE+1;i++)
{
if(0x00==(FlashCopyPage(SwapBlockAddr+
i*FLASH_PAGE_SIZE,RemapBlockAddr+
i*FLASH_PAGE_SIZE)))
goto BadRemapBlock;
}
goto Exit;
break;
case 3:
SwapBlockAddr=FlashGetCurrentSwapBlock();
for(i=0;i(Addr(FLASH_BLOCK_SIZE-1))/
FLASH_PAGE_SIZE;i++)
{
if(0x00==(FlashCopyPage(SwapBlockAddr+
i*FLASH_PAGE_SIZE,RemapBlockAddr+
i*FLASH_PAGE_SIZE)))
goto BadRemapBlock;
}
if(0x00==(FlashCopyPage(Addr,RemapBlockAddr+
i*FLASH_PAGE_SIZE)))
goto BadRemapBlock;
goto Exit;
break;
default:
break;
}
BadRemapBlock: FlashMarkRemapBlockBad
(RemapBlockAddr);
}
Exit:FlashUpdateBadBlockTable(Addr,RemapBlockAddr);
return RemapBlockAddr+(Addr(FLASH_BLOCK_SIZE-1));
}
3.5 連續讀寫(xiě)操作
當主機與設備建立批量傳輸數據連接時(shí),固件通過(guò)解析CBW封包獲得起始LBA。對該地址進(jìn)行映射和壞塊管理從而獲得PBA。設置一個(gè)變量,當此變量有效時(shí),表示主機對Flash仍需要讀或寫(xiě)數據,直到此變量失效為止。同時(shí)將主機上一次讀寫(xiě)扇區的地址存入另一個(gè)變量,此變量在連續讀中作用不大,但在連續寫(xiě)時(shí),通過(guò)與上一次寫(xiě)操作地址所在塊做比較,判斷是否同屬一個(gè)塊,以此決定是不是需要進(jìn)行地址跨塊處理。
本文設計了一種針對NAND型的閃存轉譯層,使NFTL完成地址映射和壞塊管理以及連續讀寫(xiě)數據的操作。對NAND Flash的分區設計,使塊管理結構清晰,有利于固件的開(kāi)發(fā)。本文沒(méi)有對Flash的ECC校驗進(jìn)行過(guò)多的設計,這是因為在實(shí)際應用中NAND Flash主要用于存儲多媒體數據(圖片、語(yǔ)音文件)等,并不會(huì )對它進(jìn)行頻繁的寫(xiě)入或擦除操作,而且多媒體文件數據對數據的完整性不敏感[5],所以不需要對存儲在其中的每一位數據進(jìn)行嚴格的ECC校驗,可以通過(guò)另外設計簡(jiǎn)單的校驗方法來(lái)代替ECC校驗。
評論