與參數(shù)存儲(chǔ)設(shè)計(jì)實(shí)戰(zhàn)解析)
做嵌入式這么多年第一次認(rèn)真用瑞薩RZ/N2L做量產(chǎn)項(xiàng)目遇到的第一道坎就是啟動(dòng)方式。原先方案是SPI NOR存固件、I2C EEPROM存參數(shù)板子改版時(shí)客戶要求少放器件干脆把參數(shù)也塞進(jìn)同一顆QSPI Flash里。這個(gè)決定看起來省事實(shí)際做下來牽扯到啟動(dòng)模式配置、BootROM對(duì)Flash的初始化時(shí)序、RASC生成的驅(qū)動(dòng)代碼以及參數(shù)存儲(chǔ)的磨損均衡和掉電保護(hù)一環(huán)扣一環(huán)每一環(huán)都有值得記錄的坑。這篇文章就是這次實(shí)戰(zhàn)的完整記錄核心圍繞兩件事RZ/N2L怎么從QSPI Flash啟動(dòng)以及參數(shù)存儲(chǔ)怎么做才能扛得住長(zhǎng)時(shí)間運(yùn)行。內(nèi)容包括硬件接法、RASC工程配置、Keil環(huán)境搭建、底層驅(qū)動(dòng)移植、參數(shù)區(qū)規(guī)劃和常見故障排查適合準(zhǔn)備用RZ/N2L做產(chǎn)品、或者想把代碼和參數(shù)統(tǒng)一放到外部Flash上的朋友。如果你用過瑞薩RA6M5或者熟悉e2 studio那套操作上手RZ/N2L會(huì)更輕松因?yàn)榕渲盟悸肥窍嗤ǖ牡讓蛹?xì)節(jié)差異不小建議還是從頭過一遍。1. 為什么我選了QSPI啟動(dòng)需求拆解與方案對(duì)比1.1 啟動(dòng)方式選型先把硬件資源算清楚RZ/N2L支持多種啟動(dòng)介質(zhì)包括QSPI Flash、SD卡、串行接口等。項(xiàng)目里選擇QSPI啟動(dòng)不是拍腦袋決定的主要從三個(gè)方面考量。第一是引腳開銷。RZ/N2L作為MPU級(jí)別的芯片功能外設(shè)多引腳本來就不寬裕。QSPI接口只需要SCLK、CS、IO0到IO3六根信號(hào)線就能接一顆大容量NOR Flash而并行NOR需要幾十根線在小型化板卡上完全不現(xiàn)實(shí)。第二是容量和成本。QSPI NOR Flash的單價(jià)和容量比EEPROM友好太多一顆16MB的W25Q128或者國(guó)產(chǎn)GD25Q128價(jià)格只有同容量并行Flash的零頭空間還綽綽有余。固件、文件系統(tǒng)、參數(shù)區(qū)可以全放進(jìn)去產(chǎn)品后續(xù)要加OTA升級(jí)也留得住空間。第三是XIP能力。QSPI控制器支持內(nèi)存映射模式Flash可以被映射到CPU的地址空間代碼可以直接在Flash上執(zhí)行不需要上電后先搬運(yùn)到SRAM再跑。這一點(diǎn)對(duì)啟動(dòng)速度幫助很大BootROM把QSPI初始化好之后CPU取指直接走內(nèi)存映射通道省掉了先加載后跳轉(zhuǎn)的那段等待。1.2 參數(shù)存儲(chǔ)選擇QSPI Flash到底合不合適很多人覺得參數(shù)存儲(chǔ)應(yīng)該老老實(shí)實(shí)用EEPROM擔(dān)心Flash磨損和掉電丟數(shù)據(jù)。這個(gè)顧慮在十年前有道理但放到現(xiàn)在的應(yīng)用場(chǎng)景只要設(shè)計(jì)合理QSPI NOR Flash做參數(shù)存儲(chǔ)完全夠用甚至更合適。NOR Flash的特點(diǎn)是按扇區(qū)擦除、按頁寫入擦除操作會(huì)把扇區(qū)全部置為0xFF寫入只能把1寫成0。這個(gè)特性決定了參數(shù)存儲(chǔ)不能像EEPROM那樣原地覆蓋必須做雙備份或者輪換寫入。但換個(gè)角度想正因?yàn)椴翆憜挝淮蠓炊浦闳ピO(shè)計(jì)合理的寫入策略最終的效果是數(shù)據(jù)可靠性比EEPROM方案更高因?yàn)槊恳环輸?shù)據(jù)都會(huì)被校驗(yàn)、被備份、被輪換。參數(shù)存儲(chǔ)對(duì)Flash的另一個(gè)要求是掉電保護(hù)。實(shí)際產(chǎn)品中參數(shù)寫入瞬間掉電是常態(tài)如果只寫一個(gè)區(qū)域斷電后數(shù)據(jù)可能半新半舊。QSPI方案配合雙Slot設(shè)計(jì)和CRC校驗(yàn)可以保證掉電后要么拿到舊數(shù)據(jù)要么拿到新數(shù)據(jù)不會(huì)出現(xiàn)中間態(tài)。2. 硬件設(shè)計(jì)與啟動(dòng)引腳配置先把底子打牢2.1 啟動(dòng)模式引腳和QSPI信號(hào)一個(gè)都不能接錯(cuò)RZ/N2L的啟動(dòng)介質(zhì)選擇由啟動(dòng)模式引腳的上下拉電平?jīng)Q定。具體是MD_BOOT還是其他命名一定要以芯片數(shù)據(jù)手冊(cè)和硬件設(shè)計(jì)手冊(cè)為準(zhǔn)原理圖階段就要和FAE確認(rèn)清楚。我這次就吃了個(gè)虧以為啟動(dòng)引腳默認(rèn)拉低就是QSPI啟動(dòng)結(jié)果打樣回來發(fā)現(xiàn)默認(rèn)電平對(duì)應(yīng)的是串行接口模式BootROM壓根沒去初始化QSPI調(diào)試器連上后CPU直接跑飛。啟動(dòng)模式引腳的電平需要在復(fù)位釋放前穩(wěn)定所以不要圖省事直接接固定電平最好預(yù)留0歐電阻或者跳線方便量產(chǎn)階段切換啟動(dòng)介質(zhì)。比如調(diào)試時(shí)用串行接口模式燒寫B(tài)ootloader測(cè)試完成后切回QSPI啟動(dòng)這個(gè)跳線能避免反復(fù)焊Flash。QSPI信號(hào)線的接法也要注意。WP和HOLD引腳在四線模式下會(huì)復(fù)用為IO2和IO3如果不用四線模式這兩個(gè)引腳絕對(duì)不能懸空。懸空狀態(tài)下Flash在復(fù)位或上電瞬間可能誤進(jìn)入HOLD狀態(tài)導(dǎo)致通信一直掛起。正確的做法是分別通過10K電阻上拉到VCC或下拉到GND具體選擇取決于Flash芯片手冊(cè)。2.2 信號(hào)完整性和電源處理高頻啟動(dòng)別翻車QSPI工作在幾十MHz到上百M(fèi)Hz信號(hào)完整性比SPI模式要求高。PCB上QSPI四根數(shù)據(jù)線盡量等長(zhǎng)誤差控制在5mm以內(nèi)SCLK和數(shù)據(jù)線之間不要差太多否則高速讀時(shí)序會(huì)出現(xiàn)采樣點(diǎn)偏移。走線避免跨分割底層如果有完整地平面信號(hào)質(zhì)量會(huì)明顯好過跨區(qū)走線。電源方面Flash的VCC引腳要就近放一個(gè)0.1uF陶瓷電容如果板上有空間再并一個(gè)1uF電容。QSPI Flash對(duì)電源紋波不算特別敏感但如果Flash供電和主控?cái)?shù)字電源共用一條細(xì)線擦除瞬間的大電流沖擊可能引起波形毛刺嚴(yán)重時(shí)造成通信錯(cuò)誤。還有一個(gè)小細(xì)節(jié)RZ/N2L的QSPI控制器時(shí)鐘源來自內(nèi)部PLL還是外部晶振決定了啟動(dòng)時(shí)Flash最高能跑多快。BootROM初始化QSPI時(shí)用的是系統(tǒng)默認(rèn)時(shí)鐘如果你的外部Flash最高支持133MHz但BootROM階段只跑到幾十MHz這沒關(guān)系只要應(yīng)用代碼里重新初始化QSPI把它切換到高速模式即可。啟動(dòng)階段不用追求極限速度穩(wěn)定才是第一位的。3. RASCKeil環(huán)境搭建從配置到第一行代碼3.1 RASC創(chuàng)建工程先選對(duì)芯片型號(hào)和FSP版本瑞薩的圖形化配置工具RASCRenesas Advanced Smart Configurator是RZ/N2L開發(fā)的主入口和e2 studio里的配置界面是同一套東西。如果你是從RA6M5這類RA系列轉(zhuǎn)過來會(huì)發(fā)現(xiàn)操作方式很熟悉但RZ/N2L屬于RZ系列FSP套件版本和RA系列不完全一樣生成的代碼結(jié)構(gòu)也有差異。打開RASC后第一步是選擇具體芯片型號(hào)。RZ/N2L下面還有多個(gè)子型號(hào)區(qū)別主要在封裝、內(nèi)置SRAM大小、外設(shè)數(shù)量上。選錯(cuò)型號(hào)會(huì)導(dǎo)致引腳分配不對(duì)后期改起來非常痛苦。我建議在原理圖定稿后就把型號(hào)確認(rèn)好RASC建工程時(shí)直接鎖定。創(chuàng)建工程時(shí)還需要選目標(biāo)編譯器。RASC支持生成Keil、IAR、GCC等工具鏈的工程文件。我們用的是Keil MDK所以在RASC里選擇對(duì)應(yīng)的工具鏈生成后直接用MDK打開工程不需要手動(dòng)移植。FSP版本方面不要盲目追求最新版。有些新版本對(duì)老型號(hào)的支持存在回歸問題我踩過一次升級(jí)FSP后QSPI的片選時(shí)序配置項(xiàng)位置變了生成代碼和手冊(cè)對(duì)不上。穩(wěn)妥做法是選擇當(dāng)前型號(hào)的長(zhǎng)期支持版本至少等一個(gè)版本發(fā)布三個(gè)月后再考慮升級(jí)。3.2 QSPI外設(shè)配置重點(diǎn)看時(shí)序參數(shù)在RASC的Stacks配置頁里添加QSPI驅(qū)動(dòng)配置界面會(huì)列出一堆選項(xiàng)包括Bus Width總線寬度、Command Sequence命令序列、時(shí)鐘分頻等。這些參數(shù)不是隨便填的要和實(shí)際Flash芯片的指令集對(duì)應(yīng)上。例如W25Q128JV的頁寫指令有0x02單線和0x32四線頁寫部分廠商是0x32擦除指令有扇區(qū)擦除0x20、塊擦除0xD8。RASC配置界面里會(huì)把對(duì)應(yīng)的操作類型列出你需要把Flash手冊(cè)上的指令碼填進(jìn)去。填錯(cuò)了不會(huì)立刻報(bào)錯(cuò)但讀寫數(shù)據(jù)會(huì)出現(xiàn)詭異性錯(cuò)誤。時(shí)鐘分頻配置要略留余量。如果Flash標(biāo)稱最高支持104MHzQSPI時(shí)鐘設(shè)置為80MHz到90MHz比較穩(wěn)妥這個(gè)頻率貼上限跑長(zhǎng)時(shí)間工作加上溫度變化時(shí)序余量不足會(huì)導(dǎo)致偶發(fā)讀錯(cuò)。不要為了跑分好看把頻率拉到極限嵌入式產(chǎn)品穩(wěn)定壓倒一切。RASC生成代碼后QSPI驅(qū)動(dòng)的初始化、讀寫接口都會(huì)被封裝好實(shí)際使用時(shí)通常從R_QSPI_Open開始然后通過R_QSPI_Read、R_QSPI_Write、R_QSPI_Erase等接口操作Flash。具體函數(shù)名以生成代碼為準(zhǔn)但調(diào)用邏輯和標(biāo)準(zhǔn)FSP驅(qū)動(dòng)是一致的熟悉一家以后換型號(hào)很容易上手。3.3 Keil工程導(dǎo)入與下載算法配置RASC生成完工程用Keil MDK打開后第一個(gè)要處理的問題是下載算法。默認(rèn)工程里一般只包含片內(nèi)Flash的編程算法外部QSPI Flash不在其中直接點(diǎn)下載會(huì)提示找不到算法。Keil里為外部Flash添加下載算法有兩種方式。一種是選擇Keil安裝目錄下現(xiàn)成的Flash算法文件比如針對(duì)W25Q系列通常能找到對(duì)應(yīng)的.FLM文件另一種是通過調(diào)試器的QSPI Flash Loader實(shí)現(xiàn)J-Link的J-Flash軟件支持大多數(shù)常見QSPI Flash型號(hào)配置好之后可以在線燒寫。如果芯片的QSPI Flash型號(hào)比較冷門找不到現(xiàn)成下載算法還有一個(gè)通用辦法先用串行接口啟動(dòng)模式通過UART把一段一級(jí)Bootloader燒到片內(nèi)SRAM再由Bootloader通過QSPI驅(qū)動(dòng)把完整的Bootloader和應(yīng)用固件寫到外部Flash。這個(gè)流程雖然繞但在量產(chǎn)階段很實(shí)用尤其是調(diào)試器算法兼容性出問題時(shí)。下載算法配置好之后還有一個(gè)坑Keil的Flash Download選項(xiàng)里Programming Algorithm和RAM for Algorithm這兩組地址空間要和芯片的QSPI映射地址范圍匹配。RZ/N2L的QSPI內(nèi)存映射地址通常是0x80000000開頭的一段區(qū)間算法配置里的起始地址必須寫對(duì)這個(gè)值否則程序能下載但上電后無法從Flash啟動(dòng)。4. QSPI底層驅(qū)動(dòng)快速移植讀、寫、擦除完整實(shí)現(xiàn)4.1 QSPI控制器的工作方式先理解再動(dòng)手RZ/N2L的QSPI控制器支持兩種工作模式間接模式和內(nèi)存映射模式。間接模式適合指令發(fā)送、狀態(tài)讀取、小塊數(shù)據(jù)寫入內(nèi)存映射模式適合大塊數(shù)據(jù)讀取也就是XIP。理解這兩種模式的區(qū)別對(duì)接下來的代碼設(shè)計(jì)很重要。間接模式下代碼像操作普通外設(shè)一樣往控制器的傳輸寄存器里寫數(shù)據(jù)觸發(fā)QSPI控制器自動(dòng)產(chǎn)生SCLK時(shí)序。這種方式靈活可以發(fā)任意指令缺點(diǎn)是效率偏低每筆數(shù)據(jù)傳輸都要軟件參與。內(nèi)存映射模式下Flash被映射到CPU地址空間CPU直接讀地址就能拿到數(shù)據(jù)效率最高但不適合寫入操作因?yàn)閷懖僮餍枰暾牟翆懥鞒?。參?shù)存儲(chǔ)的優(yōu)化思路就在這里讀取參數(shù)時(shí)走內(nèi)存映射模式直接以指針方式訪問寫入?yún)?shù)時(shí)切換到間接模式執(zhí)行擦除、寫使能、頁寫一系列命令。不少工程師忽略了這個(gè)區(qū)分全程用間接模式讀Flash導(dǎo)致系統(tǒng)啟動(dòng)時(shí)讀參數(shù)耗時(shí)幾十毫秒完全沒有發(fā)揮QSPI的性能優(yōu)勢(shì)。4.2 核心驅(qū)動(dòng)代碼忙檢測(cè)、頁寫、扇區(qū)擦除QSPI NOR Flash的操作基礎(chǔ)是命令序列。常見的命令包括寫使能0x06、讀狀態(tài)寄存器0x05、頁寫0x02、扇區(qū)擦除0x20、塊擦除0xD8、讀數(shù)據(jù)0x03單線高速讀0x0B。驅(qū)動(dòng)設(shè)計(jì)時(shí)把這些命令封裝成基礎(chǔ)函數(shù)上層按需組合。下面這段代碼是一個(gè)精簡(jiǎn)的忙等待和頁寫實(shí)現(xiàn)基于瑞薩FSP生成代碼的基礎(chǔ)上做的簡(jiǎn)化實(shí)際工程中要做超時(shí)保護(hù)/* 讀QSPI Flash狀態(tài)寄存器 */ static uint8_t qspi_read_status(void) { uint8_t status 0; uint8_t cmd 0x05; /* Read Status Register-1 */ R_QSPI_Command(cmd, 1, status, 1); return status; } /* 等待Flash空閑帶超時(shí) */ static int qspi_wait_busy(uint32_t timeout_ms) { uint32_t tick 0; while (tick timeout_ms) { if ((qspi_read_status() 0x01) 0) { return 0; } R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); tick; } return -1; } /* 頁寫len為1~256字節(jié) */ static int qspi_page_program(uint32_t addr, const uint8_t *buf, uint16_t len) { uint8_t write_enable 0x06; R_QSPI_Command(write_enable, 1, NULL, 0); return R_QSPI_Write(addr, buf, len); } /* 扇區(qū)擦除4KB */ static int qspi_sector_erase(uint32_t addr) { uint8_t write_enable 0x06; uint8_t erase_cmd 0x20; R_QSPI_Command(write_enable, 1, NULL, 0); R_QSPI_Command(erase_cmd, 1, NULL, 0); return qspi_wait_busy(500); }頁寫有個(gè)容易被忽略的坑如果寫入數(shù)據(jù)跨過了256字節(jié)頁邊界Flash內(nèi)部會(huì)自動(dòng)回卷把后半段數(shù)據(jù)寫到頁首導(dǎo)致數(shù)據(jù)錯(cuò)亂。所以驅(qū)動(dòng)里一定要做長(zhǎng)度和跨頁判斷超過頁邊界就拆分成多次寫入每次最多寫到本頁結(jié)尾。擦除等待時(shí)間也要給足。扇區(qū)擦除典型時(shí)間是50ms到400ms塊擦除更長(zhǎng)。等待循環(huán)里如果超時(shí)閾值設(shè)置太短會(huì)誤報(bào)出錯(cuò)。我習(xí)慣把扇區(qū)擦除超時(shí)設(shè)置到500ms塊擦除設(shè)置到2s給芯片留足余量。4.3 讀性能優(yōu)化內(nèi)存映射模式一次搞定實(shí)際產(chǎn)品里參數(shù)結(jié)構(gòu)體可能包含幾十個(gè)字段分布在幾百字節(jié)范圍內(nèi)。如果用間接模式一條條讀每次都要拉起CS、發(fā)送命令效率非常低。正確做法是在系統(tǒng)初始化時(shí)把QSPI控制器切換到內(nèi)存映射模式然后直接用指針讀取#define QSPI_MAPPED_BASE 0x80000000UL #define PARAM_AREA_OFFSET 0x00100000UL const param_struct_t *param_ptr; param_ptr (const param_struct_t *)(QSPI_MAPPED_BASE PARAM_AREA_OFFSET); if (param_ptr-magic PARAM_MAGIC) { /* 參數(shù)有效直接使用 */ local_param *param_ptr; }這段代碼的執(zhí)行速度比間接模式快一個(gè)數(shù)量級(jí)讀取時(shí)不占用CPU等待時(shí)間。寫入時(shí)再切換回間接模式完成擦寫后重新切回內(nèi)存映射模式。需要注意的是切換操作要嚴(yán)格配對(duì)防止寫操作執(zhí)行到一半切模式導(dǎo)致總線異常。5. 參數(shù)存儲(chǔ)優(yōu)化實(shí)戰(zhàn)磨損均衡與掉電保護(hù)5.1 存儲(chǔ)區(qū)布局設(shè)計(jì)代碼和參數(shù)分家參數(shù)存儲(chǔ)設(shè)計(jì)的首要原則是參數(shù)區(qū)和代碼區(qū)必須物理隔離不能混在一起。否則一次參數(shù)擦除失誤可能把固件代碼一同抹掉產(chǎn)品直接變磚。我習(xí)慣把QSPI Flash分成四個(gè)區(qū)域一級(jí)Bootloader、應(yīng)用固件區(qū)、參數(shù)存儲(chǔ)區(qū)、日志區(qū)。典型布局參考如下區(qū)域起始地址大小用途Bootloader0x00000064KB啟動(dòng)加載、異常恢復(fù)應(yīng)用固件0x0100001MB主程序XIP運(yùn)行參數(shù)存儲(chǔ)0x200000256KB參數(shù)雙Slot輪換日志區(qū)0x240000剩余空間運(yùn)行日志、升級(jí)記錄參數(shù)區(qū)劃分256KB相對(duì)整個(gè)Flash容量不算大但用雙Slot輪換設(shè)計(jì)已經(jīng)非常寬裕。每個(gè)Slot對(duì)齊到扇區(qū)邊界方便獨(dú)立擦除。如果固件本身不足1MB可以把參數(shù)區(qū)擴(kuò)大存儲(chǔ)余量更充足。參數(shù)結(jié)構(gòu)體建議這樣定義typedef struct { uint32_t magic; /* 固定魔數(shù)識(shí)別參數(shù)有效性 */ uint32_t version; /* 參數(shù)版本號(hào) */ uint32_t sequence; /* 遞增序號(hào)區(qū)分新舊 */ uint32_t crc32; /* CRC校驗(yàn)值 */ uint8_t data[512]; /* 實(shí)際參數(shù)字段 */ } param_slot_t;magic用于快速判斷區(qū)域是否被寫過version支持不同版本固件之間參數(shù)格式升級(jí)sequence在雙Slot設(shè)計(jì)中區(qū)分新舊數(shù)據(jù)crc32確保數(shù)據(jù)完整性。這四樣缺一不可少了任何一個(gè)后續(xù)排查都會(huì)很痛苦。5.2 雙Slot輪換寫入掉電也不怕參數(shù)寫入最怕掉電。設(shè)想一下寫主Slot到一半斷電主Slot的數(shù)據(jù)既不是舊值也不是新值如果系統(tǒng)只認(rèn)主Slot這次斷電就造成參數(shù)損壞。雙Slot設(shè)計(jì)就是為了解決這個(gè)問題。寫入流程這樣設(shè)計(jì)讀取當(dāng)前有效Slot的sequence值記為old_seq。擦除空閑Slot把新參數(shù)和遞增后的sequence寫入該Slot。寫入完成后讀回校驗(yàn)CRC正確才認(rèn)為寫入成功。將另一個(gè)Slot標(biāo)記為無效把magic清掉。上電時(shí)掃描兩個(gè)Slot取magic有效且sequence較大的那個(gè)作為當(dāng)前參數(shù)。這里的關(guān)鍵在于任何時(shí)候至少保留一份完整有效的參數(shù)。寫入過程只動(dòng)空閑Slot老數(shù)據(jù)始終在另一個(gè)Slot里躺著就算寫一半掉電上電后仍能讀到完整舊數(shù)據(jù)。實(shí)現(xiàn)時(shí)用一段簡(jiǎn)單的掃描函數(shù)static param_slot_t *find_valid_slot(void) { param_slot_t *slots[2]; int idx; slots[0] (param_slot_t *)(QSPI_MAPPED_BASE SLOT0_OFFSET); slots[1] (param_slot_t *)(QSPI_MAPPED_BASE SLOT1_OFFSET); /* 先判斷magic是否都有效 */ if (slots[0]-magic ! PARAM_MAGIC slots[1]-magic ! PARAM_MAGIC) { return NULL; /* 兩個(gè)槽都無效使用默認(rèn)參數(shù) */ } idx 0; if (slots[1]-magic PARAM_MAGIC) { if (slots[0]-magic ! PARAM_MAGIC || slots[1]-sequence slots[0]-sequence) { idx 1; } } if (crc32_check(slots[idx]) ! 0) { return NULL; /* CRC校驗(yàn)失敗用默認(rèn)參數(shù)并報(bào)警 */ } return slots[idx]; }這段邏輯還要配合CRC校驗(yàn)。CRC計(jì)算放在寫入之前讀回校驗(yàn)放在上電階段。不要省CRC魔數(shù)只能在某種程度上防止未擦除區(qū)域被誤讀但無法防止數(shù)據(jù)位翻轉(zhuǎn)或者部分寫失敗。CRC才是數(shù)據(jù)完整性的最終保障。5.3 磨損均衡計(jì)算Flash壽命到底夠不夠雙Slot方案解決了掉電保護(hù)問題但磨損均衡的效果取決于Slot數(shù)量和寫入頻率。這里做一個(gè)簡(jiǎn)單計(jì)算把理論壽命算清楚。假設(shè)使用W25Q12816MB參數(shù)區(qū)256KB劃分為兩個(gè)Slot每個(gè)Slot占128KB一個(gè)Slot內(nèi)包含32個(gè)4KB扇區(qū)。每次參數(shù)更新寫一個(gè)扇區(qū)。Flash扇區(qū)擦寫壽命為10萬次。兩個(gè)Slot始終各有一個(gè)扇區(qū)在參與寫入磨損均衡后相當(dāng)于有64個(gè)扇區(qū)輪流被擦寫實(shí)際上每次寫一個(gè)扇區(qū)后下次會(huì)優(yōu)先選擇擦寫次數(shù)最少的扇區(qū)。那么使用壽命約為64個(gè)扇區(qū) × 100,000次 / 每天寫入次數(shù) 6,400,000 / 每天寫入次數(shù)如果設(shè)備每10分鐘寫一次參數(shù)一天144次壽命約為44444天超過120年。就算每秒鐘寫一次一天86400次壽命也有74天對(duì)于參數(shù)保存來說完全夠用。這個(gè)計(jì)算說明一個(gè)道理磨損均衡的效果取決于參與輪換的扇區(qū)數(shù)量。如果只用固定一個(gè)扇區(qū)擦寫10萬次壽命很快耗盡但只要把參與輪換的扇區(qū)數(shù)量做大壽命會(huì)線性增長(zhǎng)。所以參數(shù)區(qū)不要?jiǎng)澐值脫笓杆阉讯嘟o幾個(gè)扇區(qū)產(chǎn)品生命周期內(nèi)的磨損問題基本不用再管。6. 啟動(dòng)與存儲(chǔ)故障排查我踩過的坑都在這里6.1 啟動(dòng)失敗反復(fù)重啟先查BootROM階段遇到上電后系統(tǒng)反復(fù)重啟或者完全沒有響應(yīng)按下面的順序排查。首先檢查啟動(dòng)模式引腳的最終電平是否和預(yù)期一致。用萬用表量一下復(fù)位釋放時(shí)的電平不要只看原理圖。我遇到過原理圖上標(biāo)示正確但PCB布線時(shí)通過一個(gè)串聯(lián)電阻接到了錯(cuò)誤電平的情況。其次確認(rèn)Flash芯片是否被BootROM正確識(shí)別。RZ/N2L的BootROM在執(zhí)行QSPI啟動(dòng)時(shí)會(huì)從Flash的起始位置讀取有效引導(dǎo)頭引導(dǎo)頭的結(jié)構(gòu)在芯片應(yīng)用手冊(cè)里有明確說明。如果引導(dǎo)頭內(nèi)容不對(duì)BootROM會(huì)靜默放棄之后的行為取決于其他啟動(dòng)源是否配置。第三步是檢查Flash里是否確實(shí)燒寫了Bootloader。用調(diào)試器讀出Flash起始地址的二進(jìn)制數(shù)據(jù)看是否有意料之外的全FF或者全00。全FF說明Flash沒燒進(jìn)去或者擦除后未編程全00說明寫操作異常通常與寫使能時(shí)序有關(guān)。6.2 參數(shù)偶發(fā)丟失CRC和掉電時(shí)序是重點(diǎn)參數(shù)偶發(fā)丟失是排查起來最頭疼的問題因?yàn)閱栴}不一定能穩(wěn)定復(fù)現(xiàn)。我遇到過的原因主要有三類。第一類是CRC校驗(yàn)邏輯漏了字節(jié)。曾經(jīng)有個(gè)同事把結(jié)構(gòu)體里padding區(qū)也算進(jìn)CRC結(jié)果不同編譯優(yōu)化級(jí)別下結(jié)構(gòu)體填充字節(jié)不同導(dǎo)致同一份參數(shù)在不同固件下校驗(yàn)結(jié)果不一致。解決方案是定義一個(gè)固定布局的參數(shù)結(jié)構(gòu)體顯式添加保留字節(jié)CRC只計(jì)算固定區(qū)域。第二類是掉電瞬間的Flash內(nèi)部狀態(tài)被破壞。QSPI Flash在寫入過程中掉電Flash內(nèi)部的高壓發(fā)生器可能處于中間狀態(tài)雖然雙Slot機(jī)制能保住有效數(shù)據(jù)但下次擦除這個(gè)損壞的Slot時(shí)如果寫入電壓不穩(wěn)定擦除時(shí)間會(huì)異常拉長(zhǎng)。解決辦法是給Flash供電加一點(diǎn)儲(chǔ)能電容讓掉電后還能維持幾十毫秒供電把正在進(jìn)行的擦寫流程走完。第三類是WP引腳保護(hù)意外開啟。很多Flash有狀態(tài)寄存器保護(hù)位一旦設(shè)置扇區(qū)或塊無法擦除。如果代碼里沒有寫狀態(tài)寄存器的操作但有外部干擾觸發(fā)了保護(hù)后續(xù)寫操作會(huì)一直失敗。排查時(shí)可以讀取狀態(tài)寄存器檢查BP位是否被意外置位。6.3 擦寫卡死和讀取錯(cuò)亂信號(hào)問題和驅(qū)動(dòng)問題兩手抓擦寫卡死最直接的原因是等待超時(shí)邏輯沒有生效流程一直卡在忙輪詢里。排查時(shí)要確認(rèn)超時(shí)計(jì)數(shù)器是否運(yùn)行正常以及是否在中斷環(huán)境下調(diào)用了驅(qū)動(dòng)且沒有處理臨界區(qū)。讀取錯(cuò)亂常見于高速模式。把QSPI時(shí)鐘從100MHz降到50MHz后故障消失基本可以斷定是信號(hào)完整性問題。排查PCB布線、串聯(lián)電阻取值、Flash供電濾波逐步降低時(shí)鐘直到穩(wěn)定。有時(shí)候問題不在硬件而是四線模式下的IO2/IO3引腳的復(fù)用配置不完整導(dǎo)致寫命令發(fā)出時(shí)數(shù)據(jù)線狀態(tài)錯(cuò)誤?,F(xiàn)象可能原因排查方法上電不啟動(dòng)復(fù)位循環(huán)啟動(dòng)模式引腳電平錯(cuò)誤萬用表量復(fù)位釋放時(shí)引腳電平下載報(bào)錯(cuò)找不到Flash算法Keil缺少對(duì)應(yīng).FLM或地址配置不對(duì)核對(duì)QSPI映射地址替換下載算法參數(shù)偶爾全零或全FFCRC校驗(yàn)失誤或雙Slot邏輯缺陷檢查CRC計(jì)算范圍打印兩個(gè)Slot有效狀態(tài)擦除200ms后仍返回忙狀態(tài)寄存器保護(hù)位被置位讀取SR檢查BP位數(shù)據(jù)高速讀錯(cuò)低速正常信號(hào)完整性問題降低時(shí)鐘、檢查布線等長(zhǎng)QSPI初始化后讀寫全失敗命令序列配置和Flash型號(hào)不匹配對(duì)照Flash手冊(cè)核對(duì)指令碼排查這類問題效率最高的手段是提前在代碼里埋好診斷信息。每步初始化、每次擦寫執(zhí)行、每個(gè)CRC校驗(yàn)都記錄狀態(tài)碼。產(chǎn)品量產(chǎn)后出問題沒有l(wèi)og根本無從下手。日志區(qū)劃分出來的空間不是擺設(shè)就算只是異常時(shí)寫一個(gè)16字節(jié)的錯(cuò)誤碼也比整片F(xiàn)lash拆下來讀數(shù)據(jù)要靠譜。從RASC配置到Keil調(diào)試從間接模式到內(nèi)存映射模式這套方案跑通之后RZ/N2L的QSPI啟動(dòng)和參數(shù)存儲(chǔ)就不再是風(fēng)險(xiǎn)點(diǎn)了。以后再做新項(xiàng)目如果還是RZ系列配外部Flash我大概率會(huì)直接復(fù)用這套框架——把雙Slot輪換邏輯做成獨(dú)立模塊底層驅(qū)動(dòng)換成對(duì)應(yīng)芯片的FSP接口上層業(yè)務(wù)邏輯幾乎不用改。有一點(diǎn)想額外提醒量產(chǎn)固件里的QSPI時(shí)鐘頻率、超時(shí)時(shí)間這類參數(shù)一定要留成宏定義別散落在代碼里。后期換Flash型號(hào)或者調(diào)整PCB布局只需要改一處配置重新編譯就能用省去大范圍排查的工夫。