程升級實(shí)戰(zhàn):AXI Quad SPI與N25Q128 Flash方案詳解)
我在這塊摸爬滾打了五六年遠(yuǎn)程升級這個需求幾乎每個FPGA項(xiàng)目跑到后期都會冒出來——不管是現(xiàn)場設(shè)備固件迭代、算法參數(shù)在線調(diào)整還是產(chǎn)品出貨后悄悄修幾個不那么光彩的Bug。但說實(shí)話FPGA遠(yuǎn)程升級比MCU那套要別扭得多尤其是當(dāng)你以為“不就是往Flash里寫幾個數(shù)嘛”的時候設(shè)計本身已經(jīng)在埋雷了。這篇就拿Xilinx 7系列Vivado環(huán)境配合AXI Quad SPI IP核操作N25Q128這塊再常見不過的128Mb SPI NOR Flash把整個流程從架構(gòu)、寄存器、時序踩坑到回滾策略完整捋一遍。這篇文章適合誰正在做FPGA在線升級方案選型的、被ICAP和MultiBoot文檔繞暈的、寫完Flash讀寫卻發(fā)現(xiàn)上電起不來的還有那些準(zhǔn)備把遠(yuǎn)程升級當(dāng)“最后一公里”交付給現(xiàn)場的工程師。我們直接開工。1. 遠(yuǎn)程升級的整體架構(gòu)先把“能回滾”想明白再動手遠(yuǎn)程升級最怕的不是升級失敗而是失敗之后設(shè)備變磚人還得跑一趟現(xiàn)場。所以整個架構(gòu)設(shè)計的核心不是“怎么把新固件寫進(jìn)去”而是“寫壞了怎么回來”。N25Q128有16MB空間FPGA的bit文件通常在幾MB到十幾MB不等合理劃分這16MB讓系統(tǒng)同時具備正常啟動區(qū)和備份區(qū)是第一步。1.1 存儲布局Golden區(qū)、Update區(qū)與臨時緩存區(qū)我常用的分區(qū)方式是這樣的最前面的1MB放Golden鏡像出廠固化、永不擦除的“保命”版本接下來8MB放Update區(qū)日常遠(yuǎn)程升級的落地區(qū)再往后的4MB作為Download Buffer接收新固件的臨時暫存區(qū)域校驗(yàn)通過后才搬移到Update區(qū)最后留一些空間存版本信息和升級日志。這樣即使升級過程中途斷電最多丟一個未完成的臨時文件Golden鏡像永遠(yuǎn)在。地址分配并不是固定的要根據(jù)實(shí)際bit文件大小調(diào)整但核心準(zhǔn)則是Golden區(qū)必須獨(dú)立、寫保護(hù)、所有用戶邏輯都無法修改它。有些設(shè)計會把Golden區(qū)放在Flash末尾用跳線控制啟動地址但用AXI Quad SPI和MultiBoot機(jī)制的話Goldern放低地址段是默認(rèn)且最簡單可靠的。1.2 為什么用AXI Quad SPI而不是普通SPI或者BPIN25Q128支持標(biāo)準(zhǔn)SPI、Dual SPI和Quad SPI模式。遠(yuǎn)程升級最看重的就是寫入速度和總線打通效率。標(biāo)準(zhǔn)SPI一次只有一根數(shù)據(jù)線16MB寫滿得跑很久而Quad SPI模式下四根IO并行同樣的時鐘頻率下理論傳輸速度提升四倍。AXI Quad SPI IP把AXI4協(xié)議轉(zhuǎn)成Flash時序讓PS嵌入式ARM或者用戶邏輯可以通過內(nèi)存映射的方式直接讀寫Flash。在7系列純FPGA場景下用戶邏輯通過AXI4-Lite或AXI4接口掛Master在PS場景下Zynq的MIO/EMIO可以連接到IP。雙場景統(tǒng)一用AXI Quad SPI的好處是驅(qū)動邏輯可以復(fù)用不需要為PS和PL寫兩套Flash驅(qū)動。1.3 啟動流程與遠(yuǎn)程升級的關(guān)系7系列的上電加載流程是芯片先按配置模式引腳M[2:0]找到SPI Flash讀取起始地址0x000000的鏡像如果該鏡像頭部的CRC校驗(yàn)失敗或者你通過ICAP主動發(fā)起了IPROG命令則跳到WBSTAR寄存器指定的地址繼續(xù)加載。這就給了我們很大的設(shè)計空間正常啟動時讀Golden區(qū)需要升級時先把新固件寫到Update區(qū)然后用WBSTAR把啟動地址指向Update區(qū)最后觸發(fā)ICAP重配置——啟動流程就變成立即用新固件運(yùn)行。這個機(jī)制拆解下來會發(fā)現(xiàn)所謂“遠(yuǎn)程升級”的本質(zhì)就是改一個啟動地址 觸發(fā)一次重配置。這比以往聽到的各種云里霧里的說法樸素得多。2. AXI Quad SPI IP配置詳解最容易翻車的幾個寄存器Vivado里添加AXI Quad SPI IP并不難雙擊打開配置界面才是噩夢的開始。那些選項(xiàng)組合起來有幾十種可能選錯一個后面寫Flash寫到你懷疑人生。2.1 模式選擇Standard vs Dual vs QuadIP提供了三種模式Standard單線、Dual雙線、Quad四線。我強(qiáng)烈建議直接用Quad模式因?yàn)楹罄m(xù)所有讀寫指令都統(tǒng)一走四線協(xié)議速度和兼容性都有了。Dual是過渡方案如果是第一次接這個IP為了穩(wěn)妥想先用單線打通也不是不行但后面再改IP配置、改約束、改驅(qū)動工作量翻倍。一個容易忽視的點(diǎn)是Quad模式下讀Flash用的是0x6B指令Fast Read Quad Output而寫Flash用的是0x32指令Quad Page Program。也就是說讀和寫都會走四線但指令碼完全不同驅(qū)動里要把這兩個指令分開定義清楚。2.2 時鐘比例與FIFO深度IP內(nèi)部的SCK時鐘來自AXI時鐘分頻配置界面有幾個檔位2、4、8。N25Q128最高支持108MHz的讀時鐘但實(shí)際布線、PCB信號完整性以及溫度漂移都會限制這個值不建議直接頂?shù)缴舷?。我一般ACLK跑100MHzSCK分頻到50MHz左右寫Flash時頁面編程Page Program本身就受限于Flash內(nèi)部寫時間SCK再高也無法突破讀的時候50MHz也是比較穩(wěn)的區(qū)間。FIFO深度則取決于你的AXI總線位寬和突發(fā)長度。選32或者64就夠用除非PS側(cè)DMA搬運(yùn)數(shù)據(jù)才會考慮更深的FIFO。過大的FIFO只會浪費(fèi)Block RAM資源。2.3 EZ模式與DIY模式的取舍這是IP最隱蔽、也最坑人的一個選項(xiàng)。INCR模式叫DTR其中有個“FIFO mode Legacy mode”的選擇EZ模式IP內(nèi)部把Flash的狀態(tài)機(jī)封裝好了你只需要做AXI讀寫它會自動處理寫使能、輪詢忙標(biāo)志、頁寫入邊界等底層細(xì)節(jié)??雌饋砻篮玫俣銓Φ刂房臻g的操作是連續(xù)的、自然對齊的不夠靈活。DIY模式IP只負(fù)責(zé)最基本的指令收發(fā)寫使能、狀態(tài)寄存器輪詢、擦除、頁編程這些要你自己通過SPI指令序列完成。遠(yuǎn)程升級這種場景需要精細(xì)控制配置寄存器比如狀態(tài)寄存器的保護(hù)位而且跳轉(zhuǎn)啟動地址前要讀Flash狀態(tài)確認(rèn)EZ模式的封裝反而礙事DIY模式才是能自由操控的方案。記住這個結(jié)論遠(yuǎn)程升級用DIY模式純XAxi接口的流水?dāng)?shù)據(jù)搬運(yùn)場景才考慮EZ模式。3. N25Q128關(guān)鍵指令與時序從擦除到編程每個步驟的Wait都省不得N25Q128是一款非常標(biāo)準(zhǔn)的SPI NOR Flash指令集在Micron/Numonyx的datasheet里都有但時序的嚴(yán)謹(jǐn)程度直接決定了你的Flash是健康工作十年還是三個月就壞塊。3.1 寫使能WREN 0x06的前置角色NOR Flash寫入前必須拉高狀態(tài)寄存器的WEL位Write Enable Latch就像你改系統(tǒng)關(guān)鍵配置前要輸入sudo密碼一樣。WREN指令只有一條0x06沒有地址階段。每執(zhí)行一次Page Program或Sector Erase都會自動把WEL位清0所以每次編程/擦除操作前必須先發(fā)WREN。很多新手的第一個坑就在這里寫了WREN之后立刻發(fā)Page Program然后發(fā)現(xiàn)數(shù)據(jù)根本沒寫進(jìn)去大概率是WREN之后沒有輪詢狀態(tài)寄存器確認(rèn)WEL已經(jīng)拉高。正確的順序是發(fā)WREN → 讀狀態(tài)寄存器 → 確認(rèn)WEL1 → 再發(fā)Page Program。3.2 狀態(tài)寄存器輪詢Busy Bit的等待邏輯N25Q128的Sector Erase典型時間是3秒Page Program典型時間是0.65ms。這些時間里Flash內(nèi)部在忙外部任何指令除了RDSR0x05都被忽略。驅(qū)動必須循環(huán)讀狀態(tài)寄存器直到Bit0WIP位Write In Progress變成0才算完。這里有個時間預(yù)算的問題。Sector Erase一次要3秒如果你的升級固件有4MB按64KB一個Sector算就是64個扇區(qū)光擦除時間就192秒。這個時間用戶從APP層看就是“升級中”的等待體驗(yàn)上必須給足提示。更關(guān)鍵的是如果你的驅(qū)動在上次擦除還沒結(jié)束時就直接發(fā)下一個指令Flash會忽略當(dāng)前異常指令并導(dǎo)致狀態(tài)錯亂后續(xù)操作全部撲街。所以輪詢一定不能省而且要保證超時判斷足夠?qū)捲?。我用的輪詢函?shù)大致長這樣int flash_wait_ready(uint32_t timeout_ms) { uint32_t elapsed 0; uint8_t status 0; uint32_t command 0x05000000; // RDSR via AXI Quad SPI while (elapsed timeout_ms) { // 通過IP發(fā)送0x05指令并讀取狀態(tài)寄存器 status axi_quad_spi_transfer(command); if ((status 0x01) 0) { // WIP 0不忙了 return 0; } delay_ms(1); elapsed; } return -1; // 超時 }3.3 扇區(qū)擦除與數(shù)據(jù)保護(hù)位N25Q128的Sector Erase指令是0xD8按64KB一個扇區(qū)操作。官方還支持4KB的子扇區(qū)擦除0x20但升級固件動輒幾MB用4KB擦除會極其耗時只有更新配置參數(shù)時才劃算。所以升級場景默認(rèn)用0xD8做整扇區(qū)擦除。N25Q128有BP0~BP3四個保護(hù)位位于狀態(tài)寄存器的高位。默認(rèn)情況下保護(hù)區(qū)域是空的你可以自由擦寫。但一旦你開啟保護(hù)擦除和編程指令在保護(hù)地址范圍內(nèi)會被硬生生拒絕。我見過產(chǎn)線上一批板子Flash寫不進(jìn)去查半天發(fā)現(xiàn)是之前某次升級代碼里多了一句寫狀態(tài)寄存器保護(hù)位把整個Flash鎖死了。解鎖的方法是先發(fā)WREN然后往狀態(tài)寄存器寫入0x00清除所有保護(hù)位。這條邏輯要放在每一次擦除前做到“不管上次狀態(tài)如何這次都要強(qiáng)制解鎖”。3.4 Page Program與跨頁寫入N25Q128的Page Program指令Quad模式0x32一次最多寫256字節(jié)且這256字節(jié)必須落在同一個256字節(jié)對齊的Page內(nèi)。如果你要寫的緩沖區(qū)跨越了Page邊界必須拆分成兩次甚至多次Page Program。這塊邏輯我通常在驅(qū)動層封裝好提供一個緩沖區(qū)任意長度寫入的接口內(nèi)部自動拆頁int flash_write_buffer(uint32_t addr, const uint8_t *buf, uint32_t len) { uint32_t offset 0; uint32_t page_offset addr 0xFF; // 當(dāng)前頁內(nèi)偏移 uint32_t remaining_in_page 256 - page_offset; uint32_t chunk; while (len 0) { chunk (len remaining_in_page) ? len : remaining_in_page; flash_write_enable(); // 發(fā)送0x32指令寫入當(dāng)前塊 flash_quad_page_program(addr offset, buf offset, chunk); flash_wait_ready(1000); // 等待頁編程完成 offset chunk; len - chunk; remaining_in_page 256; // 后續(xù)都從頁首開始 } return 0; }這個封裝的要點(diǎn)是首先生效了頁內(nèi)偏移之后每次寫入都以256為基準(zhǔn)不用擔(dān)心跨頁數(shù)據(jù)被拆亂。4. 雙鏡像啟動與MultiBoot細(xì)節(jié)WBSTAR和IPROG的正確用法4.1 Golden與Update的啟動地址設(shè)定回到分區(qū)設(shè)計。假設(shè)Golden鏡像在0x000000Update鏡像在0x1000001MB處。7系列的上電默認(rèn)讀0x000000沒有問題。但要切換到Update啟動需要做兩件事把WBSTAR寄存器設(shè)為0x100000然后觸發(fā)IPROG命令。IPROG是ICAP接口提供的一條虛擬指令值通常是0x0000000F和0x0000AABB的組合具體取決于芯片族。通過ICAPE2原語發(fā)送IPROG后FPGA會立刻重新加載配置讀取WBSTAR指向的地址。這個“立即”很關(guān)鍵——它不經(jīng)過PLD復(fù)位、不經(jīng)過DDR復(fù)位相當(dāng)于整個芯片熱重啟。4.2 ICAPE2原語的驅(qū)動實(shí)現(xiàn)ICAPE2在Vivado里是一個原語直接例化即可。7系列驅(qū)動ICAP發(fā)送IPROG的代碼框架如下module icap_reboot ( input wire clk, input wire trigger, input wire [31:0] wbstar_addr, output reg done ); // WBSTAR 寄存器設(shè)置 (0x2000??) // 需要先從ICAP寫入WBSTAR再寫入IPROG localparam WBSTAR_CMD 32h2000_0000; // 地址對齊后寫入 localparam IPROG_CMD 32h0000_000F; reg [31:0] icap_data; reg [7:0] icap_count; reg icap_csib; reg icap_rdwrb; always (posedge clk) begin if (trigger) begin // 先寫WBSTAR icap_csib 0; icap_rdwrb 0; // 寫模式 icap_data {8h00, wbstar_addr}; // 按ICAP時序組合 // ... 實(shí)際需要按ICAP字對齊累加 end end // 之后再發(fā)送IPROG指令并斷言CS_B等最終觸發(fā)配置加載 endmoduleIPROG的設(shè)計有一個很重要的細(xì)節(jié)WBSTAR的地址必須按32位字對齊即bit[1:0]00原因在SPI Flash啟動時地址是字地址而不是字節(jié)地址。如果你的Update區(qū)地址設(shè)計成0x100000那么寫字到WBSTAR的值是0x040000。這個換算很多人第一次都栽了啟動總是不對往往是多了一個左移兩位。4.3 回滾策略一種主動放棄的方式雙鏡像的好處是Update區(qū)固件如果因?yàn)樽陨鞡ug起不來Golden區(qū)仍然在Flash起始處上電默認(rèn)還是從Golden啟動。但這里有個前提你的Update區(qū)Bootloader必須能判斷“自身不能正常工作”的條件。最終極的兜底方案是外置看門狗——FPGA重配置后如果軟件一段時間內(nèi)沒有喂狗看門狗強(qiáng)制硬件復(fù)位復(fù)位后的啟動流程又回到Golden。所以在一個完整的遠(yuǎn)程升級系統(tǒng)里FPGA內(nèi)部的狀態(tài)機(jī)會這樣運(yùn)轉(zhuǎn)處于Golden鏡像時收到升級請求把新固件下載到Download Buffer校驗(yàn)CRC和鏡像頭寫Update區(qū)回讀校驗(yàn)設(shè)置WBSTAR指向Update區(qū)并觸發(fā)IPROG。系統(tǒng)在Update鏡像中運(yùn)行若運(yùn)行期間檢測到致命錯誤、喂狗超時、或收到回滾命令則直接把WBSTAR改為0x000000并IPROG回到Golden。上電或復(fù)位時硬件默認(rèn)從0x000000的Golden啟動不受任何遠(yuǎn)程狀態(tài)干擾。這套策略哪怕遠(yuǎn)端網(wǎng)絡(luò)中斷、固件下載一半斷電都不會導(dǎo)致不可恢復(fù)的變磚。設(shè)備最差也就是回到出廠版本這是遠(yuǎn)程升級系統(tǒng)的保命底線。5. 遠(yuǎn)程升級執(zhí)行流程從固件接收到ICAP重啟5.1 固件包的協(xié)議設(shè)計遠(yuǎn)程升級不是把原始.bit文件裸著扔過去。現(xiàn)場網(wǎng)絡(luò)環(huán)境千差萬別丟包重傳、斷點(diǎn)續(xù)傳都是家常便飯。我建議自定義一個升級包格式至少包含以下幾個字段魔數(shù)Magic Word用于快速識別有效鏡像版本號主版本次版本構(gòu)建號鏡像長度CRC32校驗(yàn)值時間戳鏡像數(shù)據(jù)接收到固件包后先做完整性校驗(yàn)再做啟動頭校驗(yàn)Xilinx bit文件有固定的同步字32位0xFFFFFFFF的同步序列然后是0xAA995566確認(rèn)這個包確實(shí)能當(dāng)配置鏡像用才允許進(jìn)入Flash編程階段。5.2 下載到Download Buffer的通道選擇如果你的FPGA系統(tǒng)有以太網(wǎng)口固件數(shù)據(jù)通常從網(wǎng)口進(jìn)來通過DMA或者CPU搬運(yùn)到DDR再從DDR讀到AXI Quad SPI寫進(jìn)Flash。這一路帶寬瓶頸其實(shí)不在FPGA邏輯而在Flash的擦寫速度。實(shí)測下來N25Q128在Quad模式下寫完16MB需要大約40-60秒含擦除這個數(shù)據(jù)可以作為你給上層應(yīng)用設(shè)計升級進(jìn)度條的參考。如果是純PL設(shè)計比如沒有PS可以通過UART、SPI Slave或者PCIe接收固件寫入一個Block RAM或DDR作為緩存。DDRAXI DMA的方式在大固件面前優(yōu)勢很大否則就要用狀態(tài)機(jī)慢慢磨對協(xié)議棧超時控制壓力大得多。5.3 編程流程的完整狀態(tài)機(jī)整個升級寫Flash的流程我習(xí)慣用狀態(tài)機(jī)嚴(yán)格管理每個狀態(tài)都必須有超時跳轉(zhuǎn)和處理分支typedef enum { ST_IDLE, ST_UNLOCK, ST_ERASE, ST_PROGRAM, ST_VERIFY, ST_SET_BOOT_ADDR, ST_REBOOT, ST_ERROR } upgrade_state_t;各狀態(tài)的轉(zhuǎn)移條件和關(guān)鍵細(xì)節(jié)ST_UNLOCK解除Flash保護(hù)位。發(fā)WREN寫狀態(tài)寄存器0x00讀回確認(rèn)WIP0、保護(hù)位清零成功后再往下走。ST_ERASE按扇區(qū)擦除Update區(qū)整片區(qū)域。每個扇區(qū)擦除前都要發(fā)WREN、等待WIP擦除完成后建議巡檢整個區(qū)域讀回來應(yīng)該是全0xFF如果有殘留說明Flash有壞塊或擦除失敗直接進(jìn)ST_ERROR并回滾。ST_PROGRAM逐塊寫入數(shù)據(jù)。這里要注意AXI Quad SPI IP的FIFO溢出問題——你的Master寫入速率不能超過IP到Flash的實(shí)際吞吐否則FIFO滿了之后IP會拉低AXI的WREADY此時你必須等待不可超時放棄。ST_VERIFY寫完后回讀整個Update區(qū)逐字節(jié)和源數(shù)據(jù)比較。這一步很多人偷懶但恰恰是它能在現(xiàn)場升級失敗前攔住99%的壞塊和電壓波動問題。ST_SET_BOOT_ADDR把WBSTAR設(shè)置為Update區(qū)地址寫完后讀回確認(rèn)然后才進(jìn)入最后的ST_REBOOT狀態(tài)。ST_REBOOT發(fā)送IPROG命令讓FPGA從新地址加載。這個狀態(tài)機(jī)里每個超時都要有“回滾到Golden”的出口。比如說擦除超時不要試圖反復(fù)重試直接退出升級流程、保持Golden啟動并把錯誤碼上報。5.4 驗(yàn)證階段的附加保險啟動頭回讀除了逐字節(jié)比較我還會額外驗(yàn)證Update區(qū)開頭的64字節(jié)確認(rèn)同步字0xAA995566和頭部寄存器值正確。這是因?yàn)镾PI Flash偶爾會出現(xiàn)一種極其隱蔽的錯誤——頁面編程時數(shù)據(jù)線毛刺導(dǎo)致個別位寫錯但讀取時因?yàn)殡妷汉蜁r序的變化又讀出正確的值。這屬于寫后讀不一致的偶發(fā)失效單獨(dú)靠讀回校驗(yàn)可能漏過去。加一個“啟動頭校驗(yàn)”再配合CRC雙保險才能把現(xiàn)場返修率壓到最低。6. 實(shí)測階段最容易踩的坑給我打工資級別的血淚經(jīng)驗(yàn)這部分是全文的“付費(fèi)內(nèi)容”。以下每個坑我都真金白銀付過代價有的甚至是幾個通宵排查出來的。每一類問題的表象、根因、解決方式我都列在下面供你對照。6.1 坑一擦除期間IP的FIFO卡死與AXI死鎖表象寫Flash到一半CPU或狀態(tài)機(jī)跑飛AXI總線的WREADY一直拉低整個系統(tǒng)像被點(diǎn)穴一樣一動不動。根因AXI Quad SPI IP的發(fā)送FIFO深度有限當(dāng)你連續(xù)寫入大量數(shù)據(jù)時如果Flash配置了比較慢的等待時間比如頁面編程本身需要時間IP內(nèi)部的發(fā)送通道會阻塞而你沒有監(jiān)控AXI的WREADY信號傻乎乎等固定延遲結(jié)果產(chǎn)生總線上鎖。解決用戶邏輯每一個AXI寫操作都要使用正確的握手時序即等WREADY變高后才認(rèn)為寫完成開啟IP的寄存器中斷Interrupt Enable Register使能TX FIFO Half Empty和TX FIFO Full中斷用于驅(qū)動層感知利用IP的SRRSoftware Reset Register作為最后殺招檢測到異常后軟復(fù)位IP讓總線脫離死鎖。6.2 坑二SCK頻率過高導(dǎo)致的數(shù)據(jù)毛刺表象讀取時每個扇區(qū)偶爾有一兩個字節(jié)是0xFF整個文件CRC怎么都對不上擦除、編程偶爾失敗但重試一次又好了間歇性發(fā)作。根因我按datasheet的極限值把SCK設(shè)到了108MHz但板級走線過長、EMI干擾、Flash芯片批次稍差的情況下高速時鐘下的數(shù)據(jù)采樣窗口不夠臨界時序?qū)е旅?。解決把SCK降到50MHz左右把FPGA內(nèi)部IO約束加上IOBUF用于改善信號完整性同時在PCB上保證Flash的VCC旁路電容盡量靠近芯片引腳。這些“小事”能解決很大概率的偶發(fā)故障。我的習(xí)慣是量產(chǎn)前的驗(yàn)證板用108MHz跑100小時不間斷讀寫老化測試如果沒問題再放開到高速實(shí)驗(yàn)室環(huán)境直接降頻到50MHz能省掉80%的疑難雜癥。6.3 坑三IPROG后系統(tǒng)沒有從Update啟動表象IPROG觸發(fā)了但設(shè)備起來后發(fā)現(xiàn)運(yùn)行的還是老版本固件。根因三個原因按概率排序WBSTAR寫入的地址沒有按32位字對齊跳到錯誤地址加載失敗后7系列會自動fallback到SPI的0x000000也就是Golden區(qū)所以看起來是“還運(yùn)行老版本”ICAP的CE和WRITE信號時序不對IPROG命令根本沒有被識別Update區(qū)起始地址其實(shí)寫錯了和下載固件時用的地址不一致跳轉(zhuǎn)到了空白區(qū)域。排查用Vivado Hardware Manager連上JTAG檢查配置地址寄存器。如果你用的7系列UG570里面有個命令xil_rdcfg和xil_wrcfg可以用直接讀WBSTAR的值和預(yù)期比對。我印象最深的一次是花了很長時間檢查ICAP例化最后發(fā)現(xiàn)是WBSTAR地址左移兩位的問題這種低級錯誤往往最隱蔽。6.4 坑四斷電導(dǎo)致Update區(qū)損壞但擦除策略不合理表象升級過程中有時會出“部分扇區(qū)擦除了寫了一半另外一半還是舊數(shù)據(jù)”的現(xiàn)場。根因大固件升級時擦除和編程的時間窗口拉得很長。如果在這個窗口中發(fā)生掉電Update區(qū)的數(shù)據(jù)是一團(tuán)漿糊——有些扇區(qū)是舊的、有些是擦除后的0xFF、有些是半寫狀態(tài)。解決設(shè)計上升級流程必須支持“基于Sector的雙緩沖升級”。比如你的Update區(qū)分為A/B兩個子區(qū)先在一個子區(qū)完成整包的擦除寫入校驗(yàn)校驗(yàn)成功后再切斷另一個子區(qū)的配置啟動路徑。如果中途掉電最壞情況就是當(dāng)前子區(qū)壞了另一個子區(qū)還能啟動不會觸發(fā)IPROG。這在兩個子區(qū)都可用的情況下把掉電風(fēng)險窗口縮短到只有一次切換動作的幾十毫秒。6.5 坑五驅(qū)動函數(shù)沒有做“至少一次”的解鎖保護(hù)表象設(shè)備在某些批次上工作正常另一些批次升級必失敗重新上電后又恢復(fù)。根因Flash的狀態(tài)寄存器保護(hù)位可能被上電時外部下拉或上拉電阻設(shè)置成了保護(hù)狀態(tài)不同批次的Flash上電默認(rèn)值有差異。你把解鎖邏輯省略了直接把一切初始化建立在“保護(hù)位關(guān)閉”的假設(shè)上。解決每次寫操作前無條件執(zhí)行“WREN→寫狀態(tài)寄存器0x00→讀回確認(rèn)”三步曲寧可多花幾毫秒也不能賭默認(rèn)狀態(tài)。后續(xù)在加電初始化階段同步讀回狀態(tài)寄存器并斷言發(fā)現(xiàn)保護(hù)位異常就亮告警燈把問題在升級前暴露。這五個坑覆蓋了我在遠(yuǎn)程升級項(xiàng)目上遇到的80%問題。剩下的20%基本都是網(wǎng)絡(luò)協(xié)議層和上層應(yīng)用的問題不在本文討論范圍內(nèi)但內(nèi)核里的邏輯思想是通用的永遠(yuǎn)假設(shè)最壞情況會發(fā)生然后為最壞情況留好后路。7. 寫在最后一個建議和一點(diǎn)體會如果只讓我留一條建議那就是在N25Q128這種SPI Flash上做遠(yuǎn)程升級把驅(qū)動的每一條指令都當(dāng)成“可能被下一次上電打斷”的原子操作來設(shè)計。寫使能、狀態(tài)輪詢、擦除、編程、讀回校驗(yàn)每一步都要有軍規(guī)一樣的時序紀(jì)律缺失任何一環(huán)都可能把一個簡單的寫Flash任務(wù)變成拉鋸戰(zhàn)。實(shí)際項(xiàng)目里遠(yuǎn)程升級的代碼量并不大難的是把各種異常路徑想清楚、測徹底。建議在實(shí)驗(yàn)室做一個掉電矩陣測試在升級流程的每個狀態(tài)隨機(jī)斷電然后上電確認(rèn)系統(tǒng)一定回到Golden區(qū)并能正常啟動。這個測試如果能連續(xù)跑上一百次不出問題我對現(xiàn)場的信心就很足了。好了這一路的坑和東XC細(xì)節(jié)都在這了。祝各位現(xiàn)場升級順利永不翻車。