歡迎您光臨本站 登入註冊首頁

概述

   NAND和NOR是現在市場上兩種主要的非易失快閃記憶體技術相對於NOR而言,NAND結構能提供極高的單元密度,可以達到高存儲密度,並且寫入和擦除速度很快,同時,NA……

    NAND和NOR是現在市場上兩種主要的非易失快閃記憶體技術 相對於NOR而言,NAND結構能提供極高的單元密度,可以達到高存儲密度,並且寫入和擦除速度很快,同時,NAND快閃記憶體的成本要低於NOR 快閃記憶體。因此儘管NAND的介面特殊,管理複雜,讀取速度不及NOR,但是從性價比出發,NAND快閃記憶體逐漸成為嵌入式系統的首選存儲設備。
    NAND快閃記憶體具有以下特點:NAND器件是基於I/O介面的(NOR快閃記憶體是基於Bus的RAM 介面),以頁為單位讀寫,以塊為單位擦除。NAND晶元通過多個引腳傳送命令、地址和數據,使用較複雜的I/O介面來控制,同時,根據NAND規範,NAND快閃記憶體中允許存在壞塊。NAND快閃記憶體的每一頁都有8B (頁長度256B)或者16B (頁長度為512B)的OOB(Out Of Band)數據區,用來存放ECC (Error Checking&Correction)、ECC有效標誌、壞塊標誌等。所有這些決定了基於NAND 的存儲系統設計需要處理不同於其它類型快閃記憶體的特有問題。

1 MTD結構和NAND驅動介面
    Linux上設計了MTD子系統為快閃記憶體類型的設備向上層提供統一介面。MTD的主要目的是實現子系統公用部分,使新的存儲設備的驅動設計更加簡單。一個MTD設備按照用戶訪問的順序可以得到圖1所示的層次結構。
    圖1中NAND特定硬體驅動層為具體NAND設備的驅動,實現特定硬體的具體操作;NAND 通用驅動層是所有NAND設備的公用部分,實現了NAND設備發現、通用NAND讀寫等操作;MTD原始設備層是MTD原始設備的通用代碼,此外還包括各個特定的快閃記憶體設備所註冊的數據,例如NAND分區等。MTD向上提供塊設備和字元設備兩種介面。文件系統通過MTD塊設備介面訪問NAND快閃記憶體驅動。
 
圖1 LinuxMTD的層次結構
    具體的NAND快閃記憶體驅動是和NAND通用驅動相關聯的,要實現一個NAND快閃記憶體硬體驅動,需要實現以下部分:初始化函數,硬體相關的設備就緒函數和控制函數,為了靈活起見,還可以實現硬體相關的命令函數、硬體相關的等待函數和硬體ECC 函數。
    NAND設備驅動初始化時分配必要的內存,設置IO地址,然後利用NAND通用驅動提供的設施,調用nand_scan來完成通用部分的沒置,最後定義快閃記憶體分區並在MTD 中註冊來完成初始化。可以通過Linux內核中的實例了解NAND驅動的結構。

2 和NAND特性相關的問題
    NAND快閃記憶體具有其它類型的快閃記憶體所不具有的特性,因此基於NAND的存儲設計需要考慮這些問題。NAND快閃記憶體的正確、高效使用取決於以下問題的正確解決。
2.1 壞塊問題
    NAND快閃記憶體定位為一種低成本存儲介質,從成本和技術上綜合考慮,NAND快閃記憶體允許存在一定比例的壞塊。壞塊的產生有多種原因,如解碼失敗,地址線錯誤,存儲單元錯誤等,出品前廠商經過測試,將確認的壞塊標記出來。同時,因為NAND快閃記憶體的擦寫壽命有限(一般在105一106次),當使用到一定時限后也會產生壞塊,這些壞塊通過擦除和寫入后的狀態來判斷,產生擦除或寫入失敗的塊應該認為已經損壞,並由文件系統或者其它管理程序標記出來。
    壞塊在NAND快閃記憶體的OOB區域中標記,NAND快閃記憶體的每頁都有16B的OOB(以頁為512B為例),每個塊的第一頁的OOB的第6個B用來記錄該塊是否是壞塊,當該B為非0xff時則表明該塊是壞塊。
    為了正確使用NAND快閃記憶體,需要進行壞塊管理,壞塊管理的基本思想是:避免操作壞塊,同時存讀寫操作時跳過壞塊而使得上層操作看來快閃記憶體存儲區仍然是連續的。
