您好,歡迎進入深圳市穎特新科技有限公司官方網(wǎng)站!
Flash名稱的由來,F(xiàn)lash的擦除操作是以block塊為單位的,與此相對應的是其他很多存儲設備,是以bit位為最小讀取/寫入的單位,F(xiàn)lash是一次性地擦除整個塊:在發(fā)送一個擦除命令后,一次性地將一個block,常見的塊的大小是128KB/256KB,全部擦除為1,也就是里面的內(nèi)容全部都是0xFF了,由于是一下子就擦除了,相對來說,擦除用的時間很短,可以用一閃而過來形容,所以,叫做Flash Memory。所以一般將Flash翻譯為 (快速)閃存。
NAND Flash 在嵌入式系統(tǒng)中有著廣泛的應用,負載平均和壞塊管理是與之相關的兩個核心議題。Uboot 和 Linux 系統(tǒng)對 NAND 的操作都封裝了對這兩個問題的處理方法。 本文首先講述Nandflash基礎知識,然后介紹現(xiàn)有的幾類壞塊管理(BBM)方法,通過分析典型嵌入式系統(tǒng)的 NAND 存儲表,指出了輕量級管理方法的優(yōu)勢所在,分析了當前廣泛使用的輕量級管理方法,指出其缺陷所在并詳細說明了改進方法。
基礎知識
Flash的硬件實現(xiàn)機制
Flash的內(nèi)部存儲是MOSFET,里面有個懸浮門(Floating Gate),是真正存儲數(shù)據(jù)的單元。
在Flash之前,紫外線可擦除(uv-erasable)的EPROM,就已經(jīng)采用了Floating Gate存儲數(shù)據(jù)這一技術了。
典型的Flash內(nèi)存物理結(jié)構(gòu)
數(shù)據(jù)在Flash內(nèi)存單元中是以電荷(electrical charge) 形式存儲的。存儲電荷的多少,取決于圖中的外部門(external gate)所被施加的電壓,其控制了是向存儲單元中沖入電荷還是使其釋放電荷。而數(shù)據(jù)的表示,以所存儲的電荷的電壓是否超過一個特定的閾值Vth來表示,因此,F(xiàn)lash的存儲單元的默認值,不是0(其他常見的存儲設備,比如硬盤燈,默認值為0),而是1,而如果將電荷釋放掉,電壓降低到一定程度,表述數(shù)字0。
NandFlash的簡介
Nand flash成本相對低,說白了就是便宜,缺點是使用中數(shù)據(jù)讀寫容易出錯,所以一般都需要有對應的軟件或者硬件的數(shù)據(jù)校驗算法,統(tǒng)稱為ECC。但優(yōu)點是,相對來說容量比較大,現(xiàn)在常見的Nand Flash都是1GB,2GB,更大的8GB的都有了,相對來說,價格便宜,因此適合用來存儲大量的數(shù)據(jù)。其在嵌入式系統(tǒng)中的作用,相當于PC上的硬盤,用于存儲大量數(shù)據(jù)。
SLC和MLC
Nand Flash按照內(nèi)部存儲數(shù)據(jù)單元的電壓的不同層次,也就是單個內(nèi)存單元中,是存儲1位數(shù)據(jù),還是多位數(shù)據(jù),可以分為SLC和MLC。那么軟件如何識別系統(tǒng)上使用過的SLC還是MLC呢?
Nand Flash設計中,有個命令叫做Read ID,讀取ID,讀取好幾個字節(jié),一般最少是4個,新的芯片,支持5個甚至更多,從這些字節(jié)中,可以解析出很多相關的信息,比如此Nand Flash內(nèi)部是幾個芯片(chip)所組成的,每個chip包含了幾片(Plane),每一片中的頁大小,塊大小,等等。在這些信息中,其中有一個,就是識別此flash是SLC還是MLC。
oob / Redundant Area / Spare Area
每一個頁,對應還有一塊區(qū)域,叫做空閑區(qū)域(spare area)/冗余區(qū)域(redundant area),而Linux系統(tǒng)中,一般叫做OOB(Out Of Band),這個區(qū)域,是最初基于Nand Flash的硬件特性:數(shù)據(jù)在讀寫時候相對容易錯誤,所以為了保證數(shù)據(jù)的正確性,必須要有對應的檢測和糾錯機制,此機制被叫做EDC(Error Detection Code)/ECC(Error Code Correction, 或者 Error Checking and Correcting),所以設計了多余的區(qū)域,用于放置數(shù)據(jù)的校驗值。
Oob的讀寫操作,一般是隨著頁的操作一起完成的,即讀寫頁的時候,對應地就讀寫了oob。
關于oob具體用途,總結(jié)起來有:
Bad Block Management壞塊管理
Nand Flash由于其物理特性,只有有限的擦寫次數(shù),超過那個次數(shù),基本上就是壞了。在使用過程中,有些Nand Flash的block會出現(xiàn)被用壞了,當發(fā)現(xiàn)了,要及時將此block標注為壞塊,不再使用。于此相關的管理工作,屬于Nand Flash的壞塊管理的一部分工作。
Wear-Leveling負載平衡
Nand Flash的block管理,還包括負載平衡。
正是由于Nand Flash的block,都是有一定壽命限制的,所以如果你每次都往同一個block擦除然后寫入數(shù)據(jù),那么那個block就很容易被用壞了,所以我們要去管理一下,將這么多次的對同一個block的操作,平均分布到其他一些block上面,使得在block的使用上,相對較平均,這樣相對來說,可以更能充分利用Nand Flash。
ECC錯誤校驗碼
Nand Flash物理特性上使得其數(shù)據(jù)讀寫過程中會發(fā)生一定幾率的錯誤,所以要有個對應的錯誤檢測和糾正的機制,于是才有此ECC,用于數(shù)據(jù)錯誤的檢測與糾正。Nand Flash的ECC,常見的算法有海明碼和BCH,這類算法的實現(xiàn),可以是軟件也可以是硬件。不同系統(tǒng),根據(jù)自己的需求,采用對應的軟件或者是硬件。
相對來說,硬件實現(xiàn)這類ECC算法,肯定要比軟件速度要快,但是多加了對應的硬件部分,所以成本相對要高些。如果系統(tǒng)對于性能要求不是很高,那么可以采用軟件實現(xiàn)這類ECC算法,但是由于增加了數(shù)據(jù)讀取和寫入前后要做的數(shù)據(jù)錯誤檢測和糾錯,所以性能相對要降低一些,即Nand Flash的讀取和寫入速度相對會有所影響。
其中,Linux中的軟件實現(xiàn)ECC算法,即NAND_ECC_SOFT模式,就是用的對應的海明碼。
而對于目前常見的MLC的Nand Flash來說,由于容量比較大,動輒2GB,4GB,8GB等,常用BCH算法。BCH算法,相對來說,算法比較復雜。
筆者由于水平有限,目前仍未完全搞懂BCH算法的原理。
BCH算法,通常是由對應的Nand Flash的Controller中,包含對應的硬件BCH ECC模塊,實現(xiàn)了BCH算法,而作為軟件方面,需要在讀取數(shù)據(jù)后,寫入數(shù)據(jù)之前,分別操作對應BCH相關的寄存器,設置成BCH模式,然后讀取對應的BCH狀態(tài)寄存器,得知是否有錯誤,和生成的BCH校驗碼,用于寫入。
其具體代碼是如何操作這些寄存器的,由于是和具體的硬件,具體的nand flash的controller不同而不同,無法用同一的代碼。如果你是nand flash驅(qū)動開發(fā)者,自然會得到對應的起nand flash的controller部分的datasheet,按照手冊說明,去操作即可。
不過,額外說明一下的是,關于BCH算法,往往是要從專門的做軟件算法的廠家購買的,但是Micron之前在網(wǎng)上放出一個免費版本的BCH算法。
位反轉(zhuǎn)
Nand Flash的位反轉(zhuǎn)現(xiàn)象,主要是由以下一些原因/效應所導致:
漂移效應指的是,Nand Flash中cell的電壓值,慢慢地變了,變的和原始值不一樣了。
此現(xiàn)象有時候也叫做,過度編程效應(over-program effect)。
對于某個頁面的編程操作,即寫操作,引起非相關的其他的頁面的某個位跳變了。
此效應是,對一個頁進行數(shù)據(jù)讀取操作,卻使得對應的某個位的數(shù)據(jù),產(chǎn)生了永久性的變化,即Nand Flash上的該位的值變了。
以上兩種類型的位反轉(zhuǎn),其實對于從Nand Flash讀取出來的數(shù)據(jù)來說,解決其中的錯誤的位的方法,都是一樣的,即通過一定的校驗算法,常稱為ECC,去檢測出來,或檢測并糾正錯誤。
如果只是單獨檢測錯誤,那么如果發(fā)現(xiàn)數(shù)據(jù)有誤,那么再重新讀取一次即可。
實際中更多的做法是,ECC校驗發(fā)現(xiàn)有錯誤,會有對應的算法去找出哪位錯誤并且糾正過來。
其中對錯誤的檢測和糾正,具體的實現(xiàn)方式,有軟件算法,也有硬件實現(xiàn),即硬件Nand Flash的控制器controller本身包含對應的硬件模塊以實現(xiàn)數(shù)據(jù)的校驗和糾錯的。
我們寫驅(qū)動,是寫Nand Flash 控制器的驅(qū)動,而不是Nand Flash 芯片的驅(qū)動,因為獨立的Nand Flash芯片,一般來說,是很少直接拿來用的,多數(shù)都是硬件上有對應的硬件的Nand Flash的控制器,去操作和控制Nand Flash,包括提供時鐘信號,提供硬件ECC校驗等等功能,我們所寫的驅(qū)動軟件,是去操作Nand Flash的控制器
然后由控制器去操作Nand Flash芯片,實現(xiàn)我們所要的功能。
由于Nand Flash讀取和編程操作來說,一般最小單位是頁,所以Nand Flash在硬件設計時候,就考慮到這一特性,對于每一片(Plane),都有一個對應的區(qū)域?qū)iT用于存放,將要寫入到物理存儲單元中去的或者剛從存儲單元中讀取出來的,一頁的數(shù)據(jù),這個數(shù)據(jù)緩存區(qū),本質(zhì)上就是一個緩存buffer,但是只是此處datasheet里面把其叫做頁寄存器page register而已,實際將其理解為頁緩存,更貼切原意。
而正是因為有些人不了解此內(nèi)部結(jié)構(gòu),才容易產(chǎn)生之前遇到的某人的誤解,以為內(nèi)存里面的數(shù)據(jù),通過Nand Flash的FIFO,寫入到Nand Flash里面去,就以為立刻實現(xiàn)了實際數(shù)據(jù)寫入到物理存儲單元中了,而實際上只是寫到了這個頁緩存中,只有當你再發(fā)送了對應的編程第二階段的確認命令,即0x10,之后,實際的編程動作才開始,才開始把頁緩存中的數(shù)據(jù),一點點寫到物理存儲單元中去。
具體標記的地方是,對于現(xiàn)在常見的頁大小為2K的Nand Flash,是塊中第一個頁的oob起始位置的第1個字節(jié)(舊的小頁面,pagesize是512B甚至256B的Nand Flash,壞塊標記是第6個字節(jié)),如果不是0xFF,就說明是壞塊。相對應的是,所有正常的塊,好的塊,里面所有數(shù)據(jù)都是0xFF的。
對于壞塊的標記,本質(zhì)上,也只是對應的flash上的某些字節(jié)的數(shù)據(jù)是非0xFF而已,所以,只要是數(shù)據(jù),就是可以讀取和寫入的。也就意味著,可以寫入其他值,也就把這個壞塊標記信息破壞了。對于出廠時的壞塊,一般是不建議將標記好的信息擦除掉的。
uboot中有個命令是
nand scrub
就可以將塊中所有的內(nèi)容都擦除了,包括壞塊標記,不論是出廠時的,還是后來使用過程中出現(xiàn)而新標記的。
nand erase
只擦除好的塊,對于已經(jīng)標記壞塊的塊,不要輕易擦除掉,否則就很難區(qū)分哪些是出廠時就壞的,哪些是后來使用過程中用壞的了。
NAND 壞塊管理都是基于壞塊表(BBT)的,通過這張表來標識系統(tǒng)中的所有壞塊。所以,不同的管理方法之間的差異可以通過以下幾個問題來找到答案。
Uboot 是目前使用最為廣泛的 bootloader,它提供了兩種輕量級壞塊管理方法,可稱之為基本型和改進型。通過下表,我們可以看到兩者的差異。
雖然 uboot 的改進型壞塊管理方法的做了一些改進,但它仍然有三個主要的缺點。
針對現(xiàn)有管理方法的缺陷,本文提出了一種更加安全高效的管理方法,將從以下三個方面闡述其實現(xiàn)原理。
首先,使用一個統(tǒng)一的備用好塊池,為所有存放在 NAND 中的模塊提供可替換的好塊。這樣,就不需要在每個模塊后面放置一個保留區(qū),提高了 NAND 的空間利用率。
共用好塊池示意圖
為了實現(xiàn)共用好塊池,需要建立一個從壞塊到好塊的映射,所以,除了 BBT 之外,還需定義一個替換表(SBT)。這樣一來,當讀第 i 個塊的數(shù)據(jù)時,如果發(fā)現(xiàn) BBT 中記錄該塊為壞塊,就去 SBT 中查詢其替換塊;如果寫第 i 個塊出錯,需要在 BBT 中標記該塊為壞塊,同時從好塊池中獲取一個新的好塊,假設其序號為 j,然后將此好塊的序號 j 寫入 SBT 中的第 i 個字節(jié),而且 SBT 的第 j 個字節(jié)寫序號 i。SBT 中的這種雙向映射可確保數(shù)據(jù)的可靠性。此外,好塊池中的塊也有可能成為壞塊,如果掃描時發(fā)現(xiàn)是壞塊,則將 SBT 中的對應位置標記為 0x00,如果是在寫的過程中出錯,則除了在 SBT 對應位置標記 0x00 之外,還要更新雙向映射數(shù)據(jù)。
BBT/SBT 映射示意圖
傳統(tǒng)方法僅檢查 BBT 所在塊的簽名,將讀到的前幾個字節(jié)和一個特征字符串進行比較,如果一致,就認為當前塊的數(shù)據(jù)為 BBT,然后讀取接下來的 BBT 數(shù)據(jù),但并不對 BBT 的數(shù)據(jù)做校驗。如果 BBT 保存在 NAND 中,數(shù)據(jù)的有效性是可以得到驗證的,因為 NAND 控制器或驅(qū)動一般都會對數(shù)據(jù)做 ECC 校驗。但是,大多數(shù)控制器使用的 ECC 算法也僅僅能糾正一個 bit、發(fā)現(xiàn) 2 兩個 bit 的錯誤。如果 BBT 保存在其他的沒有 ECC 校驗機制的存儲體中,比如 NOR Flash,沒有對 BBT 的數(shù)據(jù)進行校驗顯然是不安全的。
為了更加可靠和靈活地驗證 BBT/SBT 數(shù)據(jù),定義下面這個結(jié)構(gòu)體來描述 BBM 信息。
BBM 頭信息
typedef struct {
UINT8 acSignature[4];/* BBM 簽名 */
UINT32 ulBBToffset;/* BBT 偏移 */
UINT32 ulSBToffset;/* SBT 偏移 */
UINT16 usBlockNum;/* BBM 管理的 block 數(shù)目 */
UINT16 usSBTstart;/* SBT 所在位置的起始 block 序號 */
UINT16 usSBtop;/* SBT top block */
UINT16 usSBnum;/* SBT number */
UINT32 ulBBTcrc;/* BBT 數(shù)據(jù) CRC 校驗碼 */
UINT32 ulSBTcrc;/* SBT 數(shù)據(jù) CRC 校驗碼 */
UINT32 ulHeadcrc;/* BBM 頭信息 CRC 校驗碼 */
} BBM_HEAD
BBT/SBT 的保存形式
使用三重 CRC 校驗機制,無論 BBT 保存在哪種存儲體中,都可以更加嚴格地驗證數(shù)據(jù)的有效性。
傳統(tǒng)的方法僅保存一份 BBT 數(shù)據(jù),如果在寫 BBT 時系統(tǒng)掉電,則 BBT 丟失,系統(tǒng)將可能無法正常啟動或工作。為安全起見,本文所述方法將同時保留三個備份,如果在寫某個備份時掉電,則還有兩個完好的備份。最壞的情況是,如果在寫第一個備份時掉電,則當前最新的一個壞塊信息丟失。
讀取壞塊表時,順序讀取三個備份,如果發(fā)現(xiàn)三個備份的數(shù)據(jù)不一致,用記錄的壞塊數(shù)最多的備份為當前的有效備份,同時立刻更新另外兩備份。
本文介紹了NandFlash基礎知識和幾類 NAND 壞塊管理方法,指出了 uboot 的輕量級管理方法的缺陷,提出了一種改進的方法,提高了 NAND 的利用率及壞塊管理的安全性,可對嵌入式開發(fā)起到有很好的借鑒作用。
ECC校驗是一種內(nèi)存糾錯原理,它是比較先進的內(nèi)存錯誤檢查和更正的手段。ECC內(nèi)存即糾錯內(nèi)存,簡單的說,其具有發(fā)現(xiàn)錯誤,糾正錯誤的功能,一般多應用在高檔臺式電腦/服務器及圖形工作站上,這將使整個電腦系統(tǒng)在工作時更趨于安全穩(wěn)定。
內(nèi)存是一種電子器件,在其工作過程中難免會出現(xiàn)錯誤,而對于穩(wěn)定性要求高的用戶來說,內(nèi)存錯誤可能會引起致命性的問題。內(nèi)存錯誤根據(jù)其原因還可分為硬錯誤和軟錯誤。硬件錯誤是由于硬件的損害或缺陷造成的,因此數(shù)據(jù)總是不正確,此類錯誤是無法糾正的;軟錯誤是隨機出現(xiàn)的,例如在內(nèi)存附近突然出現(xiàn)電子干擾等因素都可能造成內(nèi)存軟錯誤的發(fā)生。
為了能檢測和糾正內(nèi)存軟錯誤,在ECC技術出現(xiàn)之前,首先出現(xiàn)的是內(nèi)存“奇偶校驗(Parity)”。內(nèi)存中最小的單位是比特,也稱為“位(bit)”,位有只有兩種狀態(tài)分別以1和0來標示,每8個連續(xù)的比特叫做一個字節(jié)(byte)。不帶奇偶校驗的內(nèi)存每個字節(jié)只有8位,如果其某一位存儲了錯誤的值,就會導致其存儲的相應數(shù)據(jù)發(fā)生變化,進而導致應用程序發(fā)生錯誤。而奇偶校驗就是在每一字節(jié)(8位)之外又增加了一位作為錯誤檢測位。在某字節(jié)中存儲數(shù)據(jù)之后,在其8個位上存儲的數(shù)據(jù)是固定的,因為位只能有兩種狀態(tài)1或0,假設存儲的數(shù)據(jù)用位標示為1、1、1、0、0、1、0、1,那么把每個位相加(1+1+1+0+0+1+0+1=5),結(jié)果是奇數(shù)。對于偶校驗,校驗位就定義為1,反之則為0;對于奇校驗,則相反。當CPU讀取存儲的數(shù)據(jù)時,它會再次把前8位中存儲的數(shù)據(jù)相加,計算結(jié)果是否與校驗位相一致。從而一定程度上能檢測出內(nèi)存錯誤,奇偶校驗只能檢測出錯誤而無法對其進行修正,同時雖然雙位同時發(fā)生錯誤的概率相當?shù),奇偶校驗卻無法檢測出雙位錯誤。
通過上面的分析我們知道Parity內(nèi)存是通過在原來數(shù)據(jù)位的基礎上增加一個數(shù)據(jù)位來檢查當前8位數(shù)據(jù)的正確性,但隨著數(shù)據(jù)位的增加Parity用來檢驗的數(shù)據(jù)位也成倍增加,就是說當數(shù)據(jù)位為16位時它需要增加2位用于檢查,當數(shù)據(jù)位為32位時則需增加4位,依此類推。特別是當數(shù)據(jù)量非常大時,數(shù)據(jù)出錯的幾率也就越大,對于只能糾正簡單錯誤的奇偶檢驗的方法就顯得力不從心了,正是基于這樣一種情況,一種新的內(nèi)存技術應允而生了,這就是ECC(錯誤檢查和糾正),這種技術也是在原來的數(shù)據(jù)位上外加校驗位來實現(xiàn)的。不同的是兩者增加的方法不一樣,這也就導致了兩者的主要功能不太一樣。它與Parity不同的是如果數(shù)據(jù)位是8位,則需要增加5位來進行ECC錯誤檢查和糾正,數(shù)據(jù)位每增加一倍,ECC只增加一位檢驗位,也就是說當數(shù)據(jù)位為16位時ECC位為6位,32位時ECC位為7位,數(shù)據(jù)位為64位時ECC位為8位,依此類推,數(shù)據(jù)位每增加一倍,ECC位只增加一位?傊,在內(nèi)存中ECC能夠容許錯誤,并可以將錯誤更正,使系統(tǒng)得以持續(xù)正常的操作,不致因錯誤而中斷,且ECC具有自動更正的能力,可以將Parity無法檢查出來的錯誤位查出并將錯誤修正。
ECC(Error Checking and Correcting,錯誤檢查和糾正)內(nèi)存,它同樣也是在數(shù)據(jù)位上額外的位存儲一個用數(shù)據(jù)加密的代碼。當數(shù)據(jù)被寫入內(nèi)存,相應的ECC代碼與此同時也被保存下來。當重新讀回剛才存儲的數(shù)據(jù)時,保存下來的ECC代碼就會和讀數(shù)據(jù)時產(chǎn)生的ECC代碼做比較。如果兩個代碼不相同,他們則會被解碼,以確定數(shù)據(jù)中的哪一位是不正確的。然后這一錯誤位會被拋棄,內(nèi)存控制器則會釋放出正確的數(shù)據(jù)。被糾正的數(shù)據(jù)很少會被放回內(nèi)存。假如相同的錯誤數(shù)據(jù)再次被讀出,則糾正過程再次被執(zhí)行。重寫數(shù)據(jù)會增加處理過程的開銷,這樣則會導致系統(tǒng)性能的明顯降低。如果是隨機事件而非內(nèi)存的缺點產(chǎn)生的錯誤,則這一內(nèi)存地址的錯誤數(shù)據(jù)會被再次寫入的其他數(shù)據(jù)所取代。