戰(zhàn):從物理結(jié)構(gòu)到SPI驅(qū)動(dòng)與全志T113適配)
1. 項(xiàng)目概述為什么我們需要重新梳理NAND Flash在嵌入式開發(fā)、存儲(chǔ)系統(tǒng)設(shè)計(jì)甚至日常消費(fèi)電子維修的圈子里NAND Flash這個(gè)詞出現(xiàn)的頻率高得驚人。但說實(shí)話我發(fā)現(xiàn)很多剛?cè)胄械呐笥焉踔烈恍┯袔啄杲?jīng)驗(yàn)的工程師對(duì)它的理解都停留在“一種存數(shù)據(jù)的芯片”、“比硬盤快”這樣模糊的層面。當(dāng)真正需要選型、寫驅(qū)動(dòng)或者做故障分析時(shí)面對(duì)數(shù)據(jù)手冊(cè)里滿篇的“Page”、“Block”、“ECC”、“SLC/MLC/TLC”就有點(diǎn)發(fā)懵。最近在幫團(tuán)隊(duì)新人做技術(shù)培訓(xùn)以及處理幾個(gè)關(guān)于T113平臺(tái)NAND Flash適配的棘手問題時(shí)我深感有必要把這塊“老骨頭”重新啃一遍整理出一份既講透原理、又能直接指導(dǎo)實(shí)操的筆記。這份整理的目的很明確它不是一份簡(jiǎn)單的名詞解釋列表而是一份從物理結(jié)構(gòu)到邏輯操作再到實(shí)際開發(fā)中避坑指南的完整地圖。無論你是正在編寫SPI NAND Flash裸機(jī)驅(qū)動(dòng)的嵌入式軟件工程師還是在設(shè)計(jì)NAND Flash控制器的硬件架構(gòu)師亦或是需要為特定主控比如全志T113適配Flash的驅(qū)動(dòng)開發(fā)者我希望這份內(nèi)容能幫你建立起清晰的概念框架知道每一個(gè)操作背后的物理限制是什么為什么控制器要這么設(shè)計(jì)以及當(dāng)讀寫出錯(cuò)時(shí)應(yīng)該從哪個(gè)環(huán)節(jié)開始排查。畢竟理解了“為什么”才能更好地應(yīng)對(duì)“怎么做”。2. NAND Flash核心物理結(jié)構(gòu)解析數(shù)據(jù)是如何被“關(guān)”起來的要理解NAND Flash的所有特性必須從它的物理結(jié)構(gòu)這個(gè)根兒上說起。你可以把它想象成一個(gè)巨大的、立體化的“網(wǎng)格本”但這個(gè)本子有它自己非常獨(dú)特的書寫和擦除規(guī)則。2.1 從晶體管到存儲(chǔ)單元電荷的囚籠NAND Flash的存儲(chǔ)核心是浮柵晶體管Floating Gate Transistor。這和我們常見的MOS管不同它在控制柵Control Gate和溝道之間多了一個(gè)被絕緣層通常是二氧化硅完全包圍的“浮柵”Floating Gate。這個(gè)浮柵與外界沒有任何電氣連接是個(gè)孤島。數(shù)據(jù)的寫入編程本質(zhì)上是向這個(gè)浮柵上注入電荷。通過給控制柵和漏極施加較高的電壓使得溝道中的電子獲得足夠能量穿越第一層絕緣層隧穿氧化層進(jìn)入浮柵這個(gè)過程叫熱電子注入Channel Hot Electron Injection, CHE或** Fowler-Nordheim隧穿F-N Tunneling**。一旦電荷被困在浮柵上即使斷電由于絕緣層的阻擋電荷也能保存很多年典型數(shù)據(jù)保持時(shí)間在10年左右。浮柵上有電荷會(huì)改變晶體管的閾值電壓Vth。我們?cè)谧x取時(shí)施加一個(gè)介于“有電荷”和“無電荷”狀態(tài)閾值電壓之間的參考電壓通過檢測(cè)晶體管是否導(dǎo)通就能判斷存儲(chǔ)的是“1”還是“0”。這里有個(gè)關(guān)鍵點(diǎn)在NAND Flash的世界里擦除Erase才是讓存儲(chǔ)單元回到“1”狀態(tài)的操作即移除浮柵上的電荷。而編程Program是將“1”變成“0”即注入電荷。這和我們習(xí)慣的內(nèi)存“寫0/寫1”概念是反的。擦除操作是通過給襯底P-well加高壓讓浮柵上的電子通過F-N隧穿效應(yīng)被拉出來是整個(gè)Block一起進(jìn)行的。2.2 層級(jí)架構(gòu)Page, Block, Plane 與 Die單個(gè)存儲(chǔ)單元太微小必須組織起來。NAND Flash采用了嚴(yán)格的層級(jí)管理結(jié)構(gòu)這直接決定了所有軟件操作接口的設(shè)計(jì)。頁P(yáng)age最小的讀寫單元。你可以把它理解為這個(gè)網(wǎng)格本里的“一行”。一次讀或?qū)懖僮鞅仨氁砸粋€(gè)Page為單位進(jìn)行?,F(xiàn)在的NAND Flash一個(gè)Page的大小通常是4KB、8KB、16KB甚至更大。每個(gè)Page后面還會(huì)附帶一塊區(qū)域叫做備用區(qū)Spare Area或OOBOut-Of-Band大小通常是主數(shù)據(jù)區(qū)的1/32到1/16比如4KB Page對(duì)應(yīng)128B或256B OOB。OOB是干什么的主要用來存放這個(gè)Page數(shù)據(jù)的ECC校驗(yàn)碼、壞塊標(biāo)記、文件系統(tǒng)元數(shù)據(jù)等關(guān)鍵管理信息。動(dòng)OOB一定要小心里面的數(shù)據(jù)格式是控制器和文件系統(tǒng)約定好的亂寫可能導(dǎo)致數(shù)據(jù)無法識(shí)別。塊Block最小的擦除單元。這是NAND Flash最重要的一個(gè)特性也帶來了“寫前需擦”的麻煩。一個(gè)Block由幾十個(gè)到幾百個(gè)連續(xù)的Page組成例如128個(gè)、256個(gè)、512個(gè)Page。為什么擦除要以Block為單位因?yàn)椴脸僮鞯母邏菏鞘┘釉谡麄€(gè)Block的P-well上的物理上無法只針對(duì)單個(gè)Page或幾個(gè)Page進(jìn)行擦除。這就導(dǎo)致了著名的**“寫入放大Write Amplification”** 問題為了更新一個(gè)Page的數(shù)據(jù)你可能需要把整個(gè)Block里其他有效數(shù)據(jù)先搬走擦除整個(gè)Block再把新老數(shù)據(jù)一起寫回去。面Plane為了提升并行度和性能一個(gè)Die內(nèi)部可能會(huì)劃分成2個(gè)或4個(gè)Plane。每個(gè)Plane有自己獨(dú)立的頁緩沖寄存器Page Register。多平面操作Multi-Plane Operation允許對(duì)兩個(gè)Plane的相同Page地址同時(shí)進(jìn)行編程或讀取有效吞吐量幾乎翻倍這是高性能NAND控制器必須利用的特性。晶圓Die一個(gè)獨(dú)立的NAND Flash芯片核心。一個(gè)物理封裝的芯片內(nèi)部可以封裝多個(gè)Die多晶圓封裝它們可以并行工作進(jìn)一步提升速度和容量。理解這個(gè)層級(jí)關(guān)系至關(guān)重要。當(dāng)你看到數(shù)據(jù)手冊(cè)上寫“Page Size: 4KB, Block Size: 256 Pages, Plane Size: 2 Planes per Die”你腦子里就應(yīng)該立刻浮現(xiàn)出它的物理圖像和操作限制讀/寫按4KB來但想重寫其中任何一部分都得至少以256*4KB1MB為單位進(jìn)行擦除和重整。3. 核心概念深度剖析SLC/MLC/TLC/QLC、接口與ECC掌握了物理結(jié)構(gòu)我們?cè)賮聿鸾鈳讉€(gè)讓初學(xué)者最容易混淆也最影響選型和開發(fā)的核心概念。3.1 存儲(chǔ)單元類型在密度與可靠性之間走鋼絲這是根據(jù)每個(gè)存儲(chǔ)單元能存放的比特?cái)?shù)來劃分的直接決定了芯片的容量、成本、速度和壽命。SLCSingle-Level Cell每個(gè)單元存1比特2種狀態(tài)0或1。閾值電壓分布間隔寬狀態(tài)判斷非常容易。因此它具有極高的可靠性、超快的編程/讀取速度、超長(zhǎng)的擦寫壽命通常10萬次以上。但代價(jià)是成本最高容量密度最低?,F(xiàn)在主要用于對(duì)可靠性、速度和壽命有極端要求的工業(yè)、軍事、企業(yè)級(jí)SSD緩存等場(chǎng)景。MLCMulti-Level Cell每個(gè)單元存2比特4種狀態(tài)00, 01, 10, 11。需要在更窄的電壓窗口內(nèi)區(qū)分4個(gè)狀態(tài)對(duì)電路精度和噪聲控制要求更高。速度、壽命典型3000-10000次和可靠性相比SLC有下降但成本更低容量翻倍。曾是消費(fèi)級(jí)SSD和高端U盤的主流。TLCTriple-Level Cell每個(gè)單元存3比特8種狀態(tài)。電壓狀態(tài)更加擁擠需要更復(fù)雜的讀取算法如讀-重試和更強(qiáng)的ECC來保證數(shù)據(jù)正確性。擦寫壽命進(jìn)一步降低典型500-1500次速度也更慢。但容量密度比MLC高50%成本更低是目前消費(fèi)級(jí)固態(tài)硬盤和大部分大容量U盤、存儲(chǔ)卡的絕對(duì)主力。QLCQuad-Level Cell每個(gè)單元存4比特16種狀態(tài)。這是目前推向市場(chǎng)的民用極限。電壓狀態(tài)極其密集導(dǎo)致寫入速度慢尤其是從隨機(jī)寫入表現(xiàn)看、壽命短典型100-300次、對(duì)ECC依賴極強(qiáng)。它的主戰(zhàn)場(chǎng)是大容量、低成本、以順序?qū)懭霝橹鞯慕€存儲(chǔ)或消費(fèi)級(jí)大容量SSD。選型心得不要只看容量和價(jià)格。對(duì)于頻繁寫入、需要快速響應(yīng)的系統(tǒng)如工業(yè)設(shè)備日志記錄、嵌入式數(shù)據(jù)庫即使用TLC容量夠也強(qiáng)烈建議考慮MLC甚至SLC否則后期維護(hù)和數(shù)據(jù)丟失的風(fēng)險(xiǎn)會(huì)成倍增加。對(duì)于幾乎只讀或極少更新的固件存儲(chǔ)QLC或許是個(gè)低成本大容量的選擇。3.2 接口類型并行與串行的演進(jìn)異步并行接口Async Parallel經(jīng)典的老式接口有獨(dú)立的I/O數(shù)據(jù)線x8或x16、地址線、控制線CE#, CLE, ALE, WE#, RE#, R/B#等。優(yōu)點(diǎn)是協(xié)議簡(jiǎn)單容易用GPIO模擬實(shí)現(xiàn)裸機(jī)驅(qū)動(dòng)這也是很多學(xué)習(xí)者的起點(diǎn)。缺點(diǎn)是引腳多PCB走線復(fù)雜速度有瓶頸?,F(xiàn)在主要見于低端、小容量的SLC/MLC芯片。ONFIOpen NAND Flash Interface與Toggle這是兩種主流的高速并行接口標(biāo)準(zhǔn)。它們引入了源同步時(shí)鐘DQS信號(hào)在時(shí)鐘邊沿采樣數(shù)據(jù)速率可以達(dá)到200MT/s、400MT/s甚至更高。ONFI是英特爾等公司推動(dòng)的開放標(biāo)準(zhǔn)而Toggle是三星、東芝等廠商的標(biāo)準(zhǔn)。兩者互不兼容控制器需要分別支持。它們是目前高性能SSD內(nèi)部片間通信的主流方式。SPI NAND Flash近年來在嵌入式市場(chǎng)異軍突起。它使用簡(jiǎn)單的SPISerial Peripheral Interface總線通常只需4根線CS#, CLK, DI, DO或6根線加上WP#和HOLD#。優(yōu)勢(shì)極其明顯引腳數(shù)極少封裝小常見8-SOP甚至WSON-8布線簡(jiǎn)單可以直接與任何帶SPI控制器的主控連接無需專用的NAND控制器。這大大降低了硬件設(shè)計(jì)和系統(tǒng)集成的門檻。代價(jià)是性能受限于SPI時(shí)鐘頻率初期幾十MHz現(xiàn)在有高速模式能到200MHz以上峰值吞吐量遠(yuǎn)低于并行接口。但對(duì)于很多成本敏感、性能要求不極端的IoT設(shè)備、消費(fèi)電子、小容量嵌入式存儲(chǔ)來說SPI NAND是一個(gè)完美的平衡選擇。編寫SPI NAND裸機(jī)驅(qū)動(dòng)核心就是理解其特定的命令集Read ID, Read Page, Program Page, Erase Block, Read Status等和如何通過SPI總線發(fā)送命令、地址和數(shù)據(jù)。3.3 錯(cuò)誤校驗(yàn)與糾錯(cuò)ECC數(shù)據(jù)的守護(hù)神隨著存儲(chǔ)單元從SLC向QLC演進(jìn)電荷量微小的差異、絕緣層老化導(dǎo)致的電荷泄漏、讀寫干擾等都會(huì)引起閾值電壓漂移導(dǎo)致讀取時(shí)誤判產(chǎn)生位錯(cuò)誤Bit Error。ECC就是用來檢測(cè)和糾正這些錯(cuò)誤的。必要性對(duì)于TLC/QLC出廠就可能存在一定比例的位錯(cuò)誤隨著擦寫次數(shù)增加錯(cuò)誤率RBER會(huì)上升。沒有ECC數(shù)據(jù)根本不可靠。ECC強(qiáng)度通常用能糾正的比特?cái)?shù)來表示如“每512字節(jié)糾正4比特錯(cuò)誤”。ECC強(qiáng)度必須與NAND Flash的原始誤碼率UBER要求匹配。TLC通常需要40比特/1KB以上的ECCQLC要求更高。實(shí)現(xiàn)位置片上ECC一些SPI NAND Flash芯片內(nèi)部集成了簡(jiǎn)單的ECC引擎如1-bit/512B對(duì)于要求不高的應(yīng)用可以省去外部ECC計(jì)算。控制器ECC更常見的做法是在NAND Flash控制器無論是SoC內(nèi)置的還是獨(dú)立的SSD主控中實(shí)現(xiàn)強(qiáng)大的ECC算法如BCH碼或LDPC碼。控制器在寫入數(shù)據(jù)時(shí)計(jì)算ECC碼存入OOB讀取時(shí)再取出進(jìn)行校驗(yàn)和糾錯(cuò)。這就是為什么裸讀NAND Flash的OOB區(qū)域你會(huì)看到一堆非零數(shù)據(jù)的原因。實(shí)操注意當(dāng)你進(jìn)行裸機(jī)驅(qū)動(dòng)開發(fā)時(shí)如果主控沒有硬件ECC引擎你有兩個(gè)選擇1選用自帶ECC的SPI NAND芯片2在軟件中實(shí)現(xiàn)ECC算法如BCH但這會(huì)消耗大量CPU資源影響性能。在為現(xiàn)有主控適配新Flash時(shí)比如給全志T113適配一款新的NAND你必須確認(rèn)主控的ECC能力支持多強(qiáng)的BCH或LDPC是否匹配新Flash的需求。如果不匹配要么換Flash要么面臨無法糾正的數(shù)據(jù)錯(cuò)誤風(fēng)險(xiǎn)。這是適配工作中最容易踩坑的地方之一。4. NAND Flash控制器NFC的角色與工作原理NAND Flash芯片本身是一個(gè)“笨拙”的物理器件它需要一個(gè)大管家來管理其復(fù)雜的物理特性并向主機(jī)系統(tǒng)提供一個(gè)簡(jiǎn)潔、可靠的塊設(shè)備接口。這個(gè)管家就是NAND Flash控制器。4.1 控制器的核心職責(zé)協(xié)議轉(zhuǎn)換與命令調(diào)度將主機(jī)發(fā)來的標(biāo)準(zhǔn)存儲(chǔ)訪問請(qǐng)求如ATA/SCSI命令或簡(jiǎn)單的讀/寫扇區(qū)請(qǐng)求翻譯成NAND Flash芯片能聽懂的具體命令序列Command, Address, Data并處理復(fù)雜的時(shí)序。壞塊管理Bad Block Management, BBMNAND Flash出廠時(shí)就有一定比例的壞塊在使用過程中還會(huì)產(chǎn)生新的壞塊??刂破鞅仨毥⒉⒕S護(hù)一個(gè)壞塊表BBT將邏輯地址映射到物理地址時(shí)主動(dòng)跳過這些壞塊。BBT本身必須存儲(chǔ)在NAND的某個(gè)可靠位置通常是在幾個(gè)固定好的Block的OOB里并在上電時(shí)加載。磨損均衡Wear Leveling, WL為了解決NAND Flash每個(gè)Block擦寫次數(shù)有限的問題控制器必須確保數(shù)據(jù)的寫入均勻地分布到所有物理Block上避免某些Block被“寫死”而其他Block還是新的。動(dòng)態(tài)磨損均衡算法是控制器的核心算法之一。垃圾回收Garbage Collection, GC由于NAND Flash“寫前需擦”且擦除單位大當(dāng)Block中只有部分?jǐn)?shù)據(jù)無效時(shí)空間無法直接復(fù)用。垃圾回收進(jìn)程負(fù)責(zé)將Block中仍然有效的數(shù)據(jù)搬移到新的位置然后擦除這個(gè)Block使其變?yōu)榭捎玫摹翱臻e塊”。這個(gè)過程會(huì)引發(fā)寫入放大。ECC校驗(yàn)與糾錯(cuò)如前所述在寫入時(shí)生成ECC碼在讀取時(shí)進(jìn)行校驗(yàn)和糾錯(cuò)。這是保證數(shù)據(jù)可靠性的基石。讀寫緩存與緩沖利用RAM緩存數(shù)據(jù)優(yōu)化小數(shù)據(jù)寫入合并成整頁寫入提升性能。4.2 與主控的集成方式集成在SoC內(nèi)部如很多嵌入式MPU全志、瑞芯微、NXP i.MX系列等都內(nèi)置了NAND Flash控制器。開發(fā)者通常只需要配置好時(shí)序參數(shù)、頁大小、OOB布局、ECC強(qiáng)度等驅(qū)動(dòng)起來相對(duì)方便。為T113適配新NAND Flash主要工作就是根據(jù)新Flash的數(shù)據(jù)手冊(cè)正確配置這些寄存器參數(shù)。獨(dú)立的SSD主控芯片在固態(tài)硬盤中它是一個(gè)功能極其復(fù)雜的獨(dú)立芯片集成了更高級(jí)的算法如更智能的WL、GCLDPC糾錯(cuò)DRAM緩存接口等。軟件模擬FMC/FSMC在一些沒有專用NFC的STM32等MCU上可以通過靈活的靜態(tài)存儲(chǔ)器控制器FMC/FSMC用GPIO模擬NAND的時(shí)序來驅(qū)動(dòng)但這通常性能較低且所有壞塊管理、ECC都需要軟件實(shí)現(xiàn)復(fù)雜度高。開發(fā)提示當(dāng)你使用SoC內(nèi)置的NFC時(shí)數(shù)據(jù)手冊(cè)里關(guān)于NAND初始化的章節(jié)和NFC相關(guān)的寄存器描述是關(guān)鍵。重點(diǎn)關(guān)注時(shí)序參數(shù)寄存器如TCLS, TALS, TWR, TRR等這些值需要從Flash數(shù)據(jù)手冊(cè)的AC Characteristics表中計(jì)算得出、塊/頁大小配置寄存器、ECC使能與模式寄存器。配置錯(cuò)誤會(huì)導(dǎo)致讀寫不穩(wěn)定甚至無法識(shí)別ID。5. 實(shí)戰(zhàn)SPI NAND Flash裸機(jī)驅(qū)動(dòng)開發(fā)要點(diǎn)假設(shè)我們要在一塊STM32H7的板子上驅(qū)動(dòng)一顆Winbond的SPI NAND Flash如W25N01GV。這是一個(gè)典型的裸機(jī)驅(qū)動(dòng)場(chǎng)景。5.1 硬件連接與初始化連接非常簡(jiǎn)單將Flash的/CS,CLK,DI(IO0),DO(IO1)分別連接到MCU的任意一個(gè)SPI外設(shè)的NSS, SCK, MOSI, MISO引腳即可。WP#和HOLD#引腳如果不用可以上拉到VCC。初始化順序SPI外設(shè)初始化配置為模式0或模式3CPOL0/1, CPHA0/1根據(jù)Flash數(shù)據(jù)手冊(cè)推薦設(shè)置。時(shí)鐘頻率初始可以設(shè)低一點(diǎn)如1MHz確保通信穩(wěn)定。讀取JEDEC ID發(fā)送0x9F命令連續(xù)讀取3-4個(gè)字節(jié)。第一個(gè)字節(jié)是制造商ID如Winbond是0xEF后續(xù)是設(shè)備ID。通過這個(gè)ID你可以在代碼中匹配已知的Flash參數(shù)表頁大小、塊大小、總?cè)萘康取z查狀態(tài)寄存器上電后發(fā)送0x0F讀狀態(tài)寄存器或0x05讀狀態(tài)寄存器-1。確保Flash不處于寫保護(hù)狀態(tài)或之前的編程/擦除操作已完成BUSY位為0。配置內(nèi)部寄存器可選一些SPI NAND有可配置的內(nèi)部寄存器比如ECC使能、緩存模式選擇等。例如W25N01GV可以通過0x06寫使能后跟0x1F寫特征寄存器來配置。務(wù)必仔細(xì)閱讀數(shù)據(jù)手冊(cè)確認(rèn)默認(rèn)配置是否滿足需求尤其是ECC是否開啟。5.2 實(shí)現(xiàn)基本操作函數(shù)驅(qū)動(dòng)層需要實(shí)現(xiàn)以下幾個(gè)最基礎(chǔ)的函數(shù)spi_nand_read_id(): 如上所述。spi_nand_read_page(page_addr, *buffer, oob_buffer): 發(fā)送0x13讀頁加載命令和24位地址對(duì)于1Gb芯片地址為[PA17:PA0]然后發(fā)送0x03讀數(shù)據(jù)命令開始從SPI連續(xù)讀取數(shù)據(jù)。注意很多SPI NAND的讀操作是“加載到內(nèi)部緩存-從緩存讀出”兩步。地址是針對(duì)Page的。讀取時(shí)要連續(xù)讀完整個(gè)PageOOB的數(shù)據(jù)。spi_nand_program_page(page_addr, *buffer, oob_buffer): 先0x06寫使能然后0x02寫頁加載命令和地址接著連續(xù)寫入數(shù)據(jù)最后發(fā)送0x10寫頁執(zhí)行命令。關(guān)鍵點(diǎn)寫頁加載只是把數(shù)據(jù)放到內(nèi)部頁緩存0x10才是真正啟動(dòng)編程注入電荷操作。執(zhí)行0x10后必須輪詢狀態(tài)寄存器直到BUSY位清零。spi_nand_erase_block(block_addr): 先0x06寫使能然后發(fā)送0xD8塊擦除命令和地址注意這里是塊地址需要根據(jù)芯片架構(gòu)將塊地址轉(zhuǎn)換為對(duì)應(yīng)的頁地址起始最后輪詢狀態(tài)。擦除是最耗時(shí)的操作通常需要幾毫秒到十幾毫秒。spi_nand_read_status()/spi_nand_wait_ready(): 輪詢狀態(tài)寄存器檢查BUSY位和FAIL位編程/擦除失敗標(biāo)志。5.3 壞塊管理與OOB處理這是裸機(jī)驅(qū)動(dòng)中最容易出錯(cuò)的部分。壞塊檢測(cè)SPI NAND通常在出廠時(shí)會(huì)在每個(gè)Block的第一個(gè)或第二個(gè)Page的OOB區(qū)域的特定字節(jié)例如第0字節(jié)做標(biāo)記不是0xFF比如0x00或0xF0來表示這是一個(gè)壞塊。你的驅(qū)動(dòng)在初始化時(shí)應(yīng)該掃描所有Block的這個(gè)標(biāo)記建立壞塊表BBT保存在RAM中。注意不同廠商的標(biāo)記位置和值可能不同務(wù)必查數(shù)據(jù)手冊(cè)。OOB布局你的文件系統(tǒng)如UBIFS, YAFFS2或FTL層會(huì)定義OOB的用途。典型布局可能是前幾個(gè)字節(jié)放文件系統(tǒng)標(biāo)記中間64字節(jié)放ECC數(shù)據(jù)最后幾個(gè)字節(jié)放壞塊標(biāo)記。在read_page和program_page時(shí)你需要正確處理OOB緩沖區(qū)。一個(gè)黃金法則除非你明確知道你在做什么否則不要隨意覆蓋OOB中已有的數(shù)據(jù)。ECC處理如果Flash芯片內(nèi)部ECC已使能且能滿足你的需求那么你讀寫數(shù)據(jù)時(shí)可以不用管ECC。但如果你需要更強(qiáng)的ECC或者芯片沒有ECC那么你需要在program_page前計(jì)算好ECC碼填入OOB的指定位置在read_page后取出ECC碼進(jìn)行校驗(yàn)和糾錯(cuò)。這通常需要軟件BCH庫的支持計(jì)算量較大。避坑指南時(shí)序問題SPI時(shí)鐘頻率不要一開始就設(shè)到最高。從低速開始測(cè)試穩(wěn)定后再逐步提高。確保/CS的建立和保持時(shí)間滿足數(shù)據(jù)手冊(cè)要求。狀態(tài)輪詢?cè)趫?zhí)行編程或擦除命令后必須等待操作完成。簡(jiǎn)單的while(spi_nand_read_status() BUSY_MASK);可能會(huì)因?yàn)槌瑫r(shí)機(jī)制缺失而導(dǎo)致死循環(huán)。一定要加超時(shí)判斷。緩存未就緒有些操作如連續(xù)讀需要等待內(nèi)部緩存準(zhǔn)備好。在發(fā)送讀數(shù)據(jù)命令0x03前可能需要先發(fā)送一個(gè)虛字節(jié)dummy byte或等待一小段固定時(shí)間。寫保護(hù)確保WP#引腳電平正確。意外拉低可能導(dǎo)致寫操作被拒絕。6. 為特定主控適配NAND Flash以全志T113為例為像全志T113這樣的SoC適配一款新的NAND Flash是嵌入式Linux BSP開發(fā)中的常見任務(wù)。這不僅僅是驅(qū)動(dòng)一個(gè)芯片而是讓SoC內(nèi)置的NAND Flash控制器認(rèn)識(shí)并正確操作這顆新的Flash。6.1 適配工作的核心步驟獲取關(guān)鍵參數(shù)拿到新NAND Flash的數(shù)據(jù)手冊(cè)提取以下信息設(shè)備ID通過Read ID命令返回的完整字節(jié)序列。頁大小Page Size如4096字節(jié)。塊大小Block Size頁大小 * 每塊頁數(shù)如4096*64256KB。OOB大小Spare Size如128字節(jié)或256字節(jié)???cè)萘坑?jì)算總塊數(shù)。時(shí)序參數(shù)關(guān)鍵的時(shí)間參數(shù)如tWC寫周期時(shí)間、tRC讀周期時(shí)間、tREA讀訪問時(shí)間、tR頁讀取時(shí)間、tPROG頁編程時(shí)間、tBERS塊擦除時(shí)間。這些值用于計(jì)算NFC的時(shí)鐘分頻和等待周期。ECC要求芯片的ECC比特/頁要求是多少例如要求每1KB數(shù)據(jù)提供40比特ECC能力。壞塊標(biāo)記位置出廠壞塊標(biāo)記在OOB中的哪個(gè)字節(jié)通常是第0個(gè)或第1個(gè)字節(jié)。修改內(nèi)核設(shè)備樹DTS這是最主要的工作。全志平臺(tái)NAND Flash的配置通常在arch/arm/boot/dts/sunxi/目錄下的某個(gè)DTSI或DTS文件中。添加Flash節(jié)點(diǎn)在nfc節(jié)點(diǎn)下添加或修改一個(gè)nand0子節(jié)點(diǎn)。配置參數(shù)關(guān)鍵屬性包括reg 0; // CS片選 allwinner,rb 0; // Ready/Busy引腳 nand-ecc-mode hw; // 使用硬件ECC nand-ecc-strength 40; // ECC強(qiáng)度根據(jù)Flash要求設(shè)置 nand-ecc-step-size 1024; // ECC步長(zhǎng)通常為512或1024 nand-on-flash-bbt; // 使用Flash上的壞塊表 #address-cells 1; #size-cells 1;分區(qū)表在nand0節(jié)點(diǎn)下定義partitions子節(jié)點(diǎn)劃分出boot、env、rootfs等分區(qū)。配置NFC驅(qū)動(dòng)參數(shù)可能需要修改NAND控制器驅(qū)動(dòng)源碼通常是drivers/mtd/nand/raw/下的相關(guān)驅(qū)動(dòng)全志可能有自己的平臺(tái)驅(qū)動(dòng)。重點(diǎn)是確保驅(qū)動(dòng)能正確識(shí)別你的Flash ID并為其分配合適的nand_chip結(jié)構(gòu)和nand_controller_ops操作集。如果Flash的指令集或特殊功能如QPI模式與標(biāo)準(zhǔn)不同可能需要添加新的nand_flash_dev結(jié)構(gòu)體到驅(qū)動(dòng)列表中。調(diào)整時(shí)序配置在驅(qū)動(dòng)或DTS中可能需要配置NFC的時(shí)鐘頻率、采樣相位等以匹配Flash的時(shí)序要求。這可能需要查閱T113的用戶手冊(cè)中關(guān)于NFC時(shí)鐘配置的章節(jié)。6.2 常見適配問題與排查問題系統(tǒng)無法識(shí)別NAND Flash啟動(dòng)時(shí)卡住或報(bào)錯(cuò)。排查檢查硬件連接數(shù)據(jù)線、控制線是否接對(duì)上拉電阻是否合適用示波器或邏輯分析儀抓取上電后NFC嘗試讀取ID時(shí)的波形。看CE#,CLE,ALE,WE#,RE#信號(hào)是否正常數(shù)據(jù)線上是否有ID數(shù)據(jù)返回。這是最直接的診斷方法。檢查DTS中nfc節(jié)點(diǎn)的時(shí)鐘和引腳復(fù)用pinctrl配置是否正確。確認(rèn)內(nèi)核配置make menuconfig中已啟用MTD、NAND以及全志平臺(tái)的NAND驅(qū)動(dòng)如CONFIG_MTD_NAND_SUNXI。問題能識(shí)別ID但讀寫數(shù)據(jù)出錯(cuò)ECC糾錯(cuò)失敗頻繁。排查首要懷疑ECC強(qiáng)度不匹配檢查DTS中nand-ecc-strength和nand-ecc-step-size是否嚴(yán)格按照新Flash的數(shù)據(jù)手冊(cè)要求設(shè)置。T113的NFC通常支持BCH編碼強(qiáng)度可選。如果Flash要求每1KB 40bit ECC你配置成了24bit那肯定出錯(cuò)。檢查時(shí)序如果時(shí)序配置過于激進(jìn)時(shí)鐘太快等待周期太短可能導(dǎo)致數(shù)據(jù)采樣不穩(wěn)定。嘗試降低NFC時(shí)鐘頻率或在DTS中增加nand-wr-hold-ns、nand-wr-delay-ns等時(shí)序參數(shù)如果驅(qū)動(dòng)支持。檢查OOB布局內(nèi)核驅(qū)動(dòng)對(duì)OOB的使用有默認(rèn)布局。如果Flash出廠壞塊標(biāo)記位置或文件系統(tǒng)要求的ECC存放位置與驅(qū)動(dòng)默認(rèn)不符需要修改驅(qū)動(dòng)的OOB布局定義或使用nand-flash屬性指定。問題寫入速度極慢。排查檢查是否啟用了硬件ECC。軟件ECC會(huì)嚴(yán)重拖慢速度。檢查驅(qū)動(dòng)是否支持并啟用了NAND_ECC_MAXIMIZE等優(yōu)化選項(xiàng)。確認(rèn)Flash本身的速度等級(jí)。不同型號(hào)的編程時(shí)間tPROG差異很大。適配心得永遠(yuǎn)不要假設(shè)。一切以數(shù)據(jù)手冊(cè)和實(shí)際波形為準(zhǔn)。準(zhǔn)備一個(gè)邏輯分析儀是進(jìn)行NAND Flash調(diào)試的必備工具。先讓最基本的ID讀取和頁讀寫工作起來再逐步使能ECC、壞塊管理等高級(jí)功能。每次修改一個(gè)變量并做好測(cè)試記錄。全志社區(qū)和Linux MTD郵件列表是尋找類似適配案例和尋求幫助的好地方。