2.2 與文件系統相關的壞塊管理
    在NAND 快閃記憶體上使用較多的文件系統是JFFS2 和Cramfs,其中JFFS2有針對NAND快閃記憶體的實現,因此可以很好地處理NAND特性相關的問題,包括壞塊,但是Cramfs無法意識到NAND快閃記憶體的存在(Cramfs 不是為NAND快閃記憶體設計的),為了能夠在NAND快閃記憶體上使用Cramfs,需要增加壞塊管理,考慮到文件系統的複雜性,直接在Cramfs中增加比較困難,因此本文考慮在NAND 快閃記憶體和Cramfs文件系統之間增加一個管理層次米處理這個問題。
    Samsung S3C2440X 有一個類似的實現方式,但是這個設計存在以下不足:該實現通過存儲在NAND快閃記憶體分區尾部的分區表來定義又一級的分區,本文認為這是多餘的,一般來說用戶不會再在快閃記憶體分區上定義另外的分區,也沒有必要因此而考慮使用專用的操作工具,基於NAND快閃記憶體驅動中定義的分區來直接管理壞塊並作為用戶分區已經足夠,而且,這樣不需要專有工具支持,設計和使用都比較簡單;該實現需要把MTD分區中的壞塊信息存儲到快閃記憶體上,這需受佔用空間,本文的實現通過打開NAND分區時掃描分區上的全部塊來記錄壞塊,由於是每塊僅讀一次OOB的1位,總耗時不會太長;同時也沒有必要把所有的NAND分區部加入到壞塊控制中,只有要使用Cramfs的分區才是需要的,因此應該給予用戶選擇權。
    作者所設計的NAND塊管理模塊BM實現為一個只讀的塊設備驅動程序,它將讀操作的地址經過轉換處理之後用新的地址執行MTD的讀操作。除了執行與MTD和NAND相關的特定處理之外它是標準的塊設備驅動,實現時可以參考文獻4,5。
#define NOT2BLON 256
/*控制標誌,所有NAND設備都有此標誌,需要加入到BM者在其分區上設置此標誌(實際上是清除該標誌)*/
/*初始化、註冊MTDUser,為MTD提供設備加入時的回調函數*/
struct mtd_notifier BM_Notifier={
add:BM_notify add,
remove:BM_notify_remove
};
register_mtd_user(&BM_Notifier);
/*當有新設備加入,或者註冊時已經存在MTD設備時,MTD通過調用BM_notifier_add通知BM 驅動*/
/*BM_notifier_add判斷設備是否需要加入BM,如果是NAND快閃記憶體並且無NOT2BLON標誌則加入BM*/
if (!mtd->type!=MTD_NANDFLASH) return;
if (mtd->flags&MTD_NOT2BLON) return;
/*掃描設備,獲得所有壞塊的地址,並保存到bad_ blocks中*/
bad_blocks=kmalloc(1+mtd->size/mtd->erasesize)*
sizeo(unsigned short),GFP_KERNEL);
offset=0;length=mtd_size-mtd_block_size;
for(;offset<=length;offset+=mtd_block_size){
if(MTD_READOOB(mtd,offset,8,&retlen,oobbuf)<0)
continue;
if(oobbuf!=0xff)
bad_blocks[badnum++]=offset/mtd->erasesize;
}
/*讀操作時,計算所有小於地址old_block_address的壞塊,得到此地址之前的壞塊個數,然後將該地址增加相應的長度作為新地址new_block_adderss訪問設備*/
if(bad_blocks){
unsigned short *bad=bad_blocks;
while(*bad++<=old_block_addrerss)
block++;
}
new_block_adderss=old_block_adderss+block;
    BM_notifier_remove在刪除MTD 設備時調用,執行與BM_notify_add相反的資源操作,釋放佔有的資源。
2.3 損耗均衡
    NAND快閃記憶體的壽命是有限的,為了保持NAND的使用壽命足夠長,必須避免擦寫區域的不均衡,否則快閃記憶體會因為局部達到擦寫極限而報廢,這實際上是浪費 必須實現機制達到磨損均衡(Wear Balance),延長快閃記憶體的有效使用壽命。
    損耗均衡需要由文件系統或者附加的層次來處理。JFFS2採取日誌和順序寫入,很好的解決了這個問題,Cramfs是只讀的,除了初始化之外不進行擦除和寫入操作,不需要關心這個問題;需要使用其它非NAND快閃記憶體特定的文件系統者(比如Ext2等)需要考慮這個問題。

3 NAND存儲系統設計
3.1 NAND驅動設計相關的問題
    除了上述NAND設備的共性問題之外,在設計和實現中還有以下問題需要考慮:
1)地址自動增加問題
    存在兩種類型的NAND地址處理方式:地址自動增加和地址不自動增加。對於地址自動增加的NAND,當讀數據至一頁的末尾時,NAND內部地址自動增加到下一頁的數據區,讀取OOB數據時,也會自動增加到下一頁的OOB區(部分NAND支持通過GND管腳信號把整個數據區和OOB作為一個連續的區域增加地址),這個過程會持續到一塊結束,然後需要重新向NAND快閃記憶體寫命令和地址;地址不自動增加的NAND快閃記憶體的開始每一頁操作都需要重新寫命令和地址,否則內部地址不會自動增加到下一頁,無法繼續正確操作。這是NAND快閃記憶體驅動設計需要注意的,後面就會看到,現在的Linux內核中的MTD版本恰恰存在這方面的問題。
