ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Zynq老工程迁移Vitis实战:从HDF到XSA的完整指南

Zynq老工程迁移Vitis实战:从HDF到XSA的完整指南 2019年年底接手了一个老项目的维护一套基于Zynq-7000的采集板卡底层裸机程序一直用Xilinx SDK 2018.3开发。当时团队统一开发环境已经切到Vitis 2023.2我第一步就撞了墙——SDK工程里的.hdf文件在Vitis里根本打不开。后来翻了一堆文档才搞清楚从Vitis 2020.1开始Xilinx现在是AMD正式用Vitis取代SDK硬件描述文件从.hdf换成了.xsa整个软件工程的构建方式也彻底变了。这篇文章把我把一个真实SDK工程完整移植到Vitis的过程写出来包括硬件平台导出、BSP重建、应用工程迁移、启动镜像生成和调试排错。内容覆盖Zynq-7000和Zynq UltraScale两条产品线主要以裸机standalone方式为例Linux系统启动部分的移植思路也会顺带讲。如果你手里有老SDK工程、正打算迁移Vitis或者刚接手团队老代码但环境被强制统一到Vitis这篇文章可以帮你少踩几个坑。1. 为什么非迁不可SDK停更、XSA替代HDF与平台工程的出现1.1 SDK的终点到底在哪里很多团队拖到现在不迁移是因为SDK用着确实顺手硬件平台文件加进去BSP自动生成应用工程模板点几下就出来JTAG一连就能调试。但Xilinx官方在2019.2版本之后就停止了对SDK的功能更新2020.1开始全面转向Vitis。也就是说如果你还在用2018.3或2019.2就相当于守着一条停止维护的软件链。这带来的直接问题有几个一是新器件不支持Vivado新版本导出的硬件平台SDK根本不认二是编译器老旧GCC版本停在多年前代码安全性和性能优化落后三是一些新的调试工具和库函数没有回迁。对于长期产品来说这些影响会越拖越大。一个常见的误解是Vitis只是SDK改了个名字。真不是。Vitis把硬件平台和软件应用之间的耦合关系做了重新抽象引入了平台工程这个概念整个构建流程都变了。后面会详细说。1.2 Vitis里的三个核心变化首先是文件格式从HDF换成了XSA。SDK时代Vivado导出的硬件描述文件是.hdf文件包含FPGA比特流、处理器硬件配置、外设地址映射和时钟信息。Vitis时代沿用Vivado导出硬件平台的机制但统一成.xsa文件。其次是平台工程。Vitis里你先要创建一个平台工程Platform Project把XSA文件导入进去然后在这个平台基础上创建应用工程。平台工程相当于把硬件配置BSP驱动打包成一层应用工程只关心自己的代码。这个抽象对多核异构开发更友好但也意味着老工程不能直接点一下导入就能编译。第三是构建系统。SDK基于Eclipse原生构建Vitis初期版本界面也是Eclipse风格但2024.1之后的Vitis Unified IDE改成了Theia框架构建逻辑也统一到了Makefile体系。用过老Eclipse的人一开始会有点不习惯但命令行构建能力确实变强了。1.3 迁移成本到底值不值说实话如果只是一个一次性项目、烧完板子不再动那迁移Vitis的价值确实不大。但如果是长期维护的产品我建议还是咬咬牙迁。原因是新版本Vivado/Vitis对Zynq UltraScale和Versal的支持是SDK完全比不了的而且越来越多IP核和参考设计已经不再提供SDK版本示例。迁移成本主要在三块硬件工程需要重新导出XSA、BSP需要重建、启动镜像的生成方式需要调整。应用层C代码大部分是直接能编译的这一点比想象中轻松。整体下来一个小型裸机工程熟练的话半天到一天能搞定大型工程带很多第三方库可能得两三天。2. 迁前盘点硬家底、软家底和版本关系一次核对清楚2.1 硬件侧先确认你的交付物齐不齐迁移前我习惯先把所有输入文件列一个清单缺哪个就去补哪个。硬件侧需要确认这几样东西原始Vivado工程.xpr工程文件是否还在还是只有一个.hdf有没有最终的bitstream比特流文件FPGA比特流与硬件描述是否一致也就是最后一次生成HDF时的bitstream还要在最理想的情况是Vivado工程还在这样你可以直接在同一个Vivado版本里打开工程重新生成硬件平台。最麻烦的情况是工程没了只剩一个.hdf那你得想想怎么从HDF反推出硬件配置——这个非常难几乎等于重新建工程所以一定要保护好Vivado工程。一个容易忽略的点确认Vivado工程里有没有使用自定义IP。如果有自定义IPVitis构建BSP时可能会依赖这些IP的驱动而这些驱动在旧版本里能编译在新版本里可能接口变了。这个属于代码迁移范畴后面细说。2.2 软件侧BSP定制内容最容易漏应用源码好说copy到Vitis工程里就行了。麻烦的是BSP定制部分。SDK时代很多人会在BSP设置里改东西比如修改过BSP的默认堆栈大小heap size / stack size在BSP里加了自定义的驱动库或者中间件手动改过BSP生成的xparameters.h、xil_cache.h等文件设置了编译优化参数或预处理器宏这些改动在SDK迁移到Vitis时不会自动带过来。因为Vitis的BSP是重建的你在SDK里对BSP的修改全部作废。所以迁移前我建议把每个BSP的设置项截图或者用git记录好内容尤其是堆栈大小、驱动使能与禁止列表、中断优先级的配置。如果你在SDK时代改过BSP生成的官方驱动文件就是那些x*.c/x*.h文件那迁移时你还要把这些改动在新BSP里重新打一遍。这种改官方生成文件的做法本来就不是好习惯但老项目里很常见。遇到这种情况我优先建议把改动上移到应用层用Xil_In/直接操作寄存器或者注册回调的方式替代长期看能省很多事。2.3 版本对应关系这是最大的隐藏炸弹测试下来Vitis打不开XSA最常见的原因就是版本不匹配。Vivado和Vitis是配套发布的同一个小版本号之间才能无缝配合。比如你用Vivado 2020.1导出的XSA想在Vitis 2023.2里导入大概率会报错或者平台信息读不全。我的原则是Vivado和Vitis必须用同一个版本至少也要保证XSA导出版本不高于Vitis版本太多。下面是常见搭配参考Vivado版本SDK/Vitis版本硬件文件格式备注2019.2及之前Xilinx SDK 2019.2.hdfSDK最后一个版本已停止功能更新2020.12023.2VitisEclipse版.xsa老Vitis界面接近SDK2024.1至今Vitis Unified IDE.xsa新Theia界面构建命令有变化如果你的Vivado工程是2019.2之前的那迁移的时候还得先升级Vivado工程本身。这个我会在第三章说清楚。3. 分步操作重新导出XSA、重建平台工程与BSP3.1 第一步用Vivado重新导出XSA假设你手上有完整的Vivado工程操作用的是Vivado 2023.2那就直接打开工程。打开后建议先用Generate Bitstream重新跑一遍综合实现布局布线确保比特流是最新的。老工程如果IP版本比当前Vivado版本旧Vivado会弹窗提示升级IP这一步需要谨慎处理——升级IP可能会改变接口时序如果只是功能验证可以忽略但产品量产场景建议逐项确认。比特流生成之后在Vivado的菜单里选择 File Export Hardware勾选 Include bitstream导出为一个.xsa文件。注意导出的是平台文件不是HDF了。如果工程里启用了MicroBlaze还是Zynq处理器导出的XSA内容会有差别但只要勾选了bitstreamVitis就能拿到完整的平台信息。对于没有原始Vivado工程、只有HDF的老工程我的经验是看看HDF里带不带bitstream。你可以用文本方式打开HDF里面通常包含硬件描述XML和比特流的引用。不过说实话这种情况很难无损迁移我建议尽量找回原始工程或者至少从版本管理里把历史Vivado工程检出来。3.2 第二步在Vitis里创建平台工程打开Vitis IDE先创建一个存放工程的Workspace尽量用空目录。菜单 File New Platform Project起一个平台名字比如my_platform。创建的时候选择 Create from XSA把上一步导出的XSA加进去。这里有几个选项需要说明一下Hardware Specification显示XSA里的硬件信息包括处理器核、外设、DDR地址范围Operating System选择standalone裸机还是linuxProcessorZynq-7000通常有ps7_cortexa9_0A9核Zynq UltraScale会有psu_cortexa53_0A53核和psu_cortexr5_0R5核如果你用的是Zynq UltraScale并且裸机跑在A53上选中A53核如果跑在R5实时核上就选R5。这个选择决定了后面BSP的编译器和库函数。创建平台工程之后Vitis会默认帮你生成一个standalone域Domain这个域就是BSP的雏形。此时先不要急着写应用代码直接构建平台工程。构建完成后平台工程目录下会生成一个.xpfm文件这就是Vitis应用工程依赖的平台文件。3.3 第三步重建BSP并核对xparameters平台工程构建成功后进入BSP的设置界面双击平台工程里的domain或者右键BSP文件夹选Board Support Package Settings。我需要重点核对的东西有两个一是BSP版本和驱动列表。Vitis根据XSA自动使能硬件对应的驱动例如UART控制器、GPIO、AXI DMA、IIC、SPI、中断控制器等。对比你SDK老工程BSP里手动使能过的驱动确保新BSP里也包含了同样的外设驱动。二是堆栈大小和启动模式。老BSP里设置的heap size、stack size在新BSP里默认可能不同。裸机程序如果不改堆栈跑到动态内存分配时会出现内存越界表现是莫名其妙跑飞这个问题很隐蔽。建议尽早改好。新BSP生成后多了一个很常用的文件xparameters.h。这个文件里包含了所有硬件外设的基地址、中断ID、处理器的CPU ID等信息。老SDK工程里如果很多地方直接用XPAR_...宏那迁移时宏名大概率能对上因为Vitis的BSP生成器延续了SDK的命名规则。但要注意如果你的老工程里手动改过xparameters.h那新BSP里的值是权威值以新文件为准手动改过的宏要回归排查一遍。3.4 第四步导入应用工程源码并构建平台工程和BSP就绪后创建应用工程File New Application Project。选中刚才的平台工程选一个处理器A9/A53/R5输入应用名。Vitis会提供模板比如Hello World和Empty Application空应用。我一般选空应用然后把老SDK工程里的src目录整体拷贝到新工程的src下。这时直接构建通常能发现几个问题头文件路径缺失。老工程里可能自定义了include目录新工程里要右键应用工程选Properties在C/C Build的Settings里把Include Path加回去。链接库缺失。老工程的.a文件或者-lxxx选项需要在新工程里重新指定。编译器选项不同。比如老工程用了-mcpucortex-a9这种特定CPU指令集参数新工具链里可能变成了-mcpucortex-a9也可以但架构扩展参数要同步调整。构建不通过不用慌把报错信息逐条处理大部分都是路径和宏定义的问题。第一次构建成功是迁移的第一个里程碑。4. 应用代码迁移编译选项、链接脚本与第三方库的真实改动4.1 驱动API兼容性比想象中好刚迁移时我最担心Xilinx官方库函数的API变了。实际跑下来基础库xil_printf、Xil_DCacheFlush、XGpio、XUartPs、XScuGic等的名字和用法基本保持一致。Xilinx的BSP设计很稳定XDispatch用户代码层面改动极少。但有两个地方要留意一是版本较老的自定义IP驱动。如果你的Vivado工程里通过IP核生成器的自带驱动SDK时代的版本和Vitis时代的版本可能有细微差异。比如自定义IP的中断回调注册函数名称或参数个数不同这种报错会在编译阶段就暴露比较好解决——对着新驱动头文件改一下函数签名即可。二是默认的优化选项。SDK旧工程默认可能用-O0Vitis新工程的默认优化可能是-O2。优化级别变了之后有些依赖未初始化内存的代码行为会变最常见的是局部变量假设为零结果优化后不是零。建议迁移初期保持和老工程一致的优化级别跑通了再逐步调高。4.2 链接脚本lscript.ld的调整应用工程里有一个链接脚本文件老工程叫lscript.ldVitis工程里默认也会生成一个。这个文件定义了代码段、数据段、堆、栈放到的内存区域。老SDK工程直接拷贝代码时建议把新工程自动生成的lscript.ld打开看一眼DDR起始地址和大小是否和XSA里的DDR配置一致堆大小heap和栈大小stack是否匹配BSP设置有没有单独把某个段放到OCM或者DDR的特定区域最常见的坑是SDK老的链接脚本里把堆设得很小但新代码用了更多动态分配结果运行时堆溢出。另一个坑是老链接脚本里指定了_HEAP_SIZE宏但新工程BSP里也定义了HEAP_SIZE两者不一致时以长者为准容易混乱。建议统一在链接脚本里维护堆栈大小BSP设置里不要重复改。4.3 编译选项和Makefile的迁移Vitis在生成应用工程时会自动生成Makefile放在工程目录下。老SDK工程的编译选项如果复杂比如自定义宏、不同的优化等级、附加的链接参数需要手动迁移到Vitis的工程设置中。实际操作路径右键应用工程选择Properties进入 C/C Build Settings在Compiler的Miscellaneous里添加宏定义在Linker的Libraries里添加库路径和库名。如果你打算走命令行构建这个后面会推荐那Vitis生成的Makefile里可以通过环境变量传参比如make clean make -j4构建产物默认在工程目录的build子目录里路径和SDK时代的Debug/Release目录不一样脚本里写死路径的话要改。4.4 第三方库和静态库必须重新编译老SDK工程里的第三方静态库.a文件不能直接拿来用除非你确认它就是用同一套交叉编译链编出来的。新Vitis带的GCC版本和SDK时代差别不小老库直接链接轻则警告重则出现relocation truncated to fit这类链接错误。我的做法是找到第三方库的源码用当前Vitis的工具链重新编译一遍。如果拿不到源码那就先把老库的链接去掉用源码或功能替代方案顶上。另外需要注意交叉编译链名称。Zynq-7000的A9裸机用的是arm-none-eabi-gccZynq UltraScale的A53裸机用的是aarch64-none-elf-gccR5裸机用的是armr5-none-eabi-gcc。如果你在老工程里手动编译过第三方库确认你用的编译器前缀和处理器匹配别把A53的库链到R5裸机上。5. 启动镜像与产品化验证FSBL、BIF、QSPI/SD启动和调试排错5.1 FSBL、PMUFW这些启动组件怎么来SDK时代创建启动镜像需要FSBL工程FSBL通常是通过模板创建的。Vitis里也是一样的思路你需要在平台工程基础上创建一个FSBL应用工程。Zynq-7000用户新建应用工程时选择Zynq FSBL模板不同版本模板名略有不同但含义一致Vitis会基于当前平台自动生成FSBL代码编译后得到fsbl.elf。Zynq UltraScale用户除了FSBL还需要PMUFWPlatform Management Unit Firmware。这个也是通过模板创建编译后得到pmufw.elf。如果系统里使用了ATFARM Trusted Firmware和U-BootVitis里也提供了对应的构建流程但纯裸机情况下有FSBL和PMUFW基本就够了。这些组件的构建顺序有一点讲究先构建平台工程再构建FSBL和PMUFW然后构建应用工程最后生成BOOT.BIN。5.2 生成BOOT.BIN的BIF文件Zynq和Zynq UltraScale的启动都需要一个BOOT.BIN镜像生成BOOT.BIN的工具是bootgenVitis安装目录里自带。Vitis IDE里可以通过Xilinx Boot Image功能图形化生成也可以在命令行用bootgen生成。命令行方式主要是一个BIF文件内容大致如下Zynq-7000裸机the_ROM_image: { [bootloader] fsbl.elf bitstream.bit app.elf }Zynq UltraScale的BIF会多几项the_ROM_image: { [bootloader] fsbl.elf pmufw.elf atf.elf app.elf }生成命令类似bootgen -image boot.bif -o BOOT.BINBIF里镜像的顺序和启动链强相关顺序错了启动会卡住。这个顺序我一般不多动除非需要把bitstream放到外部Flash的特定偏移位置。5.3 高频报错与排查链路迁移过程中我遇到最多的问题分三类写出来给大家参考类型一编译报错找不到头文件现象fatal error: xparameters.h: No such file or directory原因应用工程没有关联到平台工程或平台工程还没有构建生成BSP排查先构建平台工程确认domain的BSP目录下生成了include文件夹检查应用工程设置里是否选中了正确的平台处理器解决在应用工程Properties里确认 Project Properties Platform 关联正确必要时重新选择平台和处理器类型二链接报错符号未定义现象undefined reference to XAxiDma_...原因BSP里没有使能对应的驱动或链接脚本段设置不对排查打开BSP设置看驱动列表里是否有AxiDma没有就勾选上重新构建平台工程解决启用对应驱动后重新构建BSP和平台工程再回来运行应用构建类型三启动卡死或跑飞现象BOOT.BIN烧进去上电串口没有输出或者输出到一半卡住排查链路先用JTAG连接在Vitis里用XSCT跑一下connect和targets命令确认处理器是否跑起来然后把应用elf直接加载到DDR里调试看能不能跑到main函数解决如果能跑起来说明启动镜像的问题重点查BIF顺序、bitstream是否包含、SD启动还是QSPI启动的选择如果跑不到main检查lscript.ld的入口地址和DDR配置5.4 用XSCT和JTAG验证移植是否成功我觉得最高效的验证方式不是烧Flash而是先走JTAG裸跑。在Vitis的Xilinx终端里启动XSCT可以做硬件初始化、下载elf、运行和停止connect targets # 选中A53核或A9核 rst -system after 100 fpga -file bitstream.bit targets -set -filter {name ~ *A53*#0} dow app.elf con这一套流程跑通说明应用在硬件上正常执行。然后再走启动镜像流程烧到SD卡或QSPI上电验证。两条链路分开测出问题能快速定位是硬件连接问题还是启动镜像问题。6. 移植项目里最值得记住的几条实战经验6.1 先跑Hello World再迁老代码别急着把老代码整个拖进来。第一次接触Vitis的人最稳妥的路径是先基于XSA创建一个空平台工程创建一个Hello World模板应用编译、加载、print出来确认整条工具链已经打通。这一步能排掉大部分环境问题后面老代码迁移时的变量就少很多。6.2 Vivado和Vitis版本必须严格一致这个教训我是付过时间成本的。有一次用Vivado 2022.1导出的XSA去Vitis 2023.1里建平台IDE没报错但生成的BSP里外设驱动不完整跑起来UART轮询方式正常、中断方式失效排查了两天才定位到是平台信息传递过程中的版本兼容问题。后来统一到同一版本重出XSA、重建BSP问题立刻消失。所以我的建议很直接团队内部统一Vivado和Vitis的版本组合XSA全程用同一个版本导出不跨大版本导入。6.3 命令行构建比界面点按更可靠Vitis IDE的图形界面做增量构建有时候会缓存陈旧表现为代码改了但编译产物没变。遇到这种问题先clean再build是常规操作但还是不如命令行来得干净利落。我现在的流程是平台工程和应用工程都写一个构建脚本用make/make clean控制make clean make -j8配合CI/CD还能做到晚上自动构建、早上出报告。这个流程对团队协作很重要因为每个人点界面构建出来的临时文件会导致工程状态不一致命令行构建能消掉不少无意义的我这边能编你那边编不过。6.4 把迁移当成一次驱动升级迁移Vitis表面上只是工具链变化实际上你会用更新的BSP库。老SDK工程的BSP库可能停留在2018年迁移到Vitis 2023.2之后驱动库版本直接跨了五六年这期间Xilinx修复了不少bug也优化了部分外设驱动性能。我的建议是迁移时不要只追求编译过、能跑可以顺手做一轮功能回归重点关注UART收发、DMA传输、中断响应这几个高频外设。很多时候你会发现新版驱动在性能上比老版本强一截比如DMA描述符分配方式更合理中断响应延迟更低。这些收益不是迁移的直接目标但迁移完成之后就变成了产品白捡的升级。另外一个隐藏收益是文档和社区支持。新版本驱动在AMD官方文档和开发者社区里有大量讨论遇到问题很容易搜到答案老SDK版本的问题往往只能靠考古式翻帖。对于长期维护的团队这本身就是提升效率的地方。最后再分享一个细节迁移完成后记得把所有构建产物和临时文件整理干净工程里只保留源文件、配置文件、链接脚本和必要的脚本工具。老工程迁到新工具链就是一个重新整理项目结构的好时机把之前积压的临时补丁测试代码注释掉的死代码顺手清掉后续维护能省很多力气。我迁完这个Zynq项目最大的感受是工具链更新其实不可怕可怕的是带着一堆历史负担硬迁把新环境用成了老样子那就白折腾这一趟了。
返回列表