from: http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html
版本:v2.2
摘要
本文先解釋了nand flash相關的一些名詞,再從flash硬體機制開始,介紹到nand flash的常見的實體特性,且深入介紹了nand
flash的一些進階功能,然後開始介紹linux下面和nand
flash相關的軟體架構mtd的相關知識,最後介紹了在linux的mtd驅動架構下,如何實作nand flash的驅動。

本文提供多種格式供:
<col>
線上閱讀
下載下傳(7zip壓縮包)
html版本的線上位址為:
有任何意見,建議,送出bug等,都歡迎去讨論組發帖讨論:
2013-04-07
修訂曆史
修訂 1.0
2009-07-21
crl
簡介如何在linux下實作nand flash驅動
修訂 1.2
2011-03-15
整理了排版
添加了很多内容
修訂 1.3
2011-06-12
修正了nand flash行列位址的計算方法
修訂 1.7
2011-07-02
添加了onfi,lba規範的介紹
添加了unique id介紹
添加了對應的mtd中檢測不同類型晶片的代碼
增加了關于nand flash的軟體和硬體的ecc算法的簡介
修訂 1.8
2011-10-04
添加了nand flash位翻轉的詳細介紹
添加了nand flash的結構圖
修訂 1.9
2012-06-14
通過docbook釋出
修訂 2.2
修正關于nand flash實體架構組成總容量方面的解釋
修正其他一些筆誤
添加了和nand flash相關的一些資料,比如id的命名規則
添加了keyword,第二章所有章節的id
修正了筆誤,把"第7000個塊中的第25頁中的1208位元組處"改為"第7000個塊中的第64頁中的1208位元組處",以及同步修正了相關的數字
版權 ? 2012 crifan,
本文章遵從:
目錄
<dl></dl>
<dt></dt>
<dd></dd>
插圖清單
<dt>1.1. </dt>
<dt>1.2. </dt>
<dt>1.3. </dt>
<dt>1.4. </dt>
<dt>1.5. </dt>
<dt>1.6. </dt>
<dt>1.7. </dt>
<dt>1.8. </dt>
<dt>1.9. </dt>
<dt>1.10. </dt>
<dt>1.11. </dt>
<dt>1.12. </dt>
<dt>1.13. </dt>
<dt>1.14. </dt>
表格清單
公式清單
常用縮略詞如下:
bbm (bbm)
bad block management
壞塊管理
參見.
bbt (bbt)
bad block table
壞塊表
ecc (ecc)
error correction code
錯誤校驗碼
e2prom
見.
eeprom (eeprom)
electrically erasable programmable read-only memory
電可擦隻讀存儲器
mlc (mlc)
multi level cell
多層單元
mosfet (mosfet)
metal-oxide–semiconductor field effect transistor
金屬氧化物半導體場效應半導體
mtd (mtd)
memory technology device
記憶體技術裝置
nvm (nvm)
non-volatile memory
nda (nda)
none-disclosure agreement
非公開協定,保密協定
otp (otp)
one time programmable
一次性可程式設計(存儲器)
slc (slc)
single level cell
單層單元,單層式存儲
本文的主要目的是,看了之後,你應該對nand flash的硬體特性以及對應的linux下軟體平台有了基本的認識,進一步地,對如何實作linux下的nand
flash的驅動,知道要做哪些事情了,以及大概是如何實作的。這樣,如果有了對應的開發環境,你就可以自己去實作nand flash的驅動了。
不過額外提示一句的是,寫出代碼,并不代表你就完全搞懂了整個系統的流程。而且已經寫好的代碼,很可能有bug,要你不斷地調試,通過調試,你才會對整個系統以及nand
flash的方方面面有個更深入的了解的。
而且,你會發現,為了寫驅動那點代碼之前,卻要弄懂太多的東西,包括硬體的工作原理,軟體的協定規範,軟體的邏輯架構等等,最後才能去實作你的驅動,是以有人會說,你寫驅動不是很簡單嘛,不就是寫那幾行代碼嗎,對此,一個經典的回答就是,對于整個寫驅動的工作的價值算作100元的話,寫代碼值1塊錢,但是知道怎麼寫,值99塊錢。^_^
正因為此文目的是讓你搞懂如何在linux下面實作nand flash的驅動,是以,目标讀者就是,希望對nand
flash硬體知識有一定了解,和想要在linux下面實作nand flash驅動的讀者。
而閱讀此文的前提,是要有一些基本的軟硬體基礎知識,和了解如何在v2.6核心之後linux的下面開發驅動的流程。有了這些知識,再看本文,然後你才能清楚真正要去實作nand
flash的驅動,是如何下手。
本文的邏輯是,先介紹nand flash的一些基本的硬體知識,然後詳細分析nand
flash的read操作的具體的流程,清楚硬體實作的邏輯,接着介紹軟體平台,即linux下面和nand
flash相關的内容,這樣,硬體和軟體都清楚是怎麼回事了,然後再介紹如何去在linux的架構下,實作nand flash驅動。
之前寫的版本,雖然前面關于nand
flash的内容介紹的比較詳細,但是後面關于相關的mtd知識,尤其是linux的mtd的架構和如何實作具體的nand的flash的操作等部分的内容,寫的很簡略,導緻有些讀者看了後,覺得是,關于如何寫驅動,和沒說差不多,呵呵。
是以,現在繼續更新,将更詳細的解釋,如何從硬體nand
flash的規範,一步步映射到具體的軟體實作的過程,這樣,使得讀者更明白其中的内在邏輯,然後接着再介紹如何在了解了軟硬體各自的所具有的功能,以及linux的mtd系統,已經幫你實作了哪些功能,然後才會更加明白,餘下的要實作的軟體部分,就是你所要實作的linux下的nand
flash的驅動部分了。
關于此貼版權問題,歡迎轉載,但是希望注明聯系方式,至少其他人看到被轉帖的内容,如果有疑問,建議和意見,可以及時與筆者溝通:admin (at)
crifan.com。
,即nv (ram)memory,斷電資料也不會丢失的存儲器,比如nand flash,nor
flash,硬碟等等。于此相對的是,斷電了資料會丢失的存儲器,比如dram等。
otp,一種非易失性存儲器,但是隻允許一次性寫入資料,寫入(或稱燒寫)資料之後,就不能修改了。
otp的好處或者說用途是,常用于寫入一些和晶片相關的一些特定資料,用于加密的一些資料等。
與一次性寫入資料的otp相對應的是,像nand
flash,硬碟等存儲器,可以被多次寫入資料。隻要硬碟這類的存儲器沒壞,你高興寫入幾次就寫入幾次,而otp就隻能寫入一次,就沒法再修改裡面的資料了。
nda,中文可以翻譯為,非公開協定,保密協定。
說白了,還是一種協定,常用于這種情況:
某家廠商的某種技術或資料,是保密的,不希望公開的。
但是呢,如果你要用他家的晶片啊之類的東西,在開發過程中,又必須得到對應的技術和資料,才能開發産品,是以,他就會要求和你簽訂這樣的nda協定,意思就是,你可以用我的技術和資料,但是你不能公開給(我未授權的)其他人。如果非法洩露我的機密技術,那我肯定要走法律程式控告你,之類的。
英文datasheet,中文一般翻譯為資料手冊。
指的是對應某個硬體,多為晶片,的功能說明,定義了如何操作該硬體,達到你要的功能,這其中主要包括晶片中的相關寄存器的定義,如何發送指令,發送什麼指令,以此來操作此硬體等等。
而英文specification,引文常縮寫為spec.,中文一般翻譯為規範。
多指某個組織(盈利的或非盈利的),定義了一些規矩,如果你要用某種東西,在計算機領域,常常指的是某硬體和相關的軟體協定,就要按照此規矩來操作,人家這個組織呢,保證你隻要實作了此規範,裝置就能按照你所期望的運作,能夠實作對應的功能,而你的晶片實作了此規範,就叫做,是和此規範相容(compatible)的。
nand flash由于其實體特性,隻有有限的擦寫次數,超過那個次數,基本上就是壞了。在使用過程中,有些nand
flash的block會出現被用壞了,當發現了,要及時将此block标注為壞塊,不再使用。
于此相關的管理工作,屬于nand flash的壞塊管理的一部分工作。
nand flash的block的管理,還包括負載平衡。
正是由于nand
flash的block,都是有一定壽命限制的,是以如果你每次都往同一個block擦除然後寫入資料,那麼那個block就很容易被用壞了,是以我們要去管理一下,将這麼多次的對同一個block的操作,平均分布到其他一些block上面,使得在block的使用上,相對較平均,這樣相對來說,可以更能充分利用nand
flash。
關于wear-leveling這個詞,再簡單解釋一下,wear就是穿(衣服)等,用(東西)導緻磨損,而leveling就是使得均衡,是以放在一起就是,使得對于nand
flash的那麼多的block的使用磨損,相對均衡一些,以此延長nand flash的使用壽命或者說更加充分利用nand flash。
nand
flash實體特性上使得其資料讀寫過程中會發生一定幾率的錯誤,是以要有個對應的錯誤檢測和糾正的機制,于是才有此ecc,用于資料錯誤的檢測與糾正。nand
flash的ecc,常見的算法有海明碼和bch,這類算法的實作,可以是軟體也可以是硬體。不同系統,根據自己的需求,采用對應的軟體或者是硬體。
相對來說,硬體實作這類ecc算法,肯定要比軟體速度要快,但是多加了對應的硬體部分,是以成本相對要高些。如果系統對于性能要求不是很高,那麼可以采用軟體實作這類ecc算法,但是由于增加了資料讀取和寫入前後要做的資料錯誤檢測和糾錯,是以性能相對要降低一些,即nand
flash的讀取和寫入速度相對會有所影響。
其中,linux中的軟體實作ecc算法,即nand_ecc_soft模式,就是用的對應的海明碼。
而對于目前常見的mlc的nand
flash來說,由于容量比較大,動辄2gb,4gb,8gb等,常用bch算法。bch算法,相對來說,算法比較複雜。
筆者由于水準有限,目前仍未完全搞懂bch算法的原理。
bch算法,通常是由對應的nand flash的controller中,包含對應的硬體bch
ecc子產品,實作了bch算法,而作為軟體方面,需要在讀取資料後,寫入資料之前,分别操作對應bch相關的寄存器,設定成bch模式,然後讀取對應的bch狀态寄存器,得知是否有錯誤,和生成的bch校驗碼,用于寫入。
其具體代碼是如何操作這些寄存器的,由于是和具體的硬體,具體的nand flash的controller不同而不同,無法用同一的代碼。如果你是nand
flash驅動開發者,自然會得到對應的起nand flash的controller部分的datasheet,按照手冊說明,去操作即可。
不過,額外說明一下的是,關于bch算法,往往是要從專門的做軟體算法的廠家購買的,但是micron之前在網上放出一個免費版本的bch算法。
想要此免費的bch算法,可以在找到下載下傳位址
flash全名叫做flash memory,從名字就能看出,是種資料儲存設備,儲存設備有很多類,flash屬于非易失性儲存設備(non-volatile
memory device),與此相對應的是易失性儲存設備(volatile memory
device)。關于什麼是非易失性/易失性,從名字中就可以看出,非易失性就是不容易丢失,資料存儲在這類裝置中,即使斷電了,也不會丢失,這類裝置,除了flash,還有其他比較常見的入硬碟,rom等,與此相對的,易失性就是斷電了,資料就丢失了,比如大家常用的記憶體,不論是以前的sdram,ddr
sdram,還是現在的ddr2,ddr3等,都是斷電後,資料就沒了。
flash的内部存儲是mosfet,裡面有個懸浮門(floating gate),是真正存儲資料的單元。
在flash之前,紫外線可擦除(uv-erasable)的eprom,就已經采用了floating gate存儲資料這一技術了。
圖 1.1. 典型的flash記憶體單元的實體結構
資料在flash記憶體單元中是以電荷(electrical charge) 形式存儲的。存儲電荷的多少,取決于圖中的外部門(external
gate)所被施加的電壓,其控制了是向存儲單元中沖入電荷還是使其釋放電荷。而資料的表示,以所存儲的電荷的電壓是否超過一個特定的門檻值vth來表示,是以,flash的存儲單元的預設值,不是0(其他常見的儲存設備,比如硬碟燈,預設值為0),而是1,而如果将電荷釋放掉,電壓降低到一定程度,表述數字0。
flash主要分兩種,nand flash和nor flash。
關于nand flash和nor flash的差別,參見
不過,關于兩者差別,除了那個解釋之外,這裡再多解釋解釋:
nor的成本相對高,容量相對小,比如常見的隻有128kb,256kb,1mb,2mb等等,優點是讀寫資料時候,不容易出錯。是以在應用領域方面,nor
flash比較适合應用于存儲少量的代碼。
flash成本相對低,說白了就是便宜,缺點是使用中資料讀寫容易出錯,是以一般都需要有對應的軟體或者硬體的資料校驗算法,統稱為ecc。但優點是,相對來說容量比較大,現在常見的nand
flash都是1gb,2gb,更大的8gb的都有了,相對來說,價格便宜,是以适合用來存儲大量的資料。其在嵌入式系統中的作用,相當于pc上的硬碟,用于存儲大量資料。
是以,一個常見的應用組合就是,用小容量的nor flash存儲啟動代碼,比如uboot,用大容量的nand flash做整個系統和使用者資料的存儲。
而一般的嵌入式平台的啟動流程也就是,系統從裝有啟動代碼的nor flash啟動後,初始化對應的硬體,包括sdram等,然後将nand
flash上的linux
核心讀取到記憶體中,做好該做的事情後,就跳轉到sdram中去執行核心了,然後核心解壓(如果是壓縮核心的話,否則就直接運作了)後,開始運作,在linux核心啟動最後,去nand
flash上,挂載根檔案,比如jffs2,yaffs2等,挂載完成,運作初始化腳本,啟動consle互動,才允許你通過console和核心互動。至此完成整個系統啟動過程。
而nor flash就分别存放的是uboot,nand flash存放的是linux的核心鏡像和根檔案系統,以及餘下的空間分成一個資料區。
nor flash,有類似于dram之類的位址總線,是以可以直接和cpu相連,cpu可以直接通過位址總線對nor flash進行通路,而nand
flash沒有這類的總線,隻有io接口,隻能通過io接口發送指令和位址,對nand flash内部資料進行通路。相比之下,nor
flash就像是并行通路,nand flash就是串行通路,是以相對來說,前者的速度更快些。
但是由于實體制程/制造方面的原因,導緻nor 和nand在一些具體操作方面的特性不同:
表 1.1. nand flash和nor flash的差別
nor
(備注)
接口
總線
i/o接口
這個是兩者實體結構上的最大差別
單個cell大小
大
小
單個cell成本
高
低
讀耗時
快
慢
單位元組的程式設計時間
多位元組的程式設計時間
擦除時間
功耗
低,但是需要額外的ram
是否可以執行代碼
是
不行, 但是一些新的晶片,可以在第一頁之外執行一些小的loader
即是否允許晶片内執行(xip, execute in place)
位反轉(bit twiddling/bit flip)
幾乎無限制
1-4次,也稱作 “部分頁程式設計限制”
也就是資料錯誤,0→1或1→0
在晶片出廠時候是否允許壞塊
不允許
允許
nand flash,按照硬體類型,可以分為
bare nand chips:
裸片。單獨的nand flash晶片。
smartmediacards:
裸片+一層薄塑膠。常用于數位相機和mp3播放器中。之是以稱smart,是由于其軟體smart,而不是硬體本身有啥smart之處。
diskonchip:
裸片+glue logic。glue logic=硬體ecc産生器+用于靜态的nand
晶片控制的寄存器+直接通路一小片位址視窗,那塊位址中包含了引導代碼的stub樁,其可以從nand
flash中拷貝真正的引導代碼。
nand flash按照内部存儲資料單元的電壓的不同層次,也就是單個記憶體單元中,是存儲1位資料,還是多位資料,可以分為slc和mlc。
單個存儲單元,隻存儲一位資料,表示1或0。
就是上面介紹的,對于資料的表示,單個存儲單元中内部所存儲電荷的電壓,和某個特定的門檻值電壓vth,相比,如果大于此vth值,就是表示1,反之,小于vth,就表示0。
對于nand flash的資料的寫入1,就是控制external
gate去充電,使得存儲的電荷夠多,超過門檻值vth,就表示1了。而對于寫入0,就是将其放電,電荷減少到小于vth,就表示0了。
關于為何nand
flash不能從0變成1,我的了解是,實體上來說,是可以實作每一位的,從0變成1的,但是實際上,對于實際的實體實作,出于效率的考慮,如果對于,每一個存儲單元都能單獨控制,即,0變成1就是,對每一個存儲單元單獨去充電,所需要的硬體實作就很複雜和昂貴,同時,所進行對塊擦除的操作,也就無法實作之前所說的的,flash的速度,即一閃而過的速度了,也就失去了flash的衆多特性了。
與slc相對應的,就是單個存儲單元,可以存儲多個位,比如2位,4位等。其實作機制,說起來比較簡單,就是通過控制内部電荷的多少,分成多個門檻值,通過控制裡面的電荷多少,而達到我們所需要的存儲成不同的資料。比如,假設輸入電壓是vin=4v(實際沒有這樣的電壓,此處隻是為了舉例友善),那麼,可以設計出2的2次方=4個門檻值,
1/4
的vin=1v,2/4的vin=2v,3/4的vin=3v,vin=4v,分别表示2位資料00,01,10,11,對于寫入資料,就是充電,通過控制内部的電荷的多少,對應表示不同的資料。
對于讀取,則是通過對應的内部的電流(與vth成反比),然後通過一系列解碼電路完成讀取,解析出所存儲的資料。這些具體的實體實作,都是有足夠精确的裝置和技術,才能實作精确的資料寫入和讀出的。
單個存儲單元可以存儲2位資料的,稱作2的2次方=4 level cell,而不是2 level cell,關于這點,之前看nand
flash的資料手冊(datasheet)的時候,差點搞暈了。
同理,對于新出的單個存儲單元可以存儲4位資料的,稱作 2的4次方=16 level cell。
nand flash設計中,有個指令叫做read
id,讀取id,意思是讀取晶片的id,就像大家的身份證一樣,這裡讀取的id中,是讀取好幾個位元組,一般最少是4個,新的晶片,支援5個甚至更多,從這些位元組中,可以解析出很多相關的資訊,比如此nand
flash内部是幾個晶片(chip)所組成的,每個chip包含了幾片(plane),每一片中的頁大小,塊大小,等等。在這些資訊中,其中有一個,就是識别此flash是slc還是mlc。下面這個就是最常見的nand
flash的datasheet中所規定的,第3個位元組,3rd byte,所表示的資訊,其中就有slc/mlc的識别資訊:
表 1.2. nand flash第3個id的含義
description
i/o7
i/o6
i/o5 i/o4
i/o3 i/o2
i/o1 i/o0
internal chip number
1
2
4
8
0 0
0 1
1 0
1 1
cell type
2 level cell
4 level cell
8 level cell
number of simultaneously programmed pages
interleave program between multiple chips
not support
support
cache program
簡單說就是,常見的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
用圖表來表示,更加易懂:
圖 1.2. nand flash的結構圖
比如,型号為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的總大小
上面所說的block,page等nand flash的實體上的組織結構,是在chip的基礎上來說的,但是軟體程式設計的時候,除非你要用到multi
plane program和interleave page
program等,一般很少區分内部有幾個chip以及每個chip有幾個plane,而最關心的隻是nand
flash的總體容量size有多大,比如是1gb還是2gb等等。
下面詳細介紹一下,nand flash的一個chip内部的硬體邏輯組織結構。
nand flash的内部組織結構,此處還是用圖來解釋,比較容易了解:
圖 1.3. nand flash實體存儲單元的陣列組織結構
上圖是k9k8g08u0a的datasheet中的描述。
簡單解釋就是:
一個nand
flash(的chip,晶片)由很多個塊(block)組成,塊的大小一般是128kb,256kb,512kb,此處是128kb。其他的小于128kb的,比如64kb,一般都是下面将要介紹到的small
block的nand flash。
塊block,是nand
flash的擦除操作的基本/最小機關。
每個塊裡面又包含了很多頁(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頁)子頁的讀寫操作,不過一般很少見。
每一個頁,對應還有一塊區域,叫做空閑區域(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中,存放很多和自己檔案系統相關的資訊。
flash的擦除操作是以block塊為機關的,與此相對應的是其他很多儲存設備,是以bit位為最小讀取/寫入的機關,flash是一次性地擦除整個塊:在發送一個擦除指令後,一次性地将一個block,常見的塊的大小是128kb/256kb。。,全部擦除為1,也就是裡面的内容全部都是0xff了,由于是一下子就擦除了,相對來說,擦除用的時間很短,可以用一閃而過來形容,是以,叫做flash
memory。是以一般将flash翻譯為 (快速)閃存。
根據上面提到過的,flash最小操作機關,相對于普通儲存設備,就顯得有些特殊。
因為一般儲存設備,比如硬碟或記憶體,讀取和寫入都是以位(bit)為機關,讀取一個bit的值,将某個值寫入對應的位址的位,都是可以按位操作的。
但是flash由于實體特性,使得内部存儲的資料,隻能從1變成0,這點,這點可以從前面的内部實作機制了解到,對于最初始值,都是1,是以是0xffffffff,而資料的寫入,即是将對應的變成0,而将資料的擦出掉,就是統一地,以block為機關,全部一起充電,所有位,都變成初始的1,而不是像普通儲存設備那樣,每一個位去擦除為0。而資料的寫入,就是電荷放電的過程,代表的資料也從1變為了0。
是以,總結一下flash的特殊性如下:
表 1.3. flash和普通裝置相比所具有的特殊性
普通裝置(硬碟/記憶體等)
flash
讀取/寫入的叫法
讀取/寫入
讀取/程式設計(program)
讀取/寫入的最小機關
bit/位
page/頁
擦除(erase)操作的最小機關
block/塊
擦除操作的含義
将資料删除/全部寫入0
将整個塊都擦除成全是1,也就是裡面的資料都是0xff
對于寫操作
直接寫即可
在寫資料之前,要先擦除,然後再寫

提示
之是以将寫操作叫做程式設計,是因為flash是從之前的eprom、eeprom等繼承發展而來,而之前的eeprom,往裡面寫入資料,就叫做程式設計program,之是以這麼稱呼,是因為其對資料的寫入,是需要用電去擦除/寫入的,是以叫做程式設計。
對于目前常見的頁大小是2k/4k的nand
flash,其塊的大小有128kb/256kb/512kb等。而對于nor flash,常見的塊大小有64k/32k等。
在寫資料之前,要先擦除,内部就都變成0xff了,然後才能寫入資料,也就是将對應的位由1變成0。
nand flash的位反轉,也叫做位翻轉,對應的英文表達有:bit flip=bit flipping=bit-flip=bit
twiddling。
nand flash由于本身硬體的内在特性,會導緻(極其)偶爾的出現位反轉的現象。
所謂的位反轉,bit flip,指的是原先nand flash中的某個位,變化了,即要麼從1變成0了,要麼從0變成1了。
nand flash的位反轉現象,主要是由以下一些原因/效應所導緻:
漂移效應(drifting
effects)
漂移效應指的是,nand flash中cell的電壓值,慢慢地變了,變的和原始值不一樣了。
程式設計幹擾所産生的錯誤(program-disturb
errors)
此現象有時候也叫做,過度程式設計效應(over-program effect)。
對于某個頁面的程式設計操作,即寫操作,引起非相關的其他的頁面的某個位跳變了。
讀操作幹擾産生的錯誤(read-disturb
此效應是,對一個頁進行資料讀取操作,卻使得對應的某個位的資料,産生了永久性的變化,即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引腳功能說明
上圖是常見的nand flash所擁有的引腳(pin)所對應的功能,簡單翻譯如下:
表 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,就是高電平有效。
硬體上,有了電源的vcc和接地的vss等引腳,很好了解,但是為何還要有ale和cle這樣的引腳,為何設計這麼多的指令,把整個系統搞這麼複雜,關于這點,最後終于想明白了:
設計指令鎖存使能(command latch enable, cle) 和 位址鎖存使能(address latch
enable,ale),那是因為,nand
flash就8個i/o,而且是複用的,也就是,可以傳資料,也可以傳位址,也可以傳指令,為了區分你目前傳入的到底是啥,是以,先要用發一個cle(或ale)指令,告訴nand
flash的控制器一聲,我下面要傳的是指令(或位址),這樣,裡面才能根據傳入的内容,進行對應的動作。否則,nand
flash内部,怎麼知道你傳入的是資料,還是位址,還是指令,也就無法實作正确的操作了。
在nand
flash的硬體設計中,你會發現很多個引腳。關于硬體上為何設計這樣的引腳,而不是直接像其他儲存設備,比如普通的ram,直接是一對資料線引出來,多麼友善和好了解啊。
關于這樣設計的好處:
相對于并口(parellel)的nor
flash的48或52個引腳來說,的确是大大減小了引腳數目,這樣封裝後的晶片體積,就小很多。現在晶片在向體積更小,功能更強,功耗更低發展,減小晶片體積,就是很大的優勢。同時,減少晶片接口,也意味着使用此晶片的相關的外圍電路會更簡化,避免了繁瑣的硬體連線。
因為沒有像其他裝置一樣用實體大小對應的完全數目的addr引腳,在晶片内部換了晶片的大小等的改動,對于用全部的位址addr的引腳,那麼就會引起這些引腳數目的增加,比如容量擴大一倍,位址空間/尋址空間擴大一倍,是以,位址線數目/addr引腳數目,就要多加一個,而對于統一用8個i/o的引腳的nand
flash,由于對外提供的都是統一的8個引腳,内部的晶片大小的變化或者其他的變化,對于外部使用者(比如編寫nand
flash驅動的人)來說,不需要關心,隻是保證新的晶片,還是遵循同樣的接口,同樣的時序,同樣的指令,就可以了。這樣就提高了系統的擴充性。
說白了,對于舊的nand flash所實作的驅動,這些軟體工作,在換新的硬體的nand
flash的情況下,仍然可以工作,或者是通過極少的修改,就同樣可以工作,使得軟硬體相容性大大提高。
頁擦除時間是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,幾乎是同樣的使用壽命了。
封裝形式
48引腳的tsop1封裝 或 52引腳的ulga封裝
關于nand flash的控制器controller和nand flash晶片chip之間的關系,覺得有必要解釋一下:
首先,我們要知道的是,我們寫驅動,是寫nand flash 控制器的驅動,而不是nand flash 晶片的驅動,因為獨立的nand
flash晶片,一般來說,是很少直接拿來用的,多數都是硬體上有對應的硬體的nand flash的控制器,去操作和控制nand
flash,包括提供時鐘信号,提供硬體ecc校驗等等功能,我們所寫的驅動軟體,是去操作nand flash的控制器
然後由控制器去操作nand flash晶片,實作我們所要的功能。
由于nand flash相對其他常見裝置來說,比較特殊,是以,特殊的裝置,就有特殊的設計,就對應某些特殊的硬體特性,就有必要解釋解釋:
頁寄存器(page register):
由于nand flash讀取和程式設計操作來說,一般最小機關是頁,是以nand
flash在硬體設計時候,就考慮到這一特性,對于每一片(plane),都有一個對應的區域專門用于存放,将要寫入到實體存儲單元中去的或者剛從存儲單元中讀取出來的,一頁的資料,這個資料緩存區,本質上就是一個緩存buffer,但是隻是此處datasheet裡面把其叫做頁寄存器page
register而已,實際将其了解為頁緩存,更貼切原意。
而正是因為有些人不了解此内部結構,才容易産生之前遇到的某人的誤解,以為記憶體裡面的資料,通過nand flash的fifo,寫入到nand
flash裡面去,就以為立刻實作了實際資料寫入到實體存儲單元中了,而實際上隻是寫到了這個頁緩存中,隻有當你再發送了對應的程式設計第二階段的确認指令,即0x10,之後,實際的程式設計動作才開始,才開始把頁緩存中的資料,一點點寫到實體存儲單元中去。
是以,簡單總結一下就是,對于資料的流向,實際是經過了如下步驟:
圖 1.5. nand flash讀寫時的資料流向
nand flash中,一個塊中含有1個或多個位是壞的,就稱為其為壞塊bad block。
壞塊的穩定性是無法保證的,也就是說,不能保證你寫入的資料是對的,或者寫入對了,讀出來也不一定對的。與此對應的正常的塊,肯定是寫入讀出都是正常的。
壞塊有兩種:
出廠時就有存在的壞塊
一種是出廠的時候,也就是,你買到的新的,還沒用過的nand flash,就可以包含了壞塊。此類出廠時就有的壞塊,被稱作factory (masked)
bad block或initial bad/invalid block,在出廠之前,就會做對應的标記,标為壞塊。
使用過程中産生的壞塊
第二類叫做在使用過程中産生的,由于使用過程時間長了,在擦塊除的時候,出錯了,說明此塊壞了,也要在程式運作過程中,發現,并且标記成壞塊的。具體标記的位置,和上面一樣。這類塊叫做worn-out
bad block。即用壞了的塊。
具體标記的地方是,對于現在常見的頁大小為2k的nand
flash,是塊中第一個頁的oob起始位置(關于什麼是頁和oob,下面會有詳細解釋)的第1個位元組(舊的小頁面,pagesize是512b甚至256b的nand
flash,壞塊标記是第6個位元組),如果不是0xff,就說明是壞塊。相對應的是,所有正常的塊,好的塊,裡面所有資料都是0xff的。
不過,對于現在新出的有些nand
flash,很多标記方式,有些變化,有的變成該壞塊的第一個頁或者第二個頁,也有的是,倒數最後一個或倒數第二個頁,用于标記壞塊的。
具體的資訊,請參考對應的nand flash的資料手冊,其中會有說明。
對于壞塊的标記,本質上,也隻是對應的flash上的某些位元組的資料是非0xff而已,是以,隻要是資料,就是可以讀取和寫入的。也就意味着,可以寫入其他值,也就把這個壞塊标記資訊破壞了。對于出廠時的壞塊,一般是不建議将标記好的資訊擦除掉的。
uboot中有個指令是
就可以将塊中所有的内容都擦除了,包括壞塊标記,不論是出廠時的,還是後來使用過程中出現而新标記的。一般來說,不建議用這個。
不過,在實際的驅動程式設計開發過程中,為了友善起見,我倒是經常用,其實也沒啥大礙,呵呵。不過呢,其實最好的做法是,用
隻擦除好的塊,對于已經标記壞塊的塊,不要輕易擦除掉,否則就很難區分哪些是出廠時就壞的,哪些是後來使用過程中用壞的了。
對于壞塊的管理,在linux系統中,叫做壞塊管理(bbm,bad block
management),對應的會有一個表去記錄好塊,壞塊的資訊,以及壞塊是出廠就有的,還是後來使用産生的,這個表叫做壞塊表(bbt,bad block
table)。在linux 核心mtd架構下的nand flash驅動,和uboot中nand
flash驅動中,在加載完驅動之後,如果你沒有加入參數主動要求跳過壞塊掃描的話,那麼都會去主動掃描壞塊,建立必要的bbt的,以備後面壞塊管理所使用。
而關于好塊和壞塊,nand flash在出廠的時候,會做出保證:
關于好的,可以使用的塊的數目達到一定的數目,比如三星的k9g8g08u0m,整個flash一共有4096個塊,出廠的時候,保證好的塊至少大于3996個,也就是意思是,你新買到這個型号的nand
flash,最壞的可能,
有3096-3996=100個壞塊。不過,事實上,現在出廠時的壞塊,比較少,絕大多數,都是使用時間長了,在使用過程中出現的。
保證第一個塊是好的,并且一般相對來說比較耐用。做此保證的主要原因是,很多nand
flash壞塊管理方法中,就是将第一個塊,用來存儲上面提到的bbt,否則,都是出錯幾率一樣的塊,那麼也就不太好管理了,連放bbt的地方,都不好找了,^_^。
一般來說,不同型号的nand flash的資料手冊中,也會提到,自己的這個nand
flash,最多允許多少個壞塊。就比如上面提到的,三星的k9g8g08u0m,最多有100個壞塊。
在一個塊内,對每一個頁進行程式設計的話,必須是順序的,而不能是随機的。比如,一個塊中有128個頁,那麼你隻能先對page0程式設計,再對page1程式設計,。。。。,而不能随機的,比如先對page3,再page1,page2,page0,page4,。。。
關于此處對于隻能順序給頁程式設計的說法,隻是翻譯自datasheet,但是實際情況卻發現是,程式中沒有按照此邏輯處理,可以任意對某block内的page去做program的動作,而不必是順序的。但是datasheet為何如此解釋,原因未知,有待知情者給解釋一下。
要實作對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的晶片,那一般來說,都是統一的。
關于一些常見的操作,比如讀一個頁的read操作和寫一個頁的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就不會去擦除資料,這樣使得資料更安全,不會由于噪音而誤操作。
下面以最簡單的read操作為例,解釋如何了解時序圖,以及将時序圖中的要求,轉化為代碼。
解釋時序圖之前,讓我們先要搞清楚,我們要做的事情:
從nand flash的某個頁page裡面,讀取我們要的資料。
要實作此功能,會涉及到幾部分的知識,即使我們不太懂nand
flash的細節,但是通過前面的基本知識的介紹,那麼以我們的常識,至少很容易想到的就是,需要用到哪些指令,怎麼發這些指令,怎麼計算所需要的位址,怎麼讀取我們要的資料等等。
下面就一步步的解釋,需要做什麼,以及如何去做:
首先,是要了解,對于讀取資料,要用什麼指令:
根據前面關于nand
flash的指令集合介紹,我們知道,要讀取資料,要用到read指令,該指令需要2個周期,第一個周期發0x00,第二個周期發0x30。
知道了用何指令後,再去了解如何發送這些指令。

在開始解釋前,關于”使能”這個詞要羅嗦一下,以防有些讀者和我以前一樣,在聽這類詞語的時候,屬于初次接觸,或者接觸不多的,就很容易被搞得一頭霧水的(雖然該詞彙對于某些專業人士說是屬于最基本的詞彙了,囧)。
使能(enable),是指使其(某個信号)有效,使其生效的意思,“使其”“能夠”怎麼怎麼樣。。。。比如,上面圖中的cle線号,是高電平有效,如果此時将其設為高電平,我們就叫做,将cle使能,也就是使其生效的意思。
圖 1.7. nand flash資料讀取操作的時序圖
此圖來自三星的型号k9k8g08u0a的nand
flash的資料手冊(datasheet)。
我們來一起看看,我在圖6中的特意标注的①邊上的黃色豎線。
黃色豎線所處的時刻,是在發送讀操作的第一個周期的指令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忙的狀态的。
介紹了時刻①的各個信号的值,以及為何是這個值之後,相信,後面的各個時刻,對應的不同信号的各個值,大家就會自己慢慢分析了,也就容易了解具體的操作順序和原理了。
在介紹具體讀取資料的詳細流程之前,還要做一件事,那就是,先要搞懂我們要通路的位址,以及這些位址,如何分解後,一點點傳入進去,使得硬體能識别才行。
此處還是以k9k8g08u0a為例,此nand flash,一共有8192個塊,每個塊内有64頁,每個頁是2k+64 bytes。
假設,我們要通路其中的第7000個塊中的第64頁中的1208位元組處的位址,此時,我們就要先把具體的位址算出來:
實體位址
=塊大小×塊号 + 頁大小×頁号 + 頁内位址
=128k×7000 + 2k×64 + 1208
=0x36b204b8
接下來,我們就看看,怎麼才能把這個實際的實體位址,轉化為nand flash所要求的格式。
在解釋位址組成之前,先要來看看其datasheet中關于位址周期的介紹:
圖 1.8. nand flash的位址周期組成
結合中的2,3階段,我們可以看出,此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~a30,稱作頁号,頁的号碼,可以定位到具體是哪一個頁。
a18~a30,表示對應的塊号,即屬于哪個塊。
簡單解釋完了位址組成,那麼就很容易分析上面例子中的位址了。
注意,下面這樣的方法,是錯誤的:
0x36b204b8 = 11 0110 1011 0010 0000 0100 1011 1000,分别配置設定到5個位址周期就是:
1st周期
a7 ~ a0
1011 1000 = 0xb8
2nd周期
a11~ a8
0100 = 0x04
3rd周期
a19~a12
0010 0000 = 0x20
4th周期
a27~a20
0110 1011 = 0x6b
5th周期
a30~a28
11 = 0x03
與中對應的,*l,意思是地電平,由于未用到那些位,datasheet中強制要求設為0,是以,才有上面的2nd周期中的高4位是0000.其他的a30之後的位也是類似原理,都是0。
而至于上述計算方法為何是錯誤的,那是因為上面計算過程中,把第11位的值,本來是屬于頁号的位a11,給算成頁内位址裡面的值了。
應該是這樣計算,才是對的:
0x36b204b8 = 11 0110 1011 0010 0000 0100 1011 1000
a10~ a8
100 = 0x04
010 0000 0 = 0x40
110 1011 0 = 0xd6
11 0 = 0x06
那有人會問了,上面表11中,不是明明寫的a0到a30,其中包括a11,不是正好對應着此處位址中的bit0到bit30嗎?
其實,我開始也是犯了同樣的錯誤,誤以為我們要傳入的位址的每一位,就是對應着表11中的a0到a30呢,實際上,表11中的a11,是比較特殊的,隻有當我們通路頁内位址處于oob的位置,即屬于2048~2111的時候,a11才會其效果,才會用a0-a11用來表示對應的某個屬于2048~2111的某個值,屬于oob的某個位置。
而我們此處的頁内位址為2108,還沒有超過2047呢,是以a11肯定是0。
這麼解釋,顯得很繞,很難看懂。
換種方式來解釋,就容易聽懂了:
說白了,我們就是要通路第7000個塊中的第64頁中的1208位元組處,對應着
頁内位址
=1208
=0x4b8
頁号
=塊數×頁數/塊 + 塊内的頁号
= 7000×(128k/2k) + 64
= 7000×64 + 64
= 448064
=0x6d640
也就是,我們要通路0x6d640頁内的0x4b8位址,這樣很好了解吧,^_^。
然後對應的:
頁内位址=0x4b8
分成兩個對應的列位址,就變成
0x4b8 :列位址1=0xb8,列位址2=0x04
頁号=0x6d640,分成三個行号就是:
0x6d640:行号1=0x40,行号2=0xd6,行号3=0x06
再回頭看看上面的計算方法,
最開始計算出來的:
列位址1=0xb8
列位址2=0x04
行号1=0x20
行号2=0x6b
行号3=0x03
是錯誤的。
而第二次計算正确的:
行号1=0x40
行号2=0xd6
行号3=0x06
才是對的,也和我們此處自己手動計算,是一緻的。
第一次之是以計算錯,就是錯誤的把行位址的最低一位a11,放到列位址中的最高位了。
至此,才算把如何手動計算行位址和列位址,解釋明白和正确了。
對應的,linux的源碼\drivers\mtd\nand\nand_base.c中,也是這樣處理的:
column,即頁内位址,多數情況下,都是0,即使不是0,也可以直接通過将傳入的位址除于頁位址所得的餘數,就是列位址了。
page_addr即頁号,也很簡單,就是通過要通路的位址,除于頁大小,即可得到。
是以,我們要通路第7000個塊中的第64頁中的1208位元組處的話,所要傳入的位址就是分5個周期:
分别傳入兩個列位址的:
然後再傳3個行位址的:
這樣硬體才能識别。
而接下來的内容,也就是介紹硬體是如何處理這些輸入的。
準備工作終于完了,下面就可以開始解釋說明,對于讀操作的,上面圖中标出來的,1-6個階段,具體是什麼含義。
操作準備階段:此處是讀(read)操作,是以,先發一個圖5中讀指令的第一個階段的0x00,表示,讓硬體先準備一下,接下來的操作是讀。
發送兩個周期的列位址。也就是頁内位址,表示,我要從一個頁的什麼位置開始讀取資料。
接下來再傳入三個行位址。對應的也就是頁号。
然後再發一個讀操作的第二個周期的指令0x30。接下來,就是硬體内部自己的事情了。
flash内部硬體邏輯,負責去按照你的要求,根據傳入的位址,找到哪個塊中的哪個頁,然後把整個這一頁的資料,都一點點搬運到頁緩存中去。而在此期間,你所能做的事,也就隻需要去讀取狀态寄存器,看看對應的位的值,也就是r/b#那一位,是1還是0,0的話,就表示,系統是busy,仍在”忙“(着讀取資料),如果是1,就說系統活幹完了,忙清了,已經把整個頁的資料都搬運到頁緩存裡去了,你可以接下來讀取你要的資料了。
對于這裡。估計有人會問了,這一個頁一共2048+64位元組,如果我傳入的頁内位址,就像上面給的1208一類的值,隻是想讀取1028到2011這部分資料,而不是頁開始的0位址整個頁的資料,那麼内部硬體卻讀取整個頁的資料出來,豈不是很浪費嗎?答案是,的确很浪費,效率看起來不高,但是實際就是這麼做的,而且本身讀取整個頁的資料,相對時間并不長,而且讀出來之後,内部資料指針會定位到你剛才所制定的1208的那個位置。
接下來,就是你“竊取“系統忙了半天之後的勞動成果的時候了,呵呵。通過先去nand
flash的控制器中的資料寄存器中寫入你要讀取多少個位元組(byte)/字(word),然後就可以去nand
flash的控制器的fifo中,一點點讀取你要的資料了。
至此,整個nand flash的讀操作就完成了。
對于其他操作,可以根據我上面的分析,一點點自己去看datasheet,根據裡面的時序圖去分析具體的操作過程,然後對照代碼,會更加清楚具體是如何實作的。
unique id,翻譯為中文就是,獨一無二的id,唯一性辨別。
很明顯,這個unique id是為了用來識别某些東西的,每一個東西都擁有一個獨一無二的辨別資訊。
在nand flash裡面的unique id,主要是某個id資訊,保證每個nand flash都是獨一無二的。主要用于其它的使用nand
flash的使用者,根據此unique id去做加密等應用,實作某些安全方面的應用。
簡而言之,就是用nand flash的unique id來實作安全相關的應用,比如加密,版權保護等等。
此處,繼續解釋之前,還要再次贅述一下:
目前nand flash的廠家有samsung,toshiba,intel, hynix,micron,numonyx,phison
,sandisk,sony,spansion等。
由于前面所說的nand flash的規範之争,即toshiba & samsung和intel +
其它廠商(hynix,micron,numonyx,phison ,sandisk,sony,spansion等)之争,導緻對于unique
id這麼個小的功能點(feature),不同的方面,弄出了不同的實作(做法)。
下面就來解釋一下各個廠家關于unique id的實作方法,以及如何讀取對應的unique id:
1.2.16.1.2.1. toshiba東芝的nand的unique id
網上找到一個datasheet:
toshiba th58ns512dc
http://datasheet.elcodis.com/pdf/11/23/112371/th58ns512dc-to51y.pdf
中有提到:
p1:“the th58ns512dc is a smartmediatm
with id and each device has 128 bit unique id number embedded in the device.
this unique id number is applicable to image files, music files, electronic
books, and so on where copyright protection is
required.”即每個toshiba的th58ns512dc中,都有一個128 bit=16 byte的unique
id,可用于圖檔,音樂,電子書等應用中的版權保護。
p24:
圖 1.9. toshiba的unique id
1.2.16.1.2.2. 讀取toshiba的nand的unique id
從上面可以看出,toshiba的nand中,關于unique id,是需要先通過普通的0x90,即read
id的指令,去讀取nand的id,找到第三個位元組(3rd byte),然後判斷其是否是0xa5,如果是0xa5,然後才能确定此nand裡面是有unique
id的,然後才有去讀取unique id這一說。
而關于如何獨缺unique id,則需要和自己去聯系toshiba,和其簽訂nda協定後,才可得知讀取nand的unique id的方法。
網上找到的某款三星的nand的datasheet:
samsung k9f5608u0b
http://hitmen.c02.at/files/docs/psp/ds_k9f5608u0b_rev13.pdf
“
6. unique id for copyright protection is available
- the device includes one block sized otp (one time programmable), which can
be used to increase system security or to provide identification capabilities.
detailed information can be obtained by contact with samsung
”
即,samsung的nand的unique id,也和toshiba的用途類似,也主要是用于版權保護,但是其實作卻不同。
samsung的unique id的實作,是專門在nand 裡面配備了一個otp的block,而此nand晶片的block大小是16kb。
而關于如何操作此otp,即如何寫入資料和讀取資料,此處未說明。
欲知詳情,請聯系三星。
不過個人了解,應該和普通的block的操作類似,即普通的block,包含很多page,每個page的操作,有對應的page read,用對應的page
read指令來讀取此特殊的otp的block裡面的資料。
而此otp的block裡面的資料是什麼,完全取決于自己最開始往裡面寫入了什麼資料。說白了就是,你根據自己需求,在你的産品出廠的時候,寫入對應的資料,比如該款産品的sn序列号等資料,然後自己在用page
read讀取出相應資料後,自己解析,得到自己要的資訊,用于自己的用途,比如版權保護等。
1.2.16.1.3.1. 讀取samsung的nand的unique
id
如前所述:
關于如何操作此otp的block,即如何寫入資料和讀取資料,此處未說明。
即想要知道如何讀取samsung的nand的unique id,請自己去問三星。
主要指的是intel英特爾,hynix海力士,micron美光,numonyx恒億,spansion飛索等公司。
對應的nand 的unique id的相關定義,onfi的規範中都有,現簡要摘錄如下:
onfi 2.2
http://onfi.org/wp-content/uploads/2009/02/onfi%202_2%20gold.pdf
onfi規範中,在“5.7.1. parameter page data
structure definition”中,如圖:
圖 1.10. onfi的參數頁資料結構定義
定義了一個page的資料,用于存儲對應的nand的各種參數,其中,有一個第8個位元組的bit5==1的時候,表示支援“read unique
id”的指令,即說明此nand晶片支援此指令,如果byte8的bit5==0,那麼說明不支援,也就沒法去讀unique id了。
1.2.16.1.4.1. 讀取遵循onfi的廠商的nand的unique
如果經過上述判斷,此符合onfi的nand flash支援read unique id指令,次此時就可以通過該指令來讀取對應的nand
flash的unique id了。
此read unique id的詳細解釋為:
“ 5.8. read unique id definition
the read unique id function is used to retrieve the 16 byte unique id (uid)
for the device. the unique id when combined with the device manufacturer shall
be unique.
the uid data may be stored within the flash array. to allow the host to
determine if the uid is without bit errors, the uid is returned with its
complement, as shown in table 47. if the xor of the uid and its bit-wise
complement is all ones, then the uid is valid.
即用read unique id指令來讀取128bit=16位元組的unique id,但是呢,為了用于防止寫入的unique
id有誤,是以在16位元組後面又添了個對應的補碼,即每位取反的結果,這樣前16位元組的unique id和後16位元組的unique
id的補碼,構成了32位元組,算作一組,如下圖所示:
圖 1.11. onfi中unique id的結構
to accommodate robust retrieval of the uid in the case of bit errors, sixteen
copies of the uid and the corresponding complement shall be stored by the
target. for example, reading bytes 32-63 returns to the host another copy of the
uid and its complement. read status enhanced shall not be used during execution
of the read unique id command
figure 57 defines the read unique id behavior. the host may use any timing
mode supported by the target in order to retrieve the uid data.
而為了進一步防止出錯,将上面32位元組算一組,重複了16次,将這16個32位元組的資料,存在nand flash裡面,然後用read unique
id指令去讀取出來,取得其中某個32位元組即可,然後判斷前16位元組和後16位元組取反,如果結果所有位都是1,那麼結果即為16個0xff,那麼說明unique
id是正确的。否則說明有誤。
read unique id的指令的詳細格式如下圖所示:
圖 1.12. onfi中read unique
id指令的時序圖
即先發送0xed指令,再發送0x00位址,然後等待nand
flash的busy狀态結束,就可以讀取出來的那16組的32個位元組了,然後用上面辦法去判斷,找到前16位元組和後16位元組異或得到結果全部16位元組都為0xff,即說明得到了正确的unique
id。至此,符合onfi規範的unique id,就讀取出來了。
很多nand flash支援一個叫做ce don’t-care的技術,字面意思就是,不關心是否片選。
對此也許有人會問了,如果不片選,那還能對其操作嗎?答案就是,這個技術,主要用在當時是不需要選中晶片,但是晶片内部卻仍可以繼續操作的這些情況:在某些應用,比如錄音,音頻播放等應用中,外部使用的微秒(us)級的時鐘周期,此處假設是比較少的2us,在進行讀取一頁或者對頁程式設計時,是對nand
flash操作,這樣的串行(serial
access)通路的周期都是20/30/50ns,都是納秒(ns)級的,此處假設是50ns,當你已經發了對應的讀或寫的指令之後,接下來隻是需要nand
flash内部去自己操作,将資料讀取除了或寫入進去到内部的資料寄存器中而已,此處,如果可以把片選取消,ce#是低電平有效,取消片選就是拉高電平,這樣會在下一個外部指令發送過來之前,即微秒量級的時間裡面,即2us-50ns≈2us,這段時間的取消片選,可以降低很少的系統功耗,但是多次的操作,就可以在很大程度上降低整體的功耗了。
總的來說就是:由于某些外部應用所需要的通路nand flash的頻率比較低,而nand flash内部操作速度比較快,是以在針對nand
flash的讀或寫操作的大部分時間裡面,都是在等待外部指令的輸入,同時卻選中晶片,産生了多餘的功耗,此“不關心片選”技術,就是在nand
flash的内部的相對快速的操作(讀或寫)完成之後,就取消片選,以節省系統功耗。待下次外部指令/資料/位址輸入來的時候,再選中晶片,即可正常繼續操作了。這樣,整體上,就可以大大降低系統功耗了。

如果想要操作硬體nand
flash晶片,先要将對應的ce#(低有效)片選信号拉低,選中該晶片,然後才能做接下來的讀寫操作所要做的發指令,發資料等動作。
flash的片選與否,功耗差别會有很大。如果資料沒有記錯的話,我之前遇到我們系統裡面的nand
flash的片選,大概有5個ma的電流輸出呢,也許你對5ma沒太多概念,給你說個資料你就知道了:當時為了針對mp3播放功耗進行優化,整個系統優化之後的待機功耗,也才10個ma左右的,是以節省5ma已經算是很不錯的功耗優化了。
copy-back功能,簡單的說就是,将一個頁的資料,拷貝到另一個頁。
如果沒有copy-back功能,那麼正常的做法就是,先要将那個頁的資料拷貝出來放到記憶體的資料buffer中,讀出來之後,再用寫指令将這頁的資料,寫到新的頁裡面。
而copy-back功能的好處在于,不需要用到外部的存儲空間,不需要讀出來放到外部的buffer裡面,而是可以直接讀取資料到内部的頁寄存器(page
register)然後寫到新的頁裡面去。
而且,為了保證資料的正确,要硬體支援edc(error detection
code)的,否則,在資料的拷貝過程中,可能會出現錯誤,并且拷貝次數多了,可能會累積更多錯誤。
而對于錯誤檢測來說,硬體一般支援的是512位元組資料,對應有16位元組用來存放校驗産生的ecc數值,而這512位元組一般叫做一個扇區。對于2k+64位元組大小的頁來說,按照512位元組分,分别叫做a,b,c,d區,而後面的64位元組的oob區域,按照16位元組一個區,分别叫做e,f,g,h區,對應存放a,b,c,d資料區的ecc的值。
copy-back程式設計的主要作用在于,去掉了資料串行讀取出來,再串行寫入進去的時間,是以,而這部分操作,是比較耗時的,是以此技術可以提高程式設計效率,提高系統整體性能。
對于有些新出的nand
flash,支援同時對多個片進行程式設計,比如上面提到的三星的k9k8g08u0a,内部包含4片(plane),分别叫做plane0,plane1,plane2,plane3。.由于硬體上,對于每一個plane,都有對應的大小是2048+64=2112位元組的頁寄存器(page
register),使得同時支援多個plane程式設計成為可能。 k9k8g08u0a支援同時對2個plane進行程式設計。
不過要注意的是,隻能對plane0和plane1或者plane2和plane3,同時程式設計,而不支援plane0和plane2同時程式設計。
多片同時程式設計,是針對一個chip裡面的多個plane來說的,
而此處的交錯頁程式設計,是指對多個chip而言的。
可以先對一個chip,假設叫chip1,裡面的一頁進行程式設計,然後此時,chip1内部就開始将資料一點點寫到頁裡面,就出于忙的狀态了,而此時可以利用這個時間,對出于就緒狀态的chip2,也進行頁程式設計,發送對應的指令後,chip2内部也就開始慢慢的寫資料到存儲單元裡面去了,也出于忙的狀态了。此時,再去檢查chip1,如果程式設計完成了,就可以開始下一頁的程式設計了,然後發完指令後,就讓其内部慢慢的程式設計吧,再去檢查chip2,如果也是程式設計完了,也就可以進行接下來的其他頁的程式設計了。如此,互動操作chip1和chip2,就可以有效地利用時間,使得整體程式設計效率提高近2倍,大大提高nand
flash的程式設計/擦寫速度了。
在介紹此特性之前,先要說說,與random data output in a page相對應的是,普通的,正常的,sequential data
output in a page。
正常情況下,我們讀取資料,都是先發讀指令,然後等待資料從存儲單元到内部的頁資料寄存器中後,我們通過不斷地将re#(read
enale,低電平有效)置低,然後從我們開始傳入的列的起始位址,一點點讀出我們要的資料,直到頁的末尾,當然有可能還沒到頁位址的末尾,就不再讀了。所謂的順序(sequential)讀取也就是,根據你之前發送的列位址的起始位址開始,每讀一個位元組的資料出來,内部的資料指針就加1,移到下個位元組的位址,然後你再讀下一個位元組資料,就可以讀出來你要的資料了,直到讀取全部的資料出來為止。
而此處的随機(random)讀取,就是在你正常的順序讀取的過程中,先發一個随機讀取的開始指令0x05指令,再傳入你要将内部那個資料指針定位到具體什麼位址,也就是2個cycle的列位址,然後再發随機讀取結束指令0xe0,然後,内部那個資料位址指針,就會移動到你所制定的位置了,你接下來再讀取的資料,就是從那個制定位址開始的資料了。
而nand flash資料手冊裡面也說了,這樣的随機讀取,你可以多次操作,沒限制的。
請注意,上面你所傳入的位址,都是列位址,也就是頁内位址,也就是說,對于頁大小為2k的nand
flash來說,所傳入的位址,應該是小于2048+64=2112的。
不過,實際在nand flash的使用中,好像這種用法很少的。絕大多數,都是順序讀取資料。
如果想要在linux下編寫nand flash驅動,那麼就先要搞清楚linux下,關于此部分的整個架構。弄明白,系統是如何管理你的nand
flash的,以及,系統都幫你做了那些準備工作,而剩下的,驅動底層實作部分,你要去實作哪些功能,才能使得硬體正常工作起來。
在介紹nand flash的軟體細節方面之前,先來介紹一下nand flash的兩個相關的規範:onfi和lba。
onfi規範,即open nand flash interface specification。
onfi是intel主導的,其他一些廠家(hynix,micron,numonyx,phison
,sandisk,sony,spansion等)參與制定的,統一了nand flash的操作接口。
所謂操作接口,就是那些對nand flash操作的指令等内容。
而所謂統一,意思是之前那些nand flash的操作指令等,都是各自為政,雖然大多數常見的nand flash的操作,比如page
read的指令是0x00,0x30,page write的指令是0x80,0x10等,但是有些指令相關的内容,很特别且很重要的一個例子就是,每個廠家的nand
flash的read id的指令,雖然都是0x90,但是讀取出來的幾個位元組的含義,每個廠家定義的都不太一樣。
是以,才有統一nand flash的操作接口這一說。
onfi規範,官網可以下載下傳的到:
http://onfi.org/specifications/
比如:
onfi 2.2 spec
onfi規範中定義的nand flash的指令集合為:
圖 1.13. onfi中的nand flash的指令集合
可以看到,其中常見的一些指令,比如
page read(0x00,0x30)
page write(0x80,0x10)
block erase(0x60,0xd0)
reset(0xff)
等等指令,都是和普通的nand flash的指令是一樣的,而額外多出一些指令,比如read unique id(0xed)等指令,是之前某些nand
flash指令所不具有的。
如此,定義了nand flash的操作的指令的集合以及發送對應指令所遵循的時序等内容。
onfi還定義了另外一個規範:
onfi block abstracted nand specification
http://onfi.org/wp-content/uploads/2009/02/ba_nand_rev_1_1_gold.pdf
即onfi lba nand,簡單說就是,邏輯塊尋址的nand。其含義和toshiba的lba,基本沒有太多差別。
onfi規範定義了之後,每家廠商的nand flash,隻要符合這個onfi規範,然後上層nand
flash的軟體,就可以統一隻用一種了,換句話說,我寫了一份nand flash的驅動後,就可以操作所有和onfi相容的nand flash了,整個nand
flash的相容性,上層軟體的相容性,互操作性,就大大提高了。
而且,同樣的,由于任何規範在定義的時候,都會考慮到相容性和擴充性,onfi也不例外。針對符合onfi規範的,寫好的軟體,除了可以操作多家與onfi相容的nand
flash之外,而對于以後出現的新的技術,新制程的nand flash,隻要符合onfi規範,也同樣可以支援,可以在舊的軟體下工作,而不需要由于nand
flash的更新換代,而更改上層軟體和驅動,這個優勢,由于對于将nand
flash晶片內建到自己系統中的相關開發人員來說,是個好消息。
lba nand flash,logical block address,邏輯塊尋址的nand flash,是nand
flash大廠之一的toshiba,自己獨立設計出來的新一代的nand flash的規範。
之是以叫做邏輯塊尋址,是相對于之前常見的,普通的nand flash的實體塊的尋址來說的。常見的nand
flash,如果要讀取和寫入資料,所用的對應的位址是對應的:block位址+block内的page位址+page内的偏移量 = 絕對的實體位址,
此實體塊尋址,相對來說有個缺點,那就是,由于之前提到的nand
flash會出現使用過程中出現壞塊,是以,遇到這樣的壞塊,首先壞塊管理要去将此壞塊标記,然後将壞塊的資料拷貝到另一個好的block中,再繼續通路新的block。
而且資料讀寫過程中,還要有對應的ecc校驗,很多情況下,也都是軟體來實作這部分的工作,即使是硬體的ecc校驗,也要寫少量的軟體,去操作對應寄存器,讀取ecc校驗的結果,當然别忘了,還有對應的負載平衡等工作。
如此的這類的壞塊管理工作,對于軟體來說,很是繁重,而且整個系統實作起來也不是很容易,是以,才催生了一個想法,是否可以把ecc校驗,負載平衡,壞塊管理,全部都放到硬體實作上,而對于軟體來說,我都不關心,隻關心有多少個block供我使用,用于資料讀寫。
針對于此需求,toshiba推出了lba邏輯塊尋址的nand flash,在nand
flash存儲晶片之外,加了對應一個硬體控制權controller,實作了上述的壞塊管理,ecc校驗,負載平衡等工作,這樣使得人家想要用你lba的nand
flash的人,去開發對應的軟體來驅動lba nand
flash工作,相對要做的事情,就少了很多,相對來說就是減輕了軟體系統內建方面的工作,提高了開發效率,縮短了産品上市周期。
lba nand,最早放出對應的樣片(sample)是在2006年8月。
網上找到一個lba nand flash的簡介:
http://www.toshiba-components.com/prpdf/5678e.pdf
現早已經量産,偶在之前開發過程中,就用過其某款lba的nand flash。
目前網上還找不到免費的lba的規範。除非你搞開發,和toshiba簽訂nda協定後,才可以拿到對應的specification。
關于toshiba lba nand規範,在此多說一點(參考附錄中:lba-core.c):
lba nand分為pnp,vfp和mdp三種分區:
pnp主要用于存放uboot等啟動代碼
vfp主要用于存放uimage等核心代碼
mdp主要用于存放使用者的資料,以及rootfs等内容
在解釋為何會有onfi和lba之前,先來個背景介紹:
onfi的出現,上面已經解釋過了,就是為了統一nand flash的接口,使得軟體相容性更好;
而lba的出現,是為了減輕軟體方面對nand flash的各種管理工作。上面這些解釋,其實隻是技術上解釋。
而現實方面是,onfi是intel主導的,其他一些nand flash廠商參與制定出來的一套nand flash的規範,但是卻沒有得到nand
flash的兩個大廠家的認可,一個是以第一廠商自居samsung,另一個是nand flash技術引導者的toshiba。是以,可以算是在nand
flash領域裡,老三帶着一幫小的,定了一個規範,但是老大和老二卻不買賬。是以,技術上的nand的老大toshiba聯手産量上的老大,自己去推出了另外一套規範lba。
這可以稱得上是典型的規範之争吧。
總的來說,onfi在對于舊的nand flash的相容上,都是相對類似的。
總的來說:
onfi規範,更注重對于nand flash的操作接口方面的定義 + onfi lba nand的定義,而toshiba
lba規範,主要側重于lba的nand的定義。
onfi block abstracted nand
specification,基本上和toshiba的lba,沒太多差別,隻是toshiba的lba規範中,又多了些其他模式和應用類别。
總的來說,可以這麼劃分:
onfi = nand flash操作接口的統一 + onfi的lba nand
toshiba lba = 等價于onfi的lba nand + 多種模式和對應的不同應用
mtd,是linux的儲存設備中的一個子系統。其設計此系統的目的是,對于記憶體類的裝置,提供一個抽象層,一個接口,使得對于硬體驅動設計者來說,可以盡量少的去關心存儲格式,比如ftl,ffs2等,而隻需要去提供最簡單的底層硬體裝置的讀/寫/擦除函數就可以了。而對于資料對于上層使用者來說是如何表示的,硬體驅動設計者可以不關心,而mtd儲存設備子系統都幫你做好了。
對于mtd子系統的好處,簡單解釋就是,他幫助你實作了,很多對于以前或者其他系統來說,本來也是你驅動設計者要去實作的很多功能。換句話說,有了mtd,使得你設計nand
flash的驅動,所要做的事情,要少很多很多,因為大部分工作,都由mtd幫你做好了。
當然,這個好處的一個“副作用”就是,使得我們不了解的人去了解整個linux驅動架構,以及mtd,變得更加複雜。但是,總的說,覺得是利遠遠大于弊,否則,就不僅需要你了解,而且還是做更多的工作,實作更多的功能了。
此外,還有一個重要的原因,那就是,前面提到的nand flash和普通硬碟等裝置的特殊性:
有限的通過出複用來實作輸入輸出指令和位址/資料等的io接口,最小機關是頁而不是常見的bit,寫前需擦除等,導緻了這類裝置,不能像平常對待硬碟等操作一樣去操作,隻能采取一些特殊方法,這就誕生了mtd裝置的統一抽象層。
mtd,将nand flash,nor
flash和其他類型的flash等裝置,統一抽象成mtd裝置來管理,根據這些裝置的特點,上層實作了常見的操作函數封裝,底層具體的内部實作,就需要驅動設計者自己來實作了。具體的内部硬體裝置的讀/寫/擦除函數,那就是你必須實作的了。
表 1.5. mtd裝置和硬碟裝置之間的差別
hard drives
mtd device
連續的扇區
連續的可擦除塊
扇區都很小(512b,1024b)
可擦除塊比較大 (32kb,128kb)
主要通過兩個操作對其維護操作:讀扇區,寫扇區
主要通過三個操作對其維護操作:從擦除塊中讀,寫入擦除塊,擦寫可擦除塊
壞快被重新映射,并且被硬體隐藏起來了(至少是在如今常見的lba硬碟裝置中是如此)
壞的可擦除塊沒有被隐藏,軟體中要處理對應的壞塊問題
hdd扇區沒有擦寫壽命超出的問題
可擦除塊是有擦除次數限制的,大概是104-105次
多說一句,關于mtd更多的内容,感興趣的,去附錄中的去看。
關于mtd裝置驅動,感興趣的可以去參考附錄中mtd裝置的文章,該文章是比較詳細地介紹了整個mtd架構和流程,友善大家了解整個mtd架構和nand
flash驅動。
關于nand flash,由于各個廠家的read
id讀出的内容的定義,都不同,導緻,對于讀出的id,分别要用不同的解析方法,下面這段代碼,是我之前寫的,本來打算自己寫信去推薦到linux
mtd核心源碼的,不過後來由于沒搞懂具體申請流程,就放棄了。不過,後來,看到linux的mtd部分更新了,加了和下面類似的做法。
此處隻是為了記錄下來,也算給感興趣的人一個參考吧。
檔案:<code>\linux-2.6.28.4\drivers\mtd\nand\nand_base.c</code>
下面這部分主要介紹一下,關于硬體的設計和規範,是如何映射到具體的軟體實作的,看了這部分内容之後,你對如何根據硬體的規範去用軟體代碼實作對應的功能,就有了大概的了解了,然後去實作對應的某硬體的驅動,就有了大概的脈絡了。
關于硬體部分的細節,前面其實已經介紹過了,但是為了友善說明,此處還是以讀操作為例去講解硬體到軟體是如何映射的。
再次貼出上面的那個圖:
圖 1.14. nand flash資料讀取操作的時序圖
對于上面的從1到6,每個階段所表示的含義,再簡單解釋一下:
此階段,是讀指令第一個周期,發送的指令為0x00。
此階段,依次發送列位址,關于這些行位址,列位址等是如何計算出來的,後面的内容會有詳細解釋。
此階段是發送對應的行位址
此階段是發送讀指令第二周期2nd
cycle所對應的指令,0x30
此階段是等待時間,等待nand
flash硬體上準備好對應的資料,以便後續讀出。
此階段,就是一點點地把所需要的資料讀出來。
上面的是内容,說的是硬體是如何設計的,而這硬體的設計,即硬體的邏輯時序是如何規定的,對應的軟體實作,也就要如何實作。不過可以看出的是,其中很多步驟,比如步驟1和步驟4,那都是固定的,而且,即使其中的步驟2和步驟3,即使是不同廠家和不同的nand
flash晶片,除了要寫入的列位址和行位址可能不同之外,也都是邏輯一樣的,同樣地,步驟5和6,也都是一樣的,唯一不同的,是每家不同的nand
flash控制器不同,是以具體到步驟6的時候,去讀出資料的方式不同,是以,那一部分肯定是你要實作nand
flash驅動的時候要自己實作的,而對應的其他幾個公有的步驟呢,就有了linux的mtd層幫你實作好了,是以,下面就來介紹一下,關于讀取一個nand
flash的頁page,linux的mtd層,是如何具體的幫你實作的:
關于nand
flash的讀操作,即讀取一頁的資料,這樣的讀資料的操作,很明顯,是從上層檔案系統傳遞過來的,其細節我們在此忽略,但是要知道,上層讀取資料的請求,傳遞到了mtd這一層,其入口是哪個函數,然後我們才能繼續往下面分析細節。
關于下面所要的介紹的代碼,如果沒有明确指出,都是位于此檔案:
代碼位置:<code>\drivers\mtd\nand\nand_base.c</code>
mtd讀取資料的入口是nand_read,然後調用nand_do_read_ops,此函數主體如下:
要讀取資料,肯定是要先發送對應的讀頁(read page)的指令
發送完指令,接着就可以去調用read_page函數讀取對應的資料了
對于上述中的函數cmdfunc,一般來說可以不用自己的驅動中實作,而直接使用mtd層提供的已有的函數,nand_command_lp,其細節如下:
此處就是就是發送讀指令的第一個周期1st cycle的指令,即0x00,對應着上述時序圖中的
接下來是發送兩個column,列位址,對應着上述時序圖中的
然後發送三個row行位址,對應着上述時序圖中的
接下來發送讀指令的第二個周期2nd cycle的指令,即0x30,對應着上述時序圖中的
此處是對應着上述時序圖中的twb的等待時間
接下來就是要等待一定的時間,使得nand flash硬體上準備好資料,以供之後讀取,即對應着時序圖中的
對于之前的的函數<code>read_page</code>,一般來說也可以不用自己的驅動中實作,而直接使用mtd層提供的已有的函數,<code>nand_read_page_hwecc</code>,該函數所要實作的功能,正是上面餘下沒介紹的,即一點點的讀出我們要的資料:
真正的資料讀取,就是下面這個read_buf函數了
上面的read_buf,就是真正的去讀取資料的函數了,由于不同的nand flash
controller控制器所實作的方式不同,是以這個函數必須在你的nand
flash驅動中實作,即mtd層,能幫我們實作的都實作了,不能實作的,那肯定要你的驅動自己實作。
對于我們這裡的s3c2410的例子來說,就是<code>s3c2410_nand_read_buf</code>:
檔案位置:<code>\drivers\mtd\nand\s3c2410.c</code>
真正的位址去讀取資料
可以看出,此處的實作相當地的簡單,就是讀取對應的io的位址,然後就可以把資料讀出來就可以了。
不過,要注意的是,并不是所有的驅動都是這麼簡單,具體情況則是不同的nand flash控制器對應不同實作方法。
至此,關于整個的nand
flash的讀取一頁的資料的操作,是如何将硬體的邏輯時序圖,映射到對應的軟體的實作的,就已經介紹完了。而看懂了這個過程,你才會更加明白,原來mtd層,已經幫助我們實作了很多很多通用的操作所對應的軟體部分,而隻需要我們實作剩下那些和具體硬體相關的操作的函數,就可以了,可以說大大減輕了驅動開發者的工作量。
因為,如果沒了mtd層,那麼上面那麼多的函數,幾乎都要我們自己實作,單單是代碼量,就很龐大,而且再加上寫完代碼後的驅動測試功能是否正常,使得整個驅動開發,變得難的多得多。
在介紹具體如何寫nand flash驅動之前,我們先要了解,大概的整個系統,和nand
flash相關的部分的驅動工作流程,這樣,對于後面的驅動實作,才能更加清楚機制,才更容易實作,否則就是,即使寫完了代碼,也還是沒搞懂系統是如何工作的了。
讓我們以最常見的,linux核心中已經有的三星的nand flash驅動,來解釋nand flash驅動具體流程和原理。
此處是參考2.6.29版本的linux源碼中的<code>\drivers\mtd\nand\s3c2410.c</code>,以2410為例。
在nand flash驅動加載後,第一步,就是去調用對應的<code>init</code>函數,<code>s3c2410_nand_init</code>,去将在nand flash驅動注冊到linux驅動架構中。
驅動本身,真正開始,是從probe函數,s3c2410_nand_probe->s3c24xx_nand_probe,
在probe過程中,去用<code>clk_enable</code>打開nand
flash控制器的clock時鐘,用<code>request_mem_region</code>去申請驅動所需要的一些記憶體等相關資源。然後,在<code>s3c2410_nand_inithw</code>中,去初始化硬體相關的部分,主要是關于時鐘頻率的計算,以及啟用nand
flash控制器,使得硬體初始化好了,後面才能正常工作。
需要多解釋一下的,是這部分代碼:
調用init chip去挂載你的nand 驅動的底層函數到nand flash的結構體中,以及設定對應的ecc
mode,挂載ecc相關的函數
scan_ident,掃描nand 裝置,設定nand
flash的預設函數,獲得實體裝置的具體型号以及對應各個特性參數,這部分算出來的一些值,對于nand flash來說,是最主要的參數,比如nand
falsh的晶片的大小,塊大小,頁大小等。
scan tail,從名字就可以看出來,是掃描的後一階段,此時,經過前面的scan_ident,我們已經獲得對應nand
flash的硬體的各個參數,然後就可以在scan
tail中,根據這些參數,去設定其他一些重要參數,尤其是ecc的layout,即ecc是如何在oob中擺放的,最後,再去進行一些初始化操作,主要是根據你的驅動,如果沒有實作一些函數的話,那麼就用系統預設的。
add partion,根據你的nand flash的分區設定,去分區
等所有的參數都計算好了,函數都挂載完畢,系統就可以正常工作了。
上層通路你的nand falsh中的資料的時候,通過mtd層,一層層調用,最後調用到你所實作的那些底層通路硬體資料/緩存的函數中。
關于上面提到的,在nand_scan_tail的時候,系統會根據你的驅動,如果沒有實作一些函數的話,那麼就用系統預設的。如果實作了自己的函數,就用你的。
估計很多人就會問了,那麼到底我要實作哪些函數呢,而又有哪些是可以不實作,用系統預設的就可以了呢。
此問題的,就是我們下面要介紹的,也就是,你要實作的,你的驅動最少要做哪些工作,才能使整個nand flash工作起來。
其實,要了解,關于驅動架構部分,你所要做的事情的話,隻要看看三星的整個nand flash驅動中的這個結構體,就差不多了:
probe就是系統“探測”,就是前面解釋的整個過程,這個過程中的多數步驟,都是和你自己的nand
flash相關的,尤其是那些硬體初始化部分,是你必須要自己實作的。
remove,就是和probe對應的,“反初始化”相關的動作。主要是釋放系統相關資源和關閉硬體的時鐘等常見操作了。
suspend和resume,對于很多沒用到電源管理的情況下,至少對于我們剛開始寫基本的驅動的時候,可以不用關心,放個空函數即可。
而對于底層硬體操作的有些函數,總體上說,都可以在上面提到的<code>s3c2410_nand_init_chip</code>中找到:
s3c2410_nand_write_buf 和
s3c2410_nand_read_buf:這是兩個最基本的操作函數,其功能,就是往你的nand
flash的控制器中的fifo讀寫資料。一般情況下,是mtd上層的操作,比如要讀取一頁的資料,那麼在發送完相關的讀指令和等待時間之後,就會調用到你底層的read_buf,去nand
flash的fifo中,一點點把我們要的資料,讀取出來,放到我們制定的記憶體的緩存中去。寫操作也是類似,将我們記憶體中的資料,寫到nand
flash的fifo中去。
s3c2410_nand_select_chip : 實作nand flash的片選。
flash控制器中,一般都有對應的資料寄存器,用于給你往裡面寫資料,表示将要讀取或寫入多少個位元組(byte,u8)/字(word,u32)
,是以,此處,你要給出位址,以便後面的操作所使用
s3c2410_nand_hwcontrol:給底層發送指令或位址,或者設定具體操作的模式,都是通過此函數。
s3c2410_nand_devready:nand
flash的一些操作,比如讀一頁資料,寫入(程式設計)一頁資料,擦除一個塊,都是需要一定時間的,在命發送完成後,就是硬體開始忙着工作的時候了,而硬體什麼時候完成這些操作,什麼時候不忙了,變就緒了,就是通過這個函數去檢查狀态的。
一般具體實作都是去讀硬體的一個狀态寄存器,其中某一位是否是1,對應着是出于“就緒/不忙”還是“忙”的狀态。這個寄存器,也就是我們前面分析時序圖中的r/b#。
s3c2410_nand_calculate_ecc:如果是上面提到的硬體ecc的話,就不用我們用軟體去實作校驗算法了,而是直接去讀取硬體産生的ecc數值就可以了。
s3c2410_nand_correct_data:當實際操作過程中,讀取出來的資料所對應的硬體或軟體計算出來的ecc,和從oob中讀出來的ecc不一樣的時候,就是說明資料有誤了,就需要調用此函數去糾正錯誤。對于現在slc常見的ecc算法來說,可以發現2位,糾正1位。如果錯誤大于1位,那麼就無法糾正回來了。一般情況下,出錯超過1位的,好像幾率不大。至少我看到的不是很大。更複雜的情況和更加注重資料安全的情況下,一般是需要另外實作更高效和檢錯和糾錯能力更強的ecc算法的。
此處,多數情況下,你所用的nand flash的控制器,都是支援硬體ecc的,是以,此處設定硬體ecc(hw_ecc)
,也是充分利用硬體的特性,而如果此處不用硬體去做的ecc的話,那麼下面也會去設定成nand_ecc_soft,系統會用預設的軟體去做ecc校驗,相比之下,比硬體ecc的效率就低很多,而你的nand
flash的讀寫,也會相應地要慢不少
s3c2410_nand_enable_hwecc:
在硬體支援的前提下,前面設定了硬體ecc的話,要實作這個函數,用于每次在讀寫操作前,通過設定對應的硬體寄存器的某些位,使得啟用硬體ecc,這樣在讀寫操作完成後,就可以去讀取硬體校驗産生出來的ecc數值了。
當然,除了這些你必須實作的函數之外,在你更加熟悉整個架構之後,你可以根據你自己的nand
flash的特點,去實作其他一些原先用系統預設但是效率不高的函數,而用自己的更高效率的函數替代他們,以提升你的nand flash的整體性能和效率。
此處記錄一些和nand flash相關的一些資料:
對于nand flash,目前市場上可以看到很多家的nand flash的晶片,每家都有各自的型号,即對應的id或part number。
此處整理記錄一下,各個廠家的nand flash的命名規則。
已整理至:
有空再整理至此。
待整理。
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]
[18]