
在ZYNQ的開發(fā)流程里把程序固化到Flash這步幾乎每個人都會栽幾次跟頭。哪怕你已經(jīng)把PL端和PS端的工程調(diào)得穩(wěn)穩(wěn)當當只要沒把鏡像寫進Flash板子一斷電就回到解放前一切都得重新來過。這篇東西不聊虛的就從“為什么燒寫老失敗”和“到底該怎么燒”這兩個角度把ZYNQ燒寫Flash這件事拆開揉碎講清楚。這篇內(nèi)容適合剛拿到ZYNQ開發(fā)板、準備從JTAG調(diào)試轉(zhuǎn)向固化啟動的工程師也適合那些已經(jīng)燒過幾次但偶爾還會被“Target DLL has been cancelled”這類報錯折磨的老手。我會把鏡像組成、燒寫方式選型、完整實操步驟、NAND與QSPI的差異、高頻報錯排查以及在線升級的擴展思路都過一遍。你可以把它當成一份可以直接抄作業(yè)的手冊遇到問題回來翻對應(yīng)章節(jié)就行。1. 燒寫之前先搞清楚ZYNQ啟動鏡像里裝了什么很多人上來就點“Program Flash Memory”燒完發(fā)現(xiàn)板子起不來然后開始懷疑Flash壞了、懷疑焊接虛了、懷疑人生。其實大部分情況下不是硬件問題而是根本沒搞清楚ZYNQ的啟動流程和鏡像結(jié)構(gòu)。這塊必須先從根上講明白。1.1 一條完整的啟動鏈BootROM、FSBL、SSBL與AppZYNQ-7000的啟動流程是固定的三段式。芯片上電后片內(nèi)固化的BootROM會先根據(jù)MIO引腳上的啟動模式設(shè)置決定從QSPI、NAND、SD還是JTAG加載下一級鏡像。BootROM本身只有192KB左右它干不了太多事只負責把第一級引導(dǎo)程序搬進片內(nèi)OCMOn-Chip Memory通常只有256KB并跳轉(zhuǎn)過去。這第一級引導(dǎo)程序就是我們常說的FSBLFirst Stage Boot Loader。FSBL由Xilinx提供源碼在SDK里編譯工程時會自動生成。它完成的工作很明確初始化PS端的必要外設(shè)比如MIO、DDR、GIC中斷控制器等加載PL端的bitstream如果需要的話然后把SSBLSecond Stage Boot Loader一般是U-Boot或者裸機App從啟動介質(zhì)搬運到DDR里最后跳轉(zhuǎn)執(zhí)行。后面的事就看你的系統(tǒng)設(shè)計了。跑Linux的話SSBL就是U-Boot再由U-Boot引導(dǎo)內(nèi)核和設(shè)備樹跑裸機或RTOSFSBL可以直接跳轉(zhuǎn)到你的應(yīng)用程序。整個鏈條里任何一環(huán)出了問題板子就表現(xiàn)為“沒反應(yīng)”“串口無輸出”或者“啟動到一半卡死”。1.2 BOOT.bin是怎么生成的Bootgen與BIF文件燒寫之前必須先把多個鏡像打包成一個文件。Xilinx的工具叫Bootgen在Vivado SDK里通過“Create Boot Image”界面操作本質(zhì)上是在調(diào)用一條bootgen命令行。打包所需的輸入至少包含F(xiàn)SBL的elf文件再加上你想加載的bitstream和應(yīng)用程序elf或U-Boot輸出結(jié)果就是BOOT.bin。如果你不想每次都在GUI里點來點去可以直接寫B(tài)IF文件然后用命令打包。一個典型的BIF文件長這樣the_ROM_image: { [bootloader] ./fsbl.elf ./system.bit ./u-boot.elf }然后在命令行執(zhí)行bootgen -image boot.bif -o i BOOT.BIN -w on-w on表示覆蓋已存在的同名文件。這個流程在PetaLinux里也被封裝好了平時我們生成BOOT.bin其實就是同一套邏輯。關(guān)鍵是理解BOOT.bin不是單個程序而是一個按BootROM和FSBL約定格式組織起來的鏡像容器順序、地址、校驗都不能亂。1.3 啟動模式選擇QSPI、NAND、SD與JTAGZYNQ的啟動模式是通過MIO[6:2]引腳電平組合決定的。不同板子可能拉高拉低的配置不一樣但只要明白一點硬件上把啟動模式設(shè)置成什么BootROM就從什么介質(zhì)去讀鏡像。QSPI模式從片外QSPI Flash讀取BOOT.bin適合量產(chǎn)和小型系統(tǒng)。NAND模式從NAND Flash讀取適合大容量存儲需求的場景。SD模式從SD卡啟動開發(fā)階段非常方便改完鏡像直接換卡或者重新拷入即可。JTAG模式不加載非易失存儲直接用JTAG把FSBL和App灌進芯片用于調(diào)試。開發(fā)階段建議先做SD卡啟動或JTAG調(diào)試把邏輯和軟件都調(diào)穩(wěn)了最后再做Flash固化。千萬別一上來就往Flash燒否則每次改一點都要擦寫一次浪費時間不說Flash的擦寫次數(shù)雖然多但沒必要這么糟蹋。2. 燒寫方式選型不是只有JTAG一條路“燒寫到Flash”這個動作本身在實際工程里有好幾種實現(xiàn)路徑。每種方式的適用場景、前置條件和風(fēng)險都不一樣選錯了會非常難受。2.1 JTAG燒寫最直接、最適合開發(fā)階段JTAG燒寫就是通過Xilinx的Vivado Hardware Manager或者SDK的“Program Flash Memory”功能把鏡像文件直接寫進連接在ZYNQ上的Flash芯片。這種方式不需要先在板子上跑任何程序只要JTAG鏈路通、Flash型號被工具識別就能寫。它的優(yōu)點是操作簡單、無需預(yù)先燒入引導(dǎo)程序非常適合開發(fā)初期驗證鏡像。缺點是速度慢尤其大容量Flash寫起來非常磨人另外它對Flash型號和連接有要求不是所有板子都能被工具自動識別。我見過有人用JTAG燒寫128Mb的QSPI Flash光寫鏡像就花了快二十分鐘中途還因為USB線接觸不良斷了一次整個人心態(tài)都崩了。2.2 U-Boot下燒寫量產(chǎn)與現(xiàn)場恢復(fù)的利器如果板子上已經(jīng)有一份能正常啟動的U-Boot那后續(xù)的Flash燒寫就簡單多了。U-Boot里自帶Flash操作命令配合TFTP或者FAT文件系統(tǒng)可以把新鏡像下載到DDR再從DDR寫入Flash。整個過程不用重新連接JTAG甚至可以通過網(wǎng)絡(luò)遠程操作。在U-Boot命令行里QSPI Flash的常見操作是這組命令的組合# 探測QSPI Flash確認型號和大小 sf probe # 通過TFTP下載BOOT.bin到DDR地址0x10000000 tftp 0x10000000 BOOT.bin # 擦除從0地址開始、大小1MB的Flash區(qū)域 sf erase 0x0 0x100000 # 把DDR里的數(shù)據(jù)寫入Flash sf write 0x10000000 0x0 0x100000這套流程看起來簡單但有幾個細節(jié)要注意。sf erase的地址和長度必須按照Flash的扇區(qū)或塊大小對齊不同型號的擦除粒度不一樣。另外TFTP下載前要在U-Boot里設(shè)好IP地址、掩碼和服務(wù)器地址否則文件拉不下來。2.3 系統(tǒng)內(nèi)升級在線升級設(shè)計的基礎(chǔ)再進一步如果設(shè)備已經(jīng)部署到現(xiàn)場總不能每次升級都拆機接JTAG這時候就需要設(shè)計一套在線升級機制。常見做法是讓運行中的系統(tǒng)Linux或RTOS接收升級包寫入一個固定的升級分區(qū)然后重啟進入U-Boot由U-Boot判斷升級標志并把新鏡像搬到啟動分區(qū)。在線升級的復(fù)雜度等級比普通燒寫高不少但它幾乎是量產(chǎn)產(chǎn)品的必經(jīng)之路。這塊我在第6節(jié)再展開講現(xiàn)在先記住一個結(jié)論JTAG燒寫是手段U-Boot燒寫是效率在線升級才是量產(chǎn)正解。3. 完整實操Vivado SDK JTAG燒寫QSPI Flash一步步來現(xiàn)在進入正題講一套我從頭到尾實測過多次的操作流程。這套流程適用于絕大多數(shù)ZYNQ-7000開發(fā)板Flash型號以常見的Winbond W25Q128、Micron N25Q128、ISSI IS25LP128等QSPI Nor Flash為例。3.1 環(huán)境準備與硬件檢查動手之前先把環(huán)境和硬件狀態(tài)確認一遍這能省掉后面九成的問題。軟件方面Vivado和SDK的版本要對應(yīng)我目前常用Vivado 2020.2和2023.1兩種版本的SDK界面略有差異但核心操作邏輯一致。目標板通過JTAG仿真器連到電腦常見的有Xilinx原廠Platform Cable USB II、Digilent JTAG-HS3或者板載的FTDI芯片大多數(shù)國產(chǎn)開發(fā)板是這個方案。硬件方面重點檢查三件事板子供電正常核心電壓、DDR電壓、IO電壓都穩(wěn)定。如果板子上有電流表養(yǎng)成上電后看一眼電流的習(xí)慣ZYNQ空載跑FSBL時的電流一般在幾百毫安級別電流異常偏低說明DDR都沒初始化直接燒寫大概率失敗。JTAG鏈路通暢。打開Vivado Hardware Manager能看到設(shè)備列表里有xc7z010、xc7z020之類的芯片編號同時能看到連接的Flash型號老版本SDK需要把Flash型號也加入掃描鏈。如果這里看不到設(shè)備先別急著燒查USB線、驅(qū)動和仿真器供電。啟動模式跳線設(shè)置在正確的位置。如果你的板子當前啟動模式在SD卡或者NAND而你要燒QSPI最好把跳線撥到QSPI模式再操作。這不是燒寫動作本身需要而是為了避免燒完后驗證時啟動介質(zhì)不對讓人誤以為燒寫失敗。3.2 打開Program Flash Memory并選擇鏡像在Vivado SDK中點擊菜單欄Xilinx - Program Flash Memory會彈出燒寫對話框。這個對話框里的選項就是整個燒寫動作的核心參數(shù)。第一個要選的是Image File也就是你要燒寫的鏡像文件。它可以是BOOT.bin也可以是MCS文件甚至可以是單獨的BIN文件。我的習(xí)慣是直接燒BOOT.bin因為它包含F(xiàn)SBL、bitstream和U-Boot一次燒完省事。第二個要選的是Flash Type。SDK會根據(jù)JTAG鏈上的設(shè)備自動識別常見的是qspi-x4-single如果工具識別不出來就需要手動選擇。這里要注意如果你的硬件設(shè)計里QSPI Flash連接在MIO1-6上且使用了x4模式就選qspi-x4-single如果只用了single模式選qspi-x1-single。選錯的話燒寫可能能完成但啟動時BootROM讀不到正確數(shù)據(jù)。第三個必選的是FSBL File。SDK燒寫Flash時并不像燒寫裸機程序那樣直接把鏡像扔進Flash而是需要一個中間代理程序先把FSBL加載到OCM里運行起來由FSBL來初始化PS端的外設(shè)尤其是QSPI控制器然后再由運行在PS上的FSBL把鏡像數(shù)據(jù)寫入Flash。所以這里選擇的FSBL文件必須和當前硬件匹配。它可以是SDK里任何能編譯通過且用到了當前硬件的fsbl工程生成的elf。如果選擇錯誤燒寫過程會報各種莫名其妙的錯誤比如第5節(jié)會講的“Target DLL has been cancelled”十有八九就出在這個FSBL上。3.3 關(guān)鍵參數(shù)地址、擦除與校驗策略Flash起始地址默認是0x0通常不需要改。如果你的設(shè)計里把BOOT.bin放在了Flash的非零偏移處那你要確保BootROM的相應(yīng)啟動模式支持這個偏移否則就是自己給自己挖坑。做開發(fā)時一律從0地址開始最穩(wěn)妥。燒寫操作里還有幾個選項需要注意Blank Check空檢查燒寫前檢查目標區(qū)域是否為空可以提前發(fā)現(xiàn)Flash擦除是否徹底。Erase Before Programming擦除后再編程建議勾選。Flash只能從1寫0如果不先擦除直接寫入會殘留舊數(shù)據(jù)導(dǎo)致校驗失敗。Verify After Programming燒寫后校驗強烈建議勾選。燒寫完成后回讀Flash數(shù)據(jù)與原始鏡像逐字節(jié)比對能從硬件層面確認鏡像完整。我遇到過一次燒寫過程顯示完成但板子死活起不來的情況后來發(fā)現(xiàn)是忘記了勾選Verify而實際上Flash的某個扇區(qū)因為壞塊導(dǎo)致數(shù)據(jù)寫入失敗。從那以后我的燒寫參數(shù)都固定勾選Erase和Verify一次燒寫多花幾十秒但換來的是可靠的結(jié)果值。3.4 順利燒寫時你會看到的日志點擊OK后SDK的Console窗口會輸出一系列信息。正常流程大致是這樣Connecting to target... -----開始燒寫----- FSBL文件加載成功 Offset: 0x00000000, Size: 0x000A0000 擦除Flash... 編程Flash... 校驗Flash... Flash編程完成。注意中間如果出現(xiàn)任何紅色的Error都別繼續(xù)往下走了先停下來分析原因。有一類錯誤是Flash ID沒被識別到可能是因為Flash型號不在Xilinx的列表里也可能是因為板子的Flash供電沒給上。遇到這類錯誤先確認硬件再折騰軟件順序別搞反。4. 換個介質(zhì)NAND Flash燒寫要點與型號適配QSPI Nor Flash是ZYNQ開發(fā)板最常見的配置但在實際產(chǎn)品里NAND Flash也開始用得越來越多。原因很簡單同樣是幾十塊錢的成本NAND能買到幾Gb甚至幾十Gb的容量而QSPI Nor通常到128Mb16MB就差不多到頭了。如果你的系統(tǒng)需要存儲大量數(shù)據(jù)比如嵌入式數(shù)據(jù)庫、本地緩存、歷史記錄等NAND幾乎是必然選擇。4.1 ZYNQ對NAND的支持情況與型號選擇ZYNQ-7000系列內(nèi)部集成了NAND控制器支持ONFI 1.0兼容的SLC NAND Flash。這里要劃重點是SLC不是MLC也不是TLC。雖然市面上一些MLC型號物理上也能用但Xilinx的FSBL和驅(qū)動并沒有針對MLC做完整的壞塊管理、位翻轉(zhuǎn)處理和ECC校驗支持強行使用風(fēng)險極高現(xiàn)場出了問題非常難查。選型上推薦Micron的MT29F系列、Winbond的W29N系列、Spansion現(xiàn)Cypress/Infineon的S34ML系列。容量方面256MB到1GB范圍的產(chǎn)品在ZYNQ系統(tǒng)里用得最多太大容量反而會引入頁映射和分區(qū)管理復(fù)雜度收益不大。另外要注意NAND對FSBL的要求比QSPI高不少。QSPI模式下FSBL只要能把鏡像讀出來就行簡單粗暴NAND模式下FSBL必須處理ECC校驗和壞塊跳過Xilinx提供的FSBL源碼里默認帶了這些功能但前提是你選了正確的BSP配置。如果在創(chuàng)建FSBL工程時沒有選對NAND驅(qū)動參數(shù)燒寫出來的鏡像即使寫進Flash啟動時也會因為ECC校驗失敗而卡住。4.2 NAND燒寫的坑用Vivado SDK的Program Flash Memory燒寫NAND整體流程和燒QSPI幾乎一樣都是先加載FSBL再寫Flash但有幾個非常折磨人的差異點。首先是速度NAND的編程單位是頁Page典型頁大小是2KB或4KB雖然單頁編程時間只有幾百微秒但批量寫入時要不斷照管ECC和壞塊實測下來速度往往只有QSPI的幾分之一。其次是擦除和寫入的粒度不同。不管QSPI還是NAND操作前都要擦除但NAND的擦除單位是塊Block一個塊通常是64頁或128頁即128KB或256KB。如果你只想更新部分數(shù)據(jù)也必須整塊擦除再整體寫入這對在線升級的分區(qū)設(shè)計有很大影響。第三是壞塊處理。NAND出廠就可能有壞塊使用過程中還會產(chǎn)生新壞塊。SDK燒寫NAND時如果遇到壞塊會報錯終止不會自動跳過這意味著你燒寫前必須確保目標區(qū)域沒有壞塊否則就要用專門的燒寫工具或U-Boot下的nand命令來寫。我實際工作中燒NAND基本都是走U-Boot很少直接用SDK。在U-Boot下燒寫NAND的命令組合長這樣# 從TFTP下載鏡像到DDR tftp 0x20000000 BOOT.bin # 擦除NAND從0地址開始、大小1MB的區(qū)域 nand erase 0x0 0x100000 # 寫入NAND nand write 0x20000000 0x0 0x100000NAND的ECC模式一般由FSBL或U-Boot在啟動時配置好燒寫前用nand info確認當前ECC方式與Flash顆粒匹配避免校驗失敗。4.3 大容量Flash的分區(qū)規(guī)劃建議NAND容量大了以后必然要涉及分區(qū)規(guī)劃。我的建議是整個Flash按用途分成三個區(qū)啟動鏡像區(qū)存放BOOT.bin大小1-2MB內(nèi)核與設(shè)備樹區(qū)如果采用U-Boot從NAND引導(dǎo)內(nèi)核需要單獨劃分大小幾十MB用戶數(shù)據(jù)區(qū)剩余全部空間按文件系統(tǒng)或裸數(shù)據(jù)管理。分區(qū)規(guī)劃這件事最好在設(shè)計階段就定下來別等軟件寫完了再補否則后面一次升級就要重新設(shè)計方案改起來牽一發(fā)動全身。5. 高頻報錯實錄與排查方法這部分是整篇文章里我最有底氣的地方因為下面這些錯誤我基本都親手踩過有的坑踩完還特意復(fù)現(xiàn)了幾次就是為了搞清楚根因。按出現(xiàn)頻率從高到低我把最典型的幾個報錯和排查思路整理出來。5.1 ErrorFlash download failed - Target DLL has been cancelled這個報錯在SDK燒寫時出現(xiàn)頻率極高而且原因非常多。Target DLL被取消本質(zhì)上是燒寫過程中JTAG鏈路或目標芯片狀態(tài)出現(xiàn)了異常導(dǎo)致工具無法繼續(xù)控制目標。我總結(jié)下來最常見的原因是FSBL文件有問題。前面說過燒寫Flash時SDK會先把FSBL加載到OCM運行由FSBL初始化QSPI/NAND控制器。如果你的FSBL不匹配芯片初始化到一半就卡死了JTAG自然無法繼續(xù)操作于是報Target DLL cancelled。排查順序是這樣確認JTAG能正常連接目標芯片單獨用Hardware Manager做一次設(shè)備掃描如果掃描都失敗先解決連接問題。確認選擇的FSBL文件正確。最穩(wěn)妥的做法是在SDK里專門創(chuàng)建或打開一個與當前硬件對應(yīng)的fsbl工程編譯一次然后選這個elf文件。檢查板子的啟動模式引腳。有些板子在JTAG模式下會禁用某些時鐘導(dǎo)致FSBL加載后初始化外部存儲失敗。把啟動模式臨時切到QSPI或者SD往往能繞開這個問題。用示波器或邏輯分析儀看Flash的片選信號和時鐘確認FSBL運行過程中確實產(chǎn)生了預(yù)期的讀寫時序。5.2 ErrorA valid FSBL file is required for flash operation這個報錯就直白多了SDK在燒寫Flash前必須有一個FSBL文件作為代理你沒給或者給的文件無效它就罷工。正常情況下在Program Flash Memory對話框里選擇FSBL File為fsbl.elf即可。但有幾種情況要注意如果SDK工程被build過一次后又改了硬件平臺舊的fsbl.elf可能引用了已經(jīng)不存在的硬件描述直接報錯還有就是把不同版本的fsbl.elf混用比如Vivado 2019.2生成的elf拿到2023.1里用大概率也會報錯。解決辦法就是重新編譯一份當前工程對應(yīng)的fsbl.elf。5.3 擦除失敗或超時QSPI Flash的擦除是按扇區(qū)通常4KB或塊通常64KB進行的。如果你的鏡像跨了多個扇區(qū)邊界SDK會逐步擦除在這個過程中如果Flash電壓不穩(wěn)或時鐘頻率過高擦除操作可能超時或失敗。這類問題一個非常常見的誘因是電源質(zhì)量。ZYNQ開發(fā)板上3.3V給Flash供電如果板上電源波動大Flash在擦除過程中進入低電壓鎖定狀態(tài)就會表現(xiàn)為擦除失敗。排查方法很簡單擦除時用示波器監(jiān)測3.3V電源軌看有沒有掉坑。另一個因素是JTAG頻率過高。SDK里可以設(shè)置JTAG時鐘頻率如果冗余太少把頻率從默認的15MHz降到6MHz或者3MHz往往能解決很多莫名其妙的失敗。別嫌慢Flash燒寫本來就不是一個追求速度的操作。5.4 燒完了卻啟動不了燒寫完成后啟動失敗這個是排查難度最大的問題因為燒寫環(huán)節(jié)一切正常報錯卻在啟動階段。按我的經(jīng)驗檢查優(yōu)先級如下啟動模式引腳設(shè)置是否正確。最常見的情況就是跳線還留在JTAG模式或者SD模式BootROM根本不會去讀Flash。鏡像內(nèi)容是否完整。用Hardware Manager讀回Flash數(shù)據(jù)和原始BOOT.bin比對看有沒有漏數(shù)據(jù)。如果用的是舊版SDK有時候擦除后寫入的數(shù)據(jù)會在尾部截斷比對能直接發(fā)現(xiàn)。FSBL里DDR初始化是否匹配。如果你的BOOT.bin里的FSBL是從某個模板工程生成的而模板的DDR顆粒和實際板子不一樣FSBL在初始化DDR時失敗后續(xù)的U-Boot或App根本沒法跑起來。bitstream是否真正被加載。有的設(shè)計里PL被配置成user modeFSBL加載完bitstream后繼續(xù)跑軟件如果bitstream本身有問題可能系統(tǒng)看起來啟動了但外設(shè)不工作。這種情況可以單獨燒一個只含bitstream的鏡像來驗證PL部分。5.5 制作SD卡啟動鏡像的補充說明不少人一看到“flash燒寫”就以為只能往Flash里寫其實ZYNQ從SD卡啟動也是開發(fā)調(diào)試階段的常用手段。制作SD卡啟動卡的過程實際上也是把BOOT.bin、boot.scr、image.ub等文件放進FAT分區(qū)和“燒寫”概念上類似只是介質(zhì)變了。用PetaLinux時一條命令就能完成打包petalinux-package --boot --fsbl --fpga --u-boot --force生成BOOT.bin之后把BOOT.bin、boot.scr、image.ub拷貝到SD卡第一個FAT分區(qū)把啟動模式撥到SD上電就能啟動。這套流程對調(diào)試內(nèi)核配置特別方便改內(nèi)核只需要替換image.ub幾十秒就能完成一輪啟動測試比反復(fù)擦寫Flash高效太多。5.6 高頻錯誤速查表錯誤現(xiàn)象最可能原因首要排查動作Flash download failed - Target DLL cancelledFSBL不匹配或JTAG鏈路不穩(wěn)確認FSBL并降低JTAG頻率A valid FSBL file is required未選或選錯FSBL選擇當前工程的fsbl.elf燒寫完成但校驗失敗Flash擦除不徹底或?qū)懭霑r序問題勾選Erase Before Programming啟動無反應(yīng)Boot模式引腳錯誤或鏡像不完整檢查跳線并回讀比對FlashU-Boot下sf probe失敗QSPI Flash型號不支持或硬件連接錯誤檢查原理圖和Flash供電6. 進階思路基于ZYNQ的Bootloader在線升級設(shè)計聊完燒寫本身我再延伸一個每個產(chǎn)品化項目都躲不開的話題在線升級。調(diào)試階段用JTAG燒寫、用SD卡啟動都沒問題但產(chǎn)品一旦部署出去在線升級就是剛需。下面這套方案是我在多個項目里落地過的直接參考即可。6.1 為什么要做在線升級設(shè)備部署在現(xiàn)場以后不可能每次更新固件都派人拆機接JTAG。哪怕只是改一個參數(shù)如果系統(tǒng)架構(gòu)不支持在線升級工程師就得背著電腦、仿真器和一把螺絲刀出差成本極高。在線升級的本質(zhì)是讓運行中的系統(tǒng)自己把新鏡像寫進Flash然后在下次啟動時切換到新版本。這個需求的工程難點在于可靠性。升級過程中如果掉電、斷網(wǎng)、鏡像損壞設(shè)備就變磚了。所以設(shè)計在線升級方案時核心要解決的不是“能不能升級”而是“升級失敗后能不能恢復(fù)”。6.2 一個實用的分區(qū)方案我常用的分區(qū)方案是這樣的QSPI或NAND的起始區(qū)域放一個固定不變的Boot區(qū)里面是BootROM所需的FSBL和U-Boot。這個區(qū)域平時不參與升級除非是重大架構(gòu)調(diào)整否則永遠不動。Boot區(qū)之后劃分兩個App區(qū)A區(qū)和B區(qū)交替存放應(yīng)用程序鏡像。當前運行在A區(qū)新版本就寫到B區(qū)寫入完成后置一個升級標志重啟后U-Boot讀取標志引導(dǎo)B區(qū)。如果B區(qū)啟動失敗U-Boot回滾引導(dǎo)A區(qū)設(shè)備回到舊版本依然可用。這個方案在業(yè)界叫A/B分區(qū)或雙備份系統(tǒng)實現(xiàn)起來不復(fù)雜但對啟動流程的理解要求比較高U-Boot需要能從環(huán)境變量或特定Flash地址讀取“下次啟動哪個分區(qū)”的標志。6.3 實現(xiàn)要點升級流程的落地路徑大概是這樣的運行在Linux或RTOS中的應(yīng)用從服務(wù)器下載新鏡像到文件系統(tǒng)寫入換一個分區(qū)寫鏡像然后設(shè)置啟動標志最后觸發(fā)重啟。U-Boot側(cè)需要定制啟動腳本讀取標志位并引導(dǎo)對應(yīng)分區(qū)的鏡像。有幾個細節(jié)容易踩坑。第一個是鏡像完整性校驗。下載下來的鏡像寫入Flash之前一定要做校驗CRC32或MD5校驗通過才允許寫入否則直接丟棄。第二個是斷電保護寫Flash的過程中掉電是最危險的最穩(wěn)妥的辦法是先把鏡像寫到DDR里確認完整后再一次性寫入Flash雖然不能完全避免掉電風(fēng)險但能大幅縮短Flash寫窗口。U-Boot側(cè)的關(guān)鍵命令是這樣的# 設(shè)置下次啟動分區(qū)為B區(qū) setenv boot_part b saveenv # 引導(dǎo)邏輯 if test $boot_part b; then run boot_b else run boot_a fi6.4 風(fēng)險控制與實測建議在線升級的測試不能只在實驗室里做“正常流程”必須刻意制造異常場景來驗證恢復(fù)能力。我自己測試時最常用的一種方法是在升級寫到一半的時候直接斷電然后重新上電看設(shè)備能不能自動回滾到舊版本。如果回滾邏輯寫得不健壯這種測試一兩次就會暴露問題。另外要強調(diào)的是Boot區(qū)的FSBL和U-Boot雖然不參與常規(guī)升級但它們本身也有需要更新的場景比如調(diào)整DDR時序、增加啟動參數(shù)所以架構(gòu)上還是要預(yù)留一個“特殊升級模式”通過按鍵或串口命令進入專門更新Boot區(qū)內(nèi)容。這個模式一定要有明顯的標志位和保守策略否則一旦Boot區(qū)寫壞整塊板子就只能返廠用JTAG恢復(fù)。最后分享一點個人經(jīng)驗燒寫Flash這件事看著是小事但每個環(huán)節(jié)都藏著坑。我最早做ZYNQ項目時因為FSBL文件選錯連續(xù)報同樣的Target DLL錯誤折騰了兩天才發(fā)現(xiàn)是在另外一個工程目錄里Build的FSBL和當前硬件根本不匹配。從那以后我養(yǎng)成了一個習(xí)慣每塊板子建一個專門的fsbl工程名字里帶硬件版本號改版就重新Build絕不沿用舊文件。還想提醒的是如果你的板子支持多種啟動方式建議在Flash里放一份穩(wěn)定版本之后日常開發(fā)切到SD卡啟動。這樣Flash里的固件始終是“最后可用狀態(tài)”就算SD卡里的新版本改壞了也能隨時切回Flash版本救磚。這個習(xí)慣幫我避免過好幾次項目延期在這里一并寫出來希望對你有用。