2)設備就緒和時間限制
    確定設備是否就緒用於判斷是否可以開始執行下一個操做,實現的方式有:延時,讀取NAND的狀態,讀取R/B (Ready/Busy)管腳的輸入信號,R/B管腳輸入是最快捷的方式,一般的設計中會將R/B管腳連接到GPIO,NAND驅動通過讀取GPIO輸入獲得R/B狀態,無R/B輸入就需要通過延時和讀取NAND的狀態寄存器來判斷。不同的NAND晶元和不同的操作其耗時是不同的(如SamsungK9F56xxQ0B 的寫操作時間為200uum~500um,擦除操作時間為2ms~3ms),需要根據設備Data sheet來沒定,不正確的設置會導致錯誤。
(3)連接方式問題
    NAND到系統的連接有軟體連接方式和硬體連接方式:軟體方式是在滿足時序要求的條件下直接將NAND快閃記憶體的IO管腳、控制管腳和系統的數據、地址和讀寫信號線相連,因此這種連接方式對NAND的命令、地址的輸入和數據的輸入輸出類似對不同地址的幾個寄存器的訪問;另一種方式是通過NAND 控制器和系統相連,NAND晶元連接到NAND控制器上,由NAND控制器來滿足NAND的時序,控制NAND,並實現硬體ECC等功能,命令、地址輸出、數據輸入輸出和控制是對NAND控制器的相應寄存器的操作。軟體方式硬體介面簡單,但需要較多的軟體操作來實現功能,硬體方式需要NAND控制器,但是可以通過NAND控制器完成更多的功能,應該根據成本和功能要求進行設計,根據實際連接方式進行NAND操作。

3.2 從NAND快閃記憶體啟動
    作為嵌入式系統的存儲設備,從NAND快閃記憶體啟動的要求是很自然的。由於NAND快閃記憶體採用IO介面,無SRAM介面(NOR 快閃記憶體是RAM介面),不能通過Bus訪問NAND,住NAND驅動載入之前無法訪問NAND,因此無法直接從NAND上啟動系統,需要其它的輔助機制。
    有如下的啟動機制可供需要從NAND啟動的系統使用:
(1)Boot loader方式:通過系統上的ROM 或者NOR 快閃記憶體,存儲一段boot代碼,這段代碼可以是boot loader的一部分,也可以是全部boot loader,由於ROM和NOR 快閃記憶體是可以直接啟動的,系統啟動后載入這部分代碼,執行這段代碼從NAND讀入boot loader的剩餘部分和內核映像或者直接讀入內核映像,實現從NAND啟動。
(2)啟動機方式(Boot Engine Method):往系統加電后和產生CPUReset信號之前,通過附加的硬體電路產生符合NAND讀操作流程的操作時序(命令、地址和數據讀入信號),將NAND 快閃記憶體的第一塊數據自動載入到系統內存,然後再產生CPU的Reset信號,CPU執行這部分載入到內存中的代碼,由這段代碼從NAND 快閃記憶體讀入boot loader的其餘部分和內核映像。
    按照NAND規範的要求,NAND晶元廠商保證出廠時其NAND產品的第一塊是完好的,因此所有NAND快閃記憶體都可以作為啟動設備。一些NAND快閃記憶體支持加電時自動首頁讀入,當NAND加電時,NAND快閃記憶體自動把第一頁的內容放到內部緩存,這種情況下實現第二種啟動機制所要做的工作可以簡化為把NAND的讀信號使能,然後讀入第一頁。
    許多系統同時擁有NAND 快閃記憶體和NOR 快閃記憶體,因此從NOR啟動不需要額外的工作,第一種啟動方式是更好的選擇,但是從成本等考慮,第二種啟動方式更有吸引力。
    基於TI Omap161x的平台支持第二種啟動方式。許多開發平台也實現了類似的啟動機制,開發者應該根據自己的平台和需求選擇合適的啟動方式。

4 基於OMAP161x的實現和Linux內核中的問題
    以下部分通過本文的實現和遇到的問題來示例NAND驅動和子系統的設計,並指出內核中NAND部分所存在的一些問題。
4.1基於Omap161x H2的實現
    採用TI Omap161x H2開發板來實現基於NAND快閃記憶體的存儲系統,TI的Omap平台在通信類產品中有著很廣泛的應用,同時其比較複雜的設計使得這個平台下NAND存儲的設計更具典型性。Linux 2.4和2.6 內核中還沒有該平台下NAND的驅動支持,需要開發者來實現。
    Omap平台系統支持從NAND啟動。Omap161x H2所採用的NAND 晶元為Samsung K9F5608Q0B (8位)和K9F5616Q0B (16位),由於2.4和2.6.*內核的MTD版本不支持16位介面,因此本文採用8位介面。
    該NAND快閃記憶體具有如下特點:
    頁大小:512B,塊大小:16kB,OOB 大小:16B;該晶元不支持地址自動增加;採用軟體連接方式和Omap連接。
    為了實現該NAND快閃記憶體的驅動,需要完成以下工作:
    配置EMIFS寄存器:這是Omap161x和NAND晶元或者NAND控制器的直接控制介面,需要配置EMIFS與NAND對應的片選寄存器CS2和NAND管腳復用,片選寄存器CS2需要設置使系統I/O時序滿足NAND的時序要求,同時,需要使和NAND相連接的管腳支持NOR 快閃記憶體工作模式。對首次接觸Omap平台的開發者來說,開放源碼的Boot Loader是很好的參考,如U-Boot,大部分工作可參考U-Boot中的設置。
   實現NAND特定的硬體相關的函數:
   由於omap161x H2上NAND是以軟體方式連接的,地址比較特殊,需要更改已有的nand_command函數,實現設備特定的nand_command函數:Omap161x H2上的NAND快閃記憶體通過地址0x0A000000,0x0A000002,0x0A000004進行數據數輸入輸出、命令輸入和地址輸入,在內核看來這是3個8位的寄存器,需要在命令發送、地址發送和數據輸入輸出中使用這些地址。
    實現設備特定的nand_ready,nand_wait函數,通過超時和讀取NAND 的狀態查詢設備是否就緒。Omap161x上的NAND晶元的R/B管腳通過GPIO連接到Omap,可以通過GPIO獲得R/B信號輸入,但是設置GPIO比較複雜,尤其是2.4內核對它的支持很少,更簡單的方式是通過超時和讀取NAND的狀態來實現,這完全可以達到所要求的效果。
     本系統採用的文件系統為JFFS2和Cramfs,JFFS2能夠直接管理NAND的ECC和壞塊,為了在NAND快閃記憶體上運行Cramfs並使用ECC,實現了塊管理層BM (見2.2.1節),增加了與JFFS2類似的ECC存儲和驗證方式,Cramfs通過/dev/bm/* (*為序號) 掛接對應的MTD分區(通過BM 的proc介面可以查到對應關係)。

4.2 2.4和2.6內核中存在的問題
    實現過程中額外的工作是更正內核中的錯誤。
    2.4和2.6內核的MTD版本都假設NAND是支持地址自動增加的,而且沒有可供用戶選擇的地方,因此對於不支持地址自動增加的NAND設備,NAND通用驅動部分的nand_read_ecc和nand_read_oob函數無法正常工作,所產生的錯誤調試信息往往使開發者陷入誤區。作者更改了內核的這部分,通過每一頁的讀操作都發送讀命令和地址,解決了這個問題,這樣對兩種類型的NAND快閃記憶體都是正確的(儘管對地址自動增加的NAND而言有些多餘)。NAND通用驅動中的寫操作始終以1頁為1個操作單元,不存在這個問題。
    2.4和2.6內核中的MTD版本的NAND通用實現中有關NAND狀態和操作隊列的實現也存在錯誤,會導致操作中斷和錯誤的重入。在nand_get_chip函數中有如下的代碼:

if(this->state==FL_ERASING){
if(new_state!=FL_ERASING){
this->state=new_state;
spin_unlock_bh(&this->chip_lock);
nand_select(); /*select in any case*/
this->cmdfunc(mtd_AND_CMD_RESET,-1,-1);
return;
}
}
    這種實現方式會打斷NAND操作,導致末完成擦除操作時重入,從而破壞操作隊列和後續操作,引入錯誤 作者更改了nand_get_chip,刪除這部分代碼以避免錯誤的重入。
    其它細節問題需要通過調試逐漸優化。充分利用MTD和文件系統(如JFFS2)輸出的調試信息是發現錯誤並使設備驅動等正常工作的最要途徑。

5 結語
    全文結合我們的實現介紹了嵌入式Linux中NAND存儲系統的結構和實現方式,同時就NAND特定的一些問題給出了表述和解決方法,就所要關注的問題如NAND壞塊管理、從NAND快閃記憶體啟動、以及當前2.4和2.6內核中的MTD版本的缺陷進行了闡述,給出了實現,並通過實例說明了NAND存儲系統設計中的具體問題,對設計和開發具有參考價值。


[admin via 研發互助社區 ] 嵌入式Linux下NAND 存儲系統的設計與實現已經有2742次圍觀

http://cocdig.com/docs/show-post-42146.html