
搞過ZYNQ的朋友應該都有這種體驗在Vivado里把硬件工程跑通了、SDK里應用程序也編譯過了仿真一切正常結果到了要把程序固化到flash這一步突然卡住。網(wǎng)上搜一圈要么是零零散散的截圖要么是只說“點Program Flash就行”的教程真到自己動手各種報錯撲面而來。這篇文章我就把自己實際燒寫ZYNQ程序到flash的完整過程、踩過的坑、排查思路都整理出來從啟動流程到FSBL的作用從QSPI和NAND的選擇到具體的燒寫命令一次性講透。這里要說明一下這篇文章面向的是所有用ZYNQ做開發(fā)的朋友不管是剛上手的新手還是被燒寫問題折磨的老手應該都能從中找到自己需要的東西。內(nèi)容以Xilinx官方工具鏈Vivado SDK/Vitis為主線覆蓋QSPI Flash和NAND Flash兩種最常見的固化場景同時也會提到SD卡啟動的替代方案以及在工程實踐中經(jīng)常碰到的幾個典型報錯。1. 燒寫之前必須搞清楚的啟動流程很多人在燒寫flash這一步翻車根源不是操作不對而是對整個啟動流程理解不夠。ZYNQ的啟動機制和單片機完全不一樣別拿STM32那套思路往上面套。1.1 ZYNQ的啟動鏡像到底由什么組成先看一個最核心的問題ZYNQ從flash啟動時硬件上電后到底執(zhí)行了什么答案不是你的應用程序而是一級引導程序后面跟著FSBL、bitstream、SSBL通常是U-Boot和應用程序。整個鏡像文件被稱為BOOT.bin它的內(nèi)部結構大致是啟動頭Boot Header包含鏡像描述信息、加密選項、校驗值等是BootROM解析鏡像的入口。FSBLFirst Stage Boot Loader由Xilinx提供模板負責初始化DDR、時鐘、MIO等并加載后續(xù)鏡像。硬件比特流bitstream如果PL端有邏輯比特流由FSBL加載到PL。第二階段引導程序或應用程序U-Boot或裸機程序最終交到用戶程序執(zhí)行。上電時ZYNQ片內(nèi)的BootROM會先運行根據(jù)MIO引腳的電平配置決定從哪個接口啟動比如QSPI、NAND、SD或者JTAG。BootROM把BOOT.bin開頭的一段代碼讀入片上RAM然后跳轉執(zhí)行也就是進入FSBL流程。所以你會看到SDK里燒寫flash時強制要求先選擇一個FSBL文件提示“A valid FSBL file is required for flash operation”。這不是Xilinx故意為難你而是燒寫工具必須用FSBL來完成flash的初始化和擦寫操作。1.2 QSPI、NAND和SD卡啟動介質怎么選ZYNQ支持的啟動介質主要是這幾種QSPI Flash、NAND Flash、SD卡還有JTAG調試模式。選哪個取決于你的應用場景。QSPI NOR Flash是最常用的。它的優(yōu)點在于接口簡單、讀取速度快、可靠性高而且Xilinx的IP核支持得很完善。容量一般在16MB到128MB之間對于中小型工程綽綽有余。大部分開發(fā)板比如米爾、正點原子、黑金這些板載的都是QSPI Flash。如果是裸機程序或者Linux系統(tǒng)比較精簡QSPI完全夠用。NAND Flash的優(yōu)勢在容量動輒512MB甚至更大適合存放大的文件系統(tǒng)、視頻數(shù)據(jù)等。但NAND本身有壞塊管理、ECC校驗等一堆問題而且ZYNQ支持的NAND型號有限在Xilinx官方文檔UG585里有詳細的兼容列表。買芯片前一定要查一下型號在不在支持范圍內(nèi)否則就算燒寫成功也可能啟動異常。SD卡啟動是另一條路嚴格來說不算燒寫flash但也經(jīng)常被用來運行Linux系統(tǒng)。SD卡容量大、方便更換開發(fā)階段尤其好用。很多基于ZYNQ的bootloader在線升級設計就是SD卡啟動Linux配合應用程序去更新QSPI里的鏡像。我的建議是產(chǎn)品量產(chǎn)用QSPI固化開發(fā)調試用SD卡NAND除非有容量剛需否則盡量避開。理由很簡單QSPI在工具鏈支持上最成熟出問題的概率最低這一點在后面的燒寫步驟里會有體現(xiàn)。2. 燒寫方案選型與工具分析搞清楚啟動流程之后接下來要選燒寫方案。ZYNQ燒寫flash的途徑主要有三條SDK/Vitis圖形界面燒寫、命令行工具燒寫、第三方燒寫器燒寫。三者各有適用場景不能一概而論。2.1 SDK圖形界面燒寫和命令行燒寫怎么取舍圖形界面燒寫是入門首選。在Vitis舊版SDK里連接開發(fā)板右鍵點擊FSBL工程選擇Run As → Launch on Hardware先把FSBL下載到板上跑起來然后選擇Xilinx → Program Flash填寫flash類型、鏡像路徑等參數(shù)點Program就完事。命令行燒寫用的是program_flash工具它支持更靈活的腳本化操作適合產(chǎn)線批量燒寫和自動化集成。比如可以寫成一條命令program_flash -f BOOT.bin -offset 0 -flash_type qspi-x4-single -fsbl fsbl.elf -cable type xilinx_tcf url TCP:127.0.0.1:3121這條命令的效果和圖形界面是一樣的但可以放進CI腳本里或者封裝成產(chǎn)線工具效率和可維護性都高得多。另外還有一個更新一點的方案是用xsdb programmer命令行。xsdb是Xilinx的調試服務器配合tcf agent可以實現(xiàn)遠程燒寫這在板卡不在手邊的時候特別有用。2.2 JTAG、串口、第三方燒寫器到底有什么區(qū)別燒寫通道的選擇同樣關鍵。ZYNQ最常用的燒寫通道是JTAG因為JTAG直接鏈到CPU的調試訪問端口DAP可以控制CPU執(zhí)行任意代碼包括FSBL。通過JTAG加載FSBL后FSBL再對接flash讀寫這就是“JTAG燒寫”的本質。串口燒寫則是另一種思路它不著眼于JTAG鏈而是通過UART把鏡像傳給一個已經(jīng)運行起來的引導程序比如U-Boot由U-Boot把數(shù)據(jù)寫入flash。這種方式在現(xiàn)場升級時用得比較多因為現(xiàn)場不一定有JTAG調試器。但你搜“串口燒寫失敗”會發(fā)現(xiàn)一堆帖子原因主要集中在波特率不匹配、流控打開、鏡像帶校驗頭導致U-Boot拒絕寫入等方面。我個人建議是能用JTAG就用JTAG串口燒寫適合用在產(chǎn)品已經(jīng)交付后的現(xiàn)場升級場景。第三方燒寫器比如之前相關搜索里出現(xiàn)的BeeProg2屬于通用編程器直接夾在flash芯片引腳上燒。這種方案通常用于空板貼片前的預燒寫或者flash焊在板上但JTAG鏈路被占用的情況。用編程器燒寫要特別注意芯片封裝適配器和電壓匹配稍有疏忽就可能損傷芯片。3. 詳細實操編譯BOOT.bin并燒寫QSPI Flash現(xiàn)在進入正題以QSPI Flash燒寫為例完整走一遍從構建鏡像到燒寫驗證的流程。3.1 從硬件工程到BOOT.bin的完整構建流程第一步在Vivado里完成硬件工程的綜合、實現(xiàn)并導出硬件平臺。注意勾選“Include bitstream”這樣導出的XSA文件里才會包含PL端配置。如果你只需要PS端跑裸機不需要PL邏輯那XSA里也可以沒有bitstream但FSBL會跳過PL初始化這個沒關系。第二步打開Vitis基于XSA創(chuàng)建platform工程然后創(chuàng)建一個FSBL工程。FSBL可以從Xilinx提供的模板生成在Vitis里新建Application Project時模板列表里找到“Zynq FSBL”直接生成即可。第三步創(chuàng)建你的應用程序工程。如果是裸機程序編譯生成ELF如果是Linux系統(tǒng)這一步就要用PetaLinux生成U-Boot和image.ub然后把FSBL、bitstream、U-Boot打包。第四步生成BOOT.bin。在Vitis的Xilinx菜單下選擇Create Boot Image界面里按順序添加分區(qū)1FSBL類型選bootloader文件為fsbl.elf分區(qū)2bitstream文件為design_1_wrapper.bit分區(qū)3應用程序或U-Boot文件為app.elf或u-boot.elf生成后得到一個BOOT.bin。如果用PetaLinux可以用以下命令一條龍生成petalinux-package --boot --fsbl --fpga --u-boot --force這條命令會自動把FSBL、比特流和U-Boot打包成BOOT.bin。3.2 使用Vitis圖形界面燒寫QSPI Flash燒寫前先準備硬件環(huán)境開發(fā)板通電JTAG調試器Digilent JTAG-HS2/3或Xilinx Platform Cable USB連接到PC板卡上啟動模式跳線設為JTAG模式。注意如果跳線設成了QSPI啟動JTAG鏈路可能被BootROM引導流程干擾導致下載失敗。打開Vitis連接目標板??梢韵冗\行一個空的FSBL到板上確保JTAG鏈路和DDR初始化正常。然后在菜單欄選擇Xilinx → Program Flash彈窗里這樣配置Image File選擇BOOT.bin路徑Offset填0x0Flash Type選qspi-x4-single或qspi-x1-single依據(jù)原理圖上的連接方式FSBL File選擇fsbl.elf勾選Verify after flash點擊Program工具會自動通過JTAG把FSBL加載到片上RAM運行然后FSBL初始化QSPI控制器執(zhí)行擦除、編程、校驗。QSPI Flash容量不大比如16MB整個流程通常在一兩分鐘內(nèi)完成。如果鏡像比較大比如幾十MB時間會明顯變長這時候不要誤以為卡死了看右下角log進度就行。燒寫完成后把啟動模式跳線改到QSPI重新上電。如果一切正常程序會自己跑起來。如果板子沒有反應優(yōu)先檢查BOOT.bin里的FSBL分區(qū)是不是放在第一個以及啟動模式引腳的電平組合是否和板卡手冊一致。3.3 命令行燒寫與u-boot下燒寫NAND FlashQSPI用圖形界面夠了但NAND Flash的燒寫情況復雜一些因為NAND有壞塊、頁大小、OOB區(qū)等概念Xilinx的圖形界面支持度不如QSPI好。到這一步我建議轉用命令行工具或者U-Boot來燒寫。用program_flash命令燒寫NAND的典型用法program_flash -f BOOT.bin -offset 0x0 -flash_type nand-x8 -fsbl fsbl.elf -verify需要注意flash_type參數(shù)必須與硬件實際使用的NAND顆粒規(guī)格一致x8還是x16是否帶ECC這些參數(shù)在UG585或者Vitis文檔里有明確說明。燒寫NAND時FSBL里也需要正確配置NAND驅動否則擦寫會失敗。另一種常見做法是通過U-Boot燒寫。先讓板子從SD卡啟動進入U-Boot然后用tftp把鏡像下載到DDR再用nand erase、nand write命令寫入。流程如下setenv ipaddr 192.168.1.100 setenv serverip 192.168.1.10 tftp 0x3000000 BOOT.bin nand erase 0x0 0x800000 nand write 0x3000000 0x0 0x800000這種方式的好處是不依賴JTAG調試器現(xiàn)場只需要網(wǎng)線和串口線就能完成升級。這個思路其實就是很多“基于ZYNQ的bootloader在線升級設計”的核心——系統(tǒng)運行起來之后通過自定義應用程序讀取新鏡像然后調用flash驅動完成在線更新。這里有個大坑寫入長度必須是頁大小的整數(shù)倍NAND的頁大小常見是2KB或4KB算錯的話末尾數(shù)據(jù)會丟失。3.4 制作SD卡啟動盤并合理規(guī)避flash燒寫風險有時候flash燒寫頻繁出錯或者你只是想快速驗證Linux系統(tǒng)我會建議干脆先用SD卡啟動。SD卡啟動的鏡像文件不是BOOT.bin而是需要一張包含BOOT.bin由FSBLbitstreamU-Boot組成和image.ub的SD卡。制作SD卡啟動盤的一般步驟用分區(qū)工具把SD卡分成兩個分區(qū)第一個分區(qū)格式化為FAT32放BOOT.bin和boot.scr第二個分區(qū)格式化為ext4放image.ub和根文件系統(tǒng)。將PetaLinux生成的BOOT.bin、boot.scr、image.ub復制到對應分區(qū)。插入SD卡開發(fā)板跳線設為SD啟動上電。SD卡啟動非常適合開發(fā)階段反復調試不會磨損板載flash也不怕燒錯變磚。很多ZYNQ在線升級方案就是利用SD卡啟動Linux在Linux里跑一個升級服務接收新的固件包然后把固件寫入QSPI Flash實現(xiàn)不拆機升級。這種方案繞開了直接燒flash的風險升級失敗還可以回退到SD卡系統(tǒng)容錯性很好。4. 常見問題與排查技巧實錄這部分我直接整理成速查表的形式每個問題都附上排查思路和解決方法。下面這些報錯基本覆蓋了90%以上的人會遇到的場景。4.1 Error: Flash Download Failed - Target DLL has been cancelled這個報錯太經(jīng)典了幾乎每個用Vitis燒寫的人都會碰上一次。它的含義是燒寫過程中目標DLL被中止通常是JTAG鏈路出現(xiàn)異?;蛘逨SBL運行崩潰導致燒寫進程中斷。排查順序按下面的來檢查JTAG連接確認調試器被PC識別并檢查板卡JTAG鏈路是否完整部分開發(fā)板JTAG和QSPI/其他外設共用引腳看板卡手冊確認是否有跳線沖突。檢查啟動模式燒寫時跳線務必設在JTAG模式如果設成了QSPI或SD啟動BootROM的啟動流程可能干擾FSBL的執(zhí)行。檢查FSBL是否選對Program Flash對話框里FSBL File一欄必須指向與當前硬件匹配的FSBL。如果FSBL是別的板子的初始化DDR或QSPI時就會崩然后報這個錯。檢查電源穩(wěn)定性ZYNQ對電源紋波比較敏感尤其是DDR供電異常時FSBL初始化DDR會卡住表現(xiàn)就是Target DLL cancelled。4.2 A valid FSBL file is required for flash operation這個提示是Vitis在Program Flash時彈出的很多新手會問“我明明選了BOOT.bin為什么還要FSBL”原因前面講過——BOOT.bin里的FSBL在燒寫過程中不一定會被重新執(zhí)行而Program Flash功能需要FSBL來初始化flash控制器并執(zhí)行驅動。注意BOOT.bin里已經(jīng)有FSBL了但你仍然需要在Program Flash對話框里單獨指定fsbl.elf。這是工具的設計要求不是可以省掉的選項。如果找不到fsbl.elf回到platform工程旁邊的FSBL工程里找Debug或Release目錄。4.3 Cant perform JTAG flash, because OpenOCD server is not running這個報錯常見于新版Vitis或使用第三方調試器如SEGGER J-Link、OpenOCD的環(huán)境。OpenOCD本身是一個開源的調試工具Vitis工程里如果檢測不到調試服務器就會報這個錯。解決思路有兩個方向。一是用Xilinx官方調試器Digilent JTAG-HS系列等此時Vitis會啟動自帶的hw_server和target manager不會依賴OpenOCD二是你確實要用OpenOCD那么先啟動OpenOCD服務openocd -f interface/ftdi/jtagkey2.cfg -f target/zynq.cfg確保OpenOCD進程保持運行再回到Vitis或命令行執(zhí)行燒寫。這個場景在Linux主機上比較常見Windows下多數(shù)還是用官方驅動居多。4.4 串口燒寫失敗的問題排查串口燒寫失敗的原因我在實際支持中見到的可以歸為三類。波特率不匹配U-Boot默認波特率常見為115200如果你串口終端軟件那邊設成9600或其他值看著就是亂碼收到的數(shù)據(jù)全是壞的。鏡像格式不對U-Boot燒寫時通常要求鏡像帶頭部信息比如mkimage生成的鏡像。如果直接把BOOT.bin通過串口發(fā)給U-BootU-Boot會拒收因為它期待的可能是它認識的image格式。文件傳輸協(xié)議問題串口燒寫常用ymodem或xmodem協(xié)議。有些終端工具對超大文件支持不好傳輸?shù)揭话刖蛿?。盡量把鏡像控制在幾MB以內(nèi)或者換一個成熟的終端工具。如果條件允許串口燒寫只作為備用方案。優(yōu)先JTAG其次是U-Boot網(wǎng)口tftp最后才是串口。4.5 燒寫完成后板子無反應這個問題的原因就要結合啟動流程來查了。先看電源指示燈和時鐘是否正常。ZYNQ板卡上一般有PS_CLK用示波器量一下如果時鐘沒有起振PS端根本沒跑起來。再確認啟動模式引腳設置。ZYNQ的啟動模式由MIO[5:2]的電平?jīng)Q定每一種組合對應一種啟動介質比如0110對應QSPI。如果跳線設置和實際燒錄介質不一致BootROM讀了錯誤的介質自然啟動不了。還有一種情況是BOOT.bin中分區(qū)順序錯了。FSBL必須排在第一個分區(qū)如果bitstream排在了第一個BootROM會把它當成FSBL執(zhí)行結果當然是異常。最后檢查是否燒到了正確的offset。QSPI Flash一般從0地址開始但有些板卡設計會在flash開頭留一段空間給別的用途比如存放保護配置或BootROM參數(shù)。這種情況下BOOT.bin的offset不一定是0要按板卡手冊來。4.6 NAND Flash型號兼容性排查前面多次提到Xilinx對ZYNQ支持的NAND Flash型號有明確限制。這個限制不是芯片引腳兼容的問題而是ZYNQ內(nèi)部NAND控制器驅動所支持的頁大小、塊大小、ECC算法有限制。所以就算你找了一顆物理上兼容的NAND但不在Xilinx支持列表里FSBL初始化時也可能讀取不到正確的芯片參數(shù)導致擦寫失敗。UG585里有一張NAND Flash支持列表的表格選型時直接對照。如果不確定手上的顆粒是否支持最穩(wěn)妥的方式是用SDK里的QSPI/NAND驅動自帶的Flash ID查詢功能把芯片ID讀出來比對。這個問題在NAND方案里非常普遍建議提前規(guī)避。5. 實際項目中燒寫方案的落地經(jīng)驗前面聊了具體操作最后這部分我想分享一些項目層面經(jīng)驗尤其是燒寫和多分區(qū)布局、在線升級搭配相關的思路。5.1 flash分區(qū)規(guī)劃與啟動方案設計產(chǎn)品開發(fā)到后期一定會遇到flash分區(qū)規(guī)劃的問題。比如QSPI 16MB不能把BOOT.bin從頭占到尾因為還要留空間給應用程序升級。常規(guī)的做法是做一個分區(qū)表舉例如下分區(qū)名稱起始地址大小存放內(nèi)容Bootloader區(qū)0x0000001MBBOOT.binFSBLbitstreamU-Boot內(nèi)核區(qū)0x1000004MBimage.ub文件系統(tǒng)區(qū)0x5000008MBrootfs/image用戶數(shù)據(jù)區(qū)0xD00000剩余應用程序、配置、日志這樣劃分的好處是升級時只需要更新其中一個分區(qū)不需要整片擦除。配合在線升級設計應用程序收到新包后寫入用戶數(shù)據(jù)區(qū)然后重啟切換啟動指針既降低了升級風險也縮短了升級時間。如果你用的是裸機程序同樣可以分兩個APP區(qū)做A/B升級。寫一個簡單的引導邏輯在FSBL或用戶引導程序里判斷哪個分區(qū)的版本號更高就從哪個分區(qū)啟動。這個方案我在好幾個產(chǎn)品里用過穩(wěn)定可靠而且實現(xiàn)起來并不復雜。5.2 在線升級降級與回滾策略基于ZYNQ的bootloader在線升級設計在實際產(chǎn)品里通常還要考慮回滾。最直接的辦法是保留出廠固件分區(qū)升級時先擦除臨時分區(qū)寫入新固件校驗通過后再把啟動標志指向新分區(qū)。如果校驗失敗或運行異常看門狗會觸發(fā)回滾啟動到舊版本。這個思路聽著簡單真正落地時有一堆細節(jié)。比如flash的寫入盡量按扇區(qū)對齊否則擦除操作會波及鄰近數(shù)據(jù)校驗采用CRC32或SHA256不能只靠長度判斷升級過程中的意外斷電必須有應對措施通常是在flash頭部存一個升級狀態(tài)標記引導程序判斷標記來決定是否繼續(xù)升級。我在實際項目中的經(jīng)驗是任何flash寫操作都先備份舊鏡像、再寫新鏡像、最后更新標志位順序不能搞反。否則一旦斷電落在“新鏡像只寫了一半、標志位已經(jīng)更新”的狀態(tài)系統(tǒng)就起不來了。5.3 產(chǎn)線批量燒寫的效率提升辦法如果你的產(chǎn)品要小批量試產(chǎn)一個個開Vitis點Program Flash顯然不現(xiàn)實。針對產(chǎn)線我有幾個建議。第一用program_flash命令行工具封裝一個批處理腳本只傳鏡像路徑和flash型號操作員雙擊執(zhí)行。這樣即使不是研發(fā)人員也能完成燒寫。第二JTAG鏈上可以串聯(lián)多塊板卡一次燒寫多塊。前提是每塊板卡的JTAG鏈IDCODE要能區(qū)分腳本里針對不同位置的目標執(zhí)行燒寫即可。第三如果是裸板沒有JTAG座子或者不想接調試器可以采用“預燒寫”模式也就是貼片前用編程器把flash芯片燒好再貼到板上。這樣速度快、成本低缺點是一旦硬件改版flash里的程序要重新燒。很多量大的產(chǎn)品都是這么干的。寫在最后ZYNQ燒寫程序到flash看起來只是一個簡單的操作背后涉及啟動流程、FSBL機制、flash選型、工具鏈使用和可靠性設計。我自己在第一次燒寫QSPI時也被“Target DLL has been cancelled”折騰了一整天最后發(fā)現(xiàn)只是跳線帽沒接對。后來項目做多了才慢慢意識到燒寫這件事本身不難難的是對整個啟動鏈路有清晰的認知。如果你正在被燒寫問題困擾我的建議是不要急著點按鈕先花半天時間把啟動流程讀一遍把BOOT.bin里每個分區(qū)的作用搞清楚。接下來再對照這篇文章的排查清單一項項過大部分問題都能解決。如果還不行就用最笨的辦法先確保JTAG能連上、FSBL能跑再一步步增加環(huán)節(jié)定位問題會快得多。最后再分享一個小技巧燒寫QSPI之前先把整片flash的讀保護、寫保護狀態(tài)檢查一遍很多板卡出廠時flash被軟件保護了擦除命令根本不生效表現(xiàn)就是燒寫時一直報錯。用燒寫工具先執(zhí)行一次blank check或chip erase往往能解決一半的“莫名其妙”現(xiàn)象。