http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html
簡單說就是,常見的Nand Flash,內部只有一個chip,每個chip只有一個plane。
而有些復雜的,容量更大的Nand Flash,內部有多個chip,每個chip有多個plane。這類的Nand Flash,往往也有更加高級的功能,
比如下面要介紹的Multi Plane Program和Interleave Page Program等。
概念上,由大到小來說,就是:
Nand Flash ⇒ Chip ⇒ Plane ⇒ Block ⇒ Page ⇒ oob
比如,型號為K9K8G08U0A這塊Nand Flash(有時候也被稱為此塊chip芯片),
其內部有兩個K9F4G08U0A的chip,chip#1和chip#2,
每個K9F4G08U0A的chip包含了2個Plane,每個Plane是2Gbbit,
所以K9F4G08U0A的大小是2Gb×2 = 4Gb = 512MB,
因此,K9K8G08U0A內部有2個K9F4G08U0A,或者說4個Plane,總大小是×256MB=1GB。
公式 1.1. K9K8G08U0A的物理結構所組成的總容量 K9K8G08U0A(這塊Nand Flash) = 2 × K9F4G08U0A(K9F4G08U0A是chip,1 K9F4G08U0A = 2 Plane) = 2 × 2個Plane = 4 Plane(1 Plane = 2048 Block) = 4 × 2048個Block(1 Block = 64 Page) = 4 × 2048 × 64Page(1 Page = 2KB) = 4 × 2048 × 64Page × 2KB = 4 × 2048 × 128KB(1 Block = 128KB) = 4 × 256MB(1 Plane = 2Gb = 256MB) = 2 × 512MB(1 K9F4G08U0A = 4Gb = 512MB) = 1GB(1 K9K8G08U0A = 1GB)
而型號是K9WAG08U1A的Nand Flash,內部包含了2個K9K8G08U0A,所以,總容量是K9K8G08U0A的兩倍=1GB×2=2GB,
類似地K9NBG08U5A,內部包含了4個K9K8G08U0A,總大小就是4×1GB=4GB。
Nand Flash物理存儲單元的陣列組織結構
1.2.5.1. Block塊
一個Nand Flash(的chip,芯片)由很多個塊(Block)組成,塊的大小一般是128KB,256KB,512KB,
此處是128KB。其他的小於128KB的,比如64KB,一般都是下面將要介紹到的small block的Nand Flash。
塊Block,是Nand Flash的擦除操作的基本/最小單位。
1.2.5.2. Page頁
每個塊里面又包含了很多頁(page)。每個頁的大小,對於現在常見的Nand Flash多數是2KB,
最新的Nand Flash的是4KB、8KB等,這類的頁大小大於2KB的Nand Flash,
被稱作big block的Nand Flash,對應的發讀寫命令地址,一共5個周期(cycle),
而老的Nand Flash,頁大小是256B,512B,這類的Nand Flash被稱作small block,地址周期只有4個。
頁Page,是讀寫操作的基本單位。
不過,也有例外的是,有些Nand Flash支持subpage(1/2頁或1/4頁)子頁的讀寫操作,不過一般很少見。
1.2.5.3. oob / Redundant Area / Spare Area
每一個頁,對應還有一塊區域,叫做空閑區域(spare area)/冗余區域(redundant area),
而Linux系統中,一般叫做OOB(Out Of Band),這個區域,是最初基於Nand Flash的硬件特性:
數據在讀寫時候相對容易錯誤,所以為了保證數據的正確性,必須要有對應的檢測和糾錯機制,
此機制被叫做EDC(Error Detection Code)/ECC(Error Code Correction, 或者
Error Checking and Correcting),所以設計了多余的區域,用於放置數據的校驗值。
Oob的讀寫操作,一般是隨着頁的操作一起完成的,即讀寫頁的時候,對應地就讀寫了oob。
關於oob具體用途,總結起來有:
- 標記是否是壞快
- 存儲ECC數據
- 存儲一些和文件系統相關的數據。如jffs2就會用到這些空間存儲一些特定信息,
而yaffs2文件系統,會在oob中,存放很多和自己文件系統相關的信息。
1.2.8. Nand Flash的位反轉特性
Nand Flash的位反轉,也叫做位翻轉,對應的英文表達有:Bit Flip=Bit Flipping=Bit-Flip=Bit twiddling。
Nand Flash由於本身硬件的內在特性,會導致(極其)偶爾的出現位反轉的現象。
所謂的位反轉,bit flip,指的是原先Nand Flash中的某個位,變化了,即要么從1變成0了,要么從0變成1了。
1.2.8.1. Nand Flash位反轉的原因
Nand Flash的位反轉現象,主要是由以下一些原因/效應所導致:
- 漂移效應(Drifting Effects)
漂移效應指的是,Nand Flash中cell的電壓值,慢慢地變了,變的和原始值不一樣了。
- 編程干擾所產生的錯誤(Program-Disturb Errors)
此現象有時候也叫做,過度編程效應(over-program effect)。
對於某個頁面的編程操作,即寫操作,引起非相關的其他的頁面的某個位跳變了。
- 讀操作干擾產生的錯誤(Read-Disturb Errors)
此效應是,對一個頁進行數據讀取操作,卻使得對應的某個位的數據,產生了永久性的變化,即Nand Flash上的該位的值變了。
1.2.8.2. Nand Flash位反轉的影響
位反轉,說白了,就是讀取數據的時候,數據出錯了。
因此,如果你讀取的數據正好是屬於某個重要的文件中的數據,比如系統的配置文件等,那么此時錯了一位,都會導致系統出現異常,問題相對會很嚴重。
而如果此數據屬於音視頻流中的數據,那么此時即使錯了一位,對整個音視頻的播放產生的影響也很小,所以問題也不大。
1.2.8.3. Nand Flash位反轉的類型和解決辦法
對應的位反轉的類型,有兩種:
- 一種是nand flash物理上的數據存儲的單元上的數據,是正確的,
只是在讀取此數據出來的數據中的某位,發生變化,出現了位反轉,
即讀取出來的數據中,某位錯了,本來是0變成1,或者本來是1變成0了。
此處可以成為軟件上位反轉。此數據位的錯誤,當然可以通過一定的校驗算法檢測並糾正。 - 另外一種,就是nand flash中的物理存儲單元中,對應的某個位,物理上發生了變化,
原來是1的,變成了0,或原來是0的,變成了1,發生了物理上的位的數據變化。
此處可以成為硬件上的位反轉。此錯誤,由於是物理上發生的,
雖然讀取出來的數據的錯誤,可以通過軟件或硬件去檢測並糾正過來,
但是物理上真正發生的位的變化,則沒辦法改變了。
不過個人理解,好像也是可以通過擦除Erase整個數據塊Block的方式去擦除此錯誤,
不過在之后的Nand Flash的使用過程中,估計此位還是很可能繼續發生同樣的硬件的位反轉的錯誤。
以上兩種類型的位反轉,其實對於從Nand Flash讀取出來的數據來說,
解決其中的錯誤的位的方法,都是一樣的,即通過一定的校驗算法,常稱為ECC,去檢測出來,或檢測並糾正錯誤。
如果只是單獨檢測錯誤,那么如果發現數據有誤,那么再重新讀取一次即可。
實際中更多的做法是,ECC校驗發現有錯誤,會有對應的算法去找出哪位錯誤並且糾正過來。
其中對錯誤的檢測和糾正,具體的實現方式,有軟件算法,也有硬件實現,
即硬件Nand Flash的控制器controller本身包含對應的硬件模塊以實現數據的校驗和糾錯的。
表 1.4. Nand Flash引腳功能的中文說明
引腳名稱 | 引腳功能 |
---|---|
I/O0 ~ I/O7 | 用於輸入地址/數據/命令,輸出數據 |
CLE | Command Latch Enable,命令鎖存使能,在輸入命令之前,要先在模式寄存器中,設置CLE使能 |
ALE | Address Latch Enable,地址鎖存使能,在輸入地址之前,要先在模式寄存器中,設置ALE使能 |
CE# | Chip Enable,芯片使能,在操作Nand Flash之前,要先選中此芯片,才能操作 |
RE# | Read Enable,讀使能,在讀取數據之前,要先使CE#有效。 |
WE# | Write Enable,寫使能, 在寫取數據之前,要先使WE#有效 |
WP# | Write Protect,寫保護 |
R/B# | Ready/Busy Output,就緒/忙,主要用於在發送完編程/擦除命令后,檢測這些操作是否完成,忙,表示編程/擦除操作仍在進行中,就緒表示操作完成 |
Vcc | Power,電源 |
Vss | Ground,接地 |
N.C | Non-Connection,未定義,未連接 |
數據手冊中的#表示低電平
在數據手冊中,你常會看到,對於一個引腳定義,有些字母上面帶一橫杠的,那是說明此引腳/信號是低電平有效,
比如你上面看到的RE頭上有個橫線,就是說明,此RE是低電平有效,此外,為了書寫方便,在字母后面加“#”,也是表示低電平有效,
比如我上面寫的CE#;如果字母頭上啥都沒有,就是默認的高電平有效,比如上面的CLE,就是高電平有效。
1.2.10. Nand Flash的一些典型(typical)的特性
- 頁擦除時間是200us,有些慢的有800us
- 塊擦除時間是1.5ms
- 頁數據讀取到數據寄存器的時間一般是20us
- 串行訪問(Serial access)讀取一個數據的時間是25ns,而一些舊的Nand Flash是30ns,甚至是50ns
- 輸入輸出端口是地址和數據以及命令一起multiplex復用的
- Nand Flash的編程/擦除的壽命:即,最多允許的擦除的次數
以前老的Nand Flash,編程/擦除時間比較短,比如K9G8G08U0M,才5K次,而后來的多數也只有10K=1萬次,
而現在很多新的Nand Flash,技術提高了,比如,Micron的MT29F1GxxABB,Numonyx的 NAND04G-B2D/NAND08G-BxC,
都可以達到100K,也就是10萬次的編程/擦除,達到和接近於之前常見的Nor Flash,幾乎是同樣的使用壽命了。
1.2.14. Nand Flash中頁的訪問順序
在一個塊內,對每一個頁進行編程的話,必須是順序的,而不能是隨機的。
比如,一個塊中有128個頁,那么你只能先對page0編程,再對page1編程,。。。。,
而不能隨機的,比如先對page3,再page1,page2,page0,page4,。。。
關於此處對於只能順序給頁編程的說法,只是翻譯自datasheet,但是實際情況卻發現是,程序中沒有按照此邏輯處理,
可以任意對某Block內的Page去做Program的動作,而不必是順序的。
但是datasheet為何如此解釋,原因未知,有待知情者給解釋一下。
1.2.15. 常見的Nand Flash的操作
圖 1.6. Nand Flash K9K8G08U0A的命令集合
從上圖可以看到,如果要實現讀一個頁的數據,就要發送Read的命令,而且是分兩個周期(Cycle),
即分兩次發送對應的命令,第一次是0x00h,第二次是0x30h,而兩次命令中間,
需要發送對應的你所要讀取的頁的地址,關於此部分詳細內容,留待后表。
對應地,其他常見的一些操作,比如寫一個頁的數據(Page Program),
就是先發送0x80h,然后發生要寫入的地址,再發送0x10h。
對於不同廠家的不同型號的Nand Flash 的基本操作,
即讀頁數據Read Page,寫頁數據(對頁進行編程)Page Program,擦除整個塊的數據Erase Block等操作,
所用的命令都是一樣的,但是針對一些Nand Flash的高級的一些特性,比如
交錯頁編程(Interleave Page Program),多片同時編程(Simultaneously Program Multi Plane)等,
所用的命令,未必一樣,不過對於同一廠家的Nand Flash的芯片,那一般來說,都是統一的。
1.2.15.1. 頁編程(Page Program)注意事項
Nand flash的寫操作叫做編程Program,編程,一般情況下,是以頁為單位的。
有的Nand Flash,比如K9K8G08U0A,支持部分頁編程(Partial Page Program),
但是有一些限制:在同一個頁內的,連續的部分頁的編程,不能超過4次。
一般情況下,都是以頁為單位進行編程操作的,很少使用到部分頁編程。
關於這個部分頁編程,本來是一個頁的寫操作,卻用兩個命令或更多的命令去實現,
看起來是操作多余,效率不高,但是實際上,有其特殊考慮:
至少對於塊擦除來說,開始的命令0x60是擦除設置命令(erase setup comman),然后傳入要擦除的塊地址,
然后再傳入擦除確認命令(erase confirm command)0xD0,以開始擦除的操作。
這種完成單個操作要分兩步發送命令的設計,即先開始設置,再最后確認的命令方式,
是為了避免由於外部由於無意的/未預料而產生的噪音,比如,由於某種噪音,而產生了0x60命令,
此時,即使被Nand Flash誤認為是擦除操作,但是沒有之后的確認操作0xD0,
Nand Flash就不會去擦除數據,這樣使得數據更安全,不會由於噪音而誤操作。
1.2.15.2. 讀(Read)操作過程詳解
解釋時序圖之前,讓我們先要搞清楚,我們要做的事情:從Nand Flash的某個頁Page里面,讀取我們要的數據。
要實現此功能,會涉及到幾部分的知識,即使我們不太懂Nand Flash的細節,
但是通過前面的基本知識的介紹,那么以我們的常識,至少很容易想到的就是,
需要用到哪些命令,怎么發這些命令,怎么計算所需要的地址,怎么讀取我們要的數據等等。
下面就一步步的解釋,需要做什么,以及如何去做:
1.2.15.2.1. 需要使用何種命令
我們知道,要讀取數據,要用到Read命令,該命令需要2個周期,第一個周期發0x00,第二個周期發0x30。
1.2.15.2.2. 發送命令前的准備工作以及時序圖各個信號的具體含義
以防有些讀者和我以前一樣,在聽這類詞語的時候,屬於初次接觸,或者接觸不多的,就很容易被搞得一頭霧水的
(雖然該詞匯對於某些專業人士說是屬於最基本的詞匯了,囧)。
使能(Enable),是指使其(某個信號)有效,使其生效的意思,“使其”“能夠”怎么怎么樣。。。。
比如,上面圖中的CLE線號,是高電平有效,如果此時將其設為高電平,我們就叫做,將CLE使能,也就是使其生效的意思。

黃色豎線所處的時刻,是在發送讀操作的第一個周期的命令0x00之前的那一刻。
讓我們看看,在那一刻,其所穿過好幾行都對應什么值,以及進一步理解,為何要那個值。
- 黃色豎線穿過的第一行,是CLE。還記得前面介紹命令所存使能(CLE)那個引腳吧?
CLE,將CLE置1,就說明你將要通過I/O復用端口發送進入Nand Flash的,是命令,
而不是地址或者其他類型的數據。只有這樣將CLE置1,使其有效,才能去通知了內部硬件邏輯,
你接下來將收到的是命令,內部硬件邏輯,才會將受到的命令,放到命令寄存器中,
才能實現后面正確的操作,否則,不去將CLE置1使其有效,硬件會無所適從,不知道你傳入的到底是數據還是命令了。 - 而第二行,是CE#,那一刻的值是0。這個道理很簡單,你既然要向Nand Flash發命令,
那么先要選中它,所以,要保證CE#為低電平,使其有效,也就是片選有效。 - 第三行是WE#,意思是寫使能。因為接下來是往Nand Flash里面寫命令,
所以,要使得WE#有效,所以設為低電平。 - 第四行,是ALE是低電平,而ALE是高電平有效,此時意思就是使其無效。
而對應地,前面介紹的,使CLE有效,因為將要數據的是命令(此時是發送圖示所示的讀命令第二周期的0x30),
而不是地址。如果在其他某些場合,比如接下來的要輸入地址的時候,就要使其有效,而使CLE無效了。 - 第五行,RE#,此時是高電平,無效。可以看到,知道后面低6階段,
才變成低電平,才有效,因為那時候,要發生讀取命令,去讀取數據。 - 第六行,就是我們重點要介紹的,復用的輸入輸出I/O端口了,
此刻,還沒有輸入數據,接下來,在不同的階段,會輸入或輸出不同的數據/地址。 - 第七行,R/B#,高電平,表示R(Ready)/就緒,因為到了后面的第5階段,硬件內部,
在第四階段,接受了外界的讀取命令后,把該頁的數據一點點送到頁寄存器中,
這段時間,屬於系統在忙着干活,屬於忙的階段,所以,R/B#才變成低,表示Busy忙的狀態的。
介紹了時刻①的各個信號的值,以及為何是這個值之后,相信,后面的各個時刻,
對應的不同信號的各個值,大家就會自己慢慢分析了,也就容易理解具體的操作順序和原理了。
1.2.15.2.3. 如何計算出我們要傳入的行地址和列地址
在介紹具體讀取數據的詳細流程之前,還要做一件事,那就是,先要搞懂我們要訪問的地址,
以及這些地址,如何分解后,一點點傳入進去,使得硬件能識別才行。
此處還是以K9K8G08U0A為例,此Nand Flash,一共有8192個塊,每個塊內有64頁,每個頁是2K+64 Bytes。
物理地址 =塊大小×塊號 + 頁大小×頁號 + 頁內地址
接下來,我們就看看,怎么才能把這個實際的物理地址,轉化為Nand Flash所要求的格式。
在解釋地址組成之前,先要來看看其datasheet中關於地址周期的介紹:
圖 1.8. Nand Flash的地址周期組成
此Nand Flash地址周期共有5個,2個列(Column)周期,3個行(Row)周期。
- 對應地,列地址A0~A10,就是頁內地址,地址范圍是從 0 到 2047。
細心的讀者可能注意到了,為何此處多出來個A11呢?
這樣從A0到A11,一共就是12位,可以表示的范圍就是0~212,即從 0 到 4096。
實際上,由於我們訪問頁內地址,可能會訪問到oob的位置,即2048-2111這64個字節的范圍內,
所以,此處實際上只用到了2048~2111,用於表示頁內的oob區域,其大小是64字節。 - 對應地,A12~A17,稱作頁號,頁的號碼,可以定位到對應的塊的哪一個頁 ( 0 .. 63 )。
- A18~A30,表示對應的塊號,即屬於哪個塊 ( 0..8191 )。
1.2.15.2.4. 讀操作過程的解釋
准備工作終於完了,下面就可以開始解釋說明,對於讀操作的,上面圖中標出來的,1-6個階段,具體是什么含義。
操作准備階段:此處是讀(Read)操作,所以,先發一個圖5中讀命令的第一個階段的0x00,表示,讓硬件先准備一下,接下來的操作是讀。
發送兩個周期的列地址。也就是頁內地址,表示,我要從一個頁的什么位置開始讀取數據。
接下來再傳入三個行地址。對應的也就是頁號。
然后再發一個讀操作的第二個周期的命令0x30。接下來,就是硬件內部自己的事情了。
Nand Flash內部硬件邏輯,負責去按照你的要求,根據傳入的地址,找到哪個塊中的哪個頁,然后把整個這一頁的數據,都一點點搬運到頁緩存中去。
而在此期間,你所能做的事,也就只需要去讀取狀態寄存器,看看對應的位的值,也就是R/B#那一位,是1還是0,
如果是0的話,就表示,系統是busy,仍在”忙“(着讀取數據),
如果是1,就說系統活干完了,忙清了,已經把整個頁的數據都搬運到頁緩存里去了,你可以接下來讀取你要的數據了。
對於這里。估計有人會問了,這一個頁一共2048+64字節,如果我傳入的頁內地址,就像上面給的1208一類的值,
只是想讀取1028到2011這部分數據,而不是頁開始的0地址整個頁的數據,那么內部硬件卻讀取整個頁的數據出來,豈不是很浪費嗎?
答案是,的確很浪費,效率看起來不高,但是實際就是這么做的,而且本身讀取整個頁的數據,相對時間並不長,而且讀出來之后,
內部數據指針會定位到你剛才所制定的1208的那個位置。