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