ARTICLE DETAIL

资讯详情

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

Vivado中Block Design复用:脚本重放与BD文件导入全攻略

Vivado中Block Design复用:脚本重放与BD文件导入全攻略 说实话干FPGA这行这几年我最怕听到的一句话就是“这个模块我们上个项目不是做过吗直接拿过来用不就行了”说这话的同事永远不知道一个Block Design要是从头搭时钟、复位、AXI总线、中断、DDR控制器、MIG这些IP一根线一根线连下来少说也要半天稍微复杂一点带Zynq PS的两天都打不住。但话说回来他这句话其实点出了一个真实需求在Vivado里Block Design的复用确实是个高频场景。芯片还是那颗芯片外设接口大同小异平台代码基本不动为什么每次都要重新连一遍所以今天我想把我自己项目里实际在用的两种BD复用方法完整梳理一遍——一种是基于脚本的复用一种是基于BD工程文件的复用。两种方法我都踩过不少坑也总结了一套相对稳妥的操作流程基本覆盖了“从旧工程把BD搬进新工程”这件事的常见路径。1. 什么时候需要复用Block Design选哪种方式更合适1.1 复用的常见场景先别急着上手操作搞清楚“为什么需要复用”比“怎么复用”更重要。我复盘了自己过去几年接触过的项目BD复用基本集中在下面四个场景里系列化产品的平台化开发同一个FPGA芯片做了好几个型号的产品每个型号的PS配置、DDR控制器、GTX通道、以太网接口基本一致只是外设数量、功能裁剪上有差异。这种情况下BD的复用比例能到80%以上。算法团队与接口团队并行开发接口团队先把底板、DDR、MIG、AXI互联这些底层BD搭好并验证通过算法团队直接在同一个BD基础上挂自己的加速模块。如果算法团队另起炉灶重新搭BD光是等MIG校准通过就够喝一壶的。老项目向新芯片迁移公司从Zynq-7000切到Zynq UltraScale或者从Artix-7换到Kintex-7BD里的PS配置、DDR控制器配置可能要调但大量IP的互联关系和地址映射是可以保留下来的重搭一遍完全是浪费时间。同一工程下的功能分支比如同一个硬件平台A版本不需要PCIeB版本需要PCIe。这时候就需要在同一个BD基础上做增删而不是维护两份完全独立的BD。这四个场景的共性是底层的硬件平台和IP互联关系是稳定的变化的是上层的业务逻辑。能把这部分稳定的内容固化下来BD复用才有意义。1.2 两种方法的本质区别Vivado里复用BD站在顶层看只有两条路一条是把BD的“搭建过程”记录下来用Tcl脚本重放另一条是把BD的“搭建结果”直接复制过去也就是把.bd文件本身导入到新工程。这两条路听起来都能达到目的但本质逻辑完全不同脚本复用write_bd_tcl记录的是搭建动作。比如“这里创建一个AXI GPIO”“这里连一根时钟线”“这里把MIG的app_addr连到AXI SmartConnect的S_AXI端口”。在新工程里执行脚本相当于把当初搭建BD的操作重新做了一遍。它的特点是灵活、可参数化、适合版本管理。BD文件复用Import Block Design / 复制.bd文件记录的是搭建结果。.bd文件本身是一种文本格式的设计描述包含了所有IP例化、端口连接、地址映射、引脚分配等信息。直接导入新工程Vivado会按文件内容把整个BD恢复出来。它的特点是快、完整、不需要重新执行搭建动作但对工程环境、IP版本、路径的依赖更高。基于这个本质区别我一般建议按下面的思路来选型如果新工程和旧工程用的Vivado版本相同且只是单纯地把BD搬到另一个工程里直接复用.bd文件最省事如果新工程和旧工程Vivado版本不一致或者你希望同一个BD能一键生成多个不同配置的版本那脚本复用是更稳的路子。当然两者也不是非此即彼后面我会专门讲怎么配合使用。2. 方法一用write_bd_tcl导出脚本一键重建BD2.1 write_bd_tcl命令的基本用法与参数脚本复用是整个Vivado BD复用体系里最值得花时间掌握的方法。它依赖的核心命令是write_bd_tcl在Vivado的Tcl Console里执行即可。我先说最基础的用法打开一个已经验证过的BD工程在Tcl Console里输入write_bd_tcl -force C:/project/scripts/pl_platform_bd.tcl-force的意思是如果目标路径下已经有同名文件直接覆盖不弹确认框。这个参数我几乎每次都会带上因为脚本一多之后你根本记不清哪个文件已经存在不带-force就会卡在那里等确认交互式操作还能点一下要是放到自动化流程里直接就把流程挂起了。除了-force还有两个参数在实际项目中很关键write_bd_tcl -force -replace_all C:/project/scripts/pl_platform_bd.tcl-replace_all表示脚本里所有引用的IP都用当前Vivado版本库里的对应版本来替换。这在新旧版本Vivado之间转移BD时特别有用。如果不加这个参数脚本会保留IP的原始版本号在新版本Vivado里source时可能会因为IP版本不存在而报错。还有一个参数-no_ip_version作用是导出脚本时不带IP版本号。这个参数适合那种团队内部已经统一了IP仓库版本的场景配合自定义IP仓库使用。我个人在跨版本迁移时反而更推荐带版本号因为可以在source后明确检查哪些IP被升级了。这里要特别说明一点write_bd_tcl导出的脚本本质上是一连串的Tcl命令包括create_bd_design、create_bd_cell、create_bd_port、connect_bd_net、assign_bd_address等等。你完全可以用文本编辑器打开看一眼里面每一行都是当时搭建BD时的一个动作。理解了这一点你就能明白为什么脚本方式最灵活——因为它不是对最终结果的快照而是对搭建过程的完整描述意味着你可以在source之前修改脚本里任何一步。2.2 在新工程中重放脚本的完整流程脚本导出来了怎么用很多新手会在工程之外直接source然后发现各种莫名其妙的错误。正确的流程应该是这样的第一步先创建一个新工程选好对应的FPGA芯片型号。这一步不能省略因为脚本里的很多IP例化、连接操作都依赖工程上下文。如果工程不存在create_bd_design会失败。第二步在Tcl Console中执行source C:/project/scripts/pl_platform_bd.tcl执行过程中Tcl Console会逐条执行脚本里的命令当前工程的Sources窗口里会看到BD的结构一点点被创建出来。如果BD里包含Zynq PS、MIG这类需要特殊初始化的IP脚本执行时间会明显变长这个阶段耐心等就好只要没有报红字错误基本都能成功。第三步脚本执行完后在Sources窗口里找到新生成的BD右键选择Generate Output Products生成所有IP的输出产物。这一步是必须的因为脚本只恢复了BD的“设计描述”还没生成IP的网表、仿真模型这些文件。第四步右键BD选择Create HDL Wrapper生成顶层例化文件。这个文件是连接BD和FPGA顶层模块之间的桥梁后面修改顶层端口信号就要靠它。第五步检查顶层Wrapper的端口与FPGA实际引脚是否匹配修改约束文件。这个流程跑通一次之后你会发现同一个BD在不同工程里恢复出来时间不会超过几分钟哪怕是最复杂的PS-PL互联设计。相比之下手动搭一个带MIG的BD光是一步步配置DDR参数、连线、地址映射半天时间就没了。2.3 脚本复用的进阶技巧上面说的是基础用法下面分享几个我实际项目里觉得特别有用的进阶技巧。技巧一利用脚本参数化BD名称。默认情况下write_bd_tcl导出的脚本会把BD名称写死。如果你希望同一个脚本能在不同工程里创建不同名字的BD可以在脚本开头定义一个变量。比如导出的脚本里第一行是set bd_name pl_platform然后后面的create_bd_design $bd_name、set_property REG_AXI_ADDR_BASE $bd_name这些命令都通过变量引用。这样在新工程里source之前先执行set bd_name new_name就能复用同一个脚本创建不同的BD名称后续的地址映射、端口命名也能跟着变很适合做多版本分支。技巧二脚本与Git配合做版本管理。这是我认为脚本复用最大的优势。.bd文件虽然也是文本但它的格式和内容对人工diff非常不友好两个版本之间到底改了哪根线肉眼基本看不出来。但Tcl脚本不一样它的格式清晰、逻辑明确团队成员提交的改动可以通过Git的diff功能直观地看到“哪里增加了一个端口”“哪里改了一条连接”。所以在我带团队时我会要求重点BD的变更尽量通过修改脚本的方式提交而不是直接改BD文件。技巧三在脚本中追加约束和检查项。我习惯在write_bd_tcl导出的脚本末尾追一段自定义代码。比如检查所有外部引脚是否都连接了物理约束或者打印BD的地址映射表。这样在source之后Tcl Console会直接输出关键信息不用再手动去Address Editor里翻。脚本复用也不是没有缺点。最大的问题是如果BD里有很多需要时序收敛的硬核IP比如高速收发器、DDR控制器脚本重放出来的BD虽然结构相同但布线结果和时序余量可能会因为版本变化而不同。这时候不能盲目相信“脚本跑通了就等于复用成功了”一定要重新跑一遍综合和实现重点看时序报告。3. 方法二复制.bd文件直接导入工程3.1 复制.bd文件并导入新工程的步骤第二种方法就直白多了把整个BD文件导入新工程。.bd文件在工程目录下的路径一般是project_name.srcs/sources_1/bd/design_name/design_name.bd这个文件本质上是一个文本文件记录了这个BD所有的IP、连接、属性。导入新工程的推荐做法有两种。第一种是直接在Vivado界面操作打开新工程后在Sources窗口左上角点击加号选择Add Sources然后在对话框里选择Add or create design sources再点Add Files找到目标.bd文件并选中最后Finish。Vivado会自动检测到这是一个Block Design文件并把它加入工程。第二种方式更适合批量操作在Tcl Console里执行add_files -norecurse C:/project/old_prj/old_prj.srcs/sources_1/bd/pl_bd/pl_bd.bd-norecurse是为了防止Vivado自动递归搜索该目录下的其他文件导致把一些不该加的文件也带进来。无论是哪种方式导入完成后Sources窗口里会出现一个“Design Sources”分组下面就是导入进来的BD。这里有一个常见误区很多人在这一步就直接打开BD开始修改了忽略了后续两个关键操作导致后面综合或仿真时各种报错。3.2 导入后的三件事Generate Output Products、Create HDL Wrapper、适配顶层导入BD文件之后一定要按顺序做三件事缺一不可。第一件事右键刚导入的BD选择Generate Output Products。这一步会生成该BD内部所有IP的仿真模型、综合网表、约束文件。如果不做这一步后续在顶层例化BD时会提示找不到IP的实现文件。生成时间取决于BD内IP数量和复杂度MIG这类IP会比较慢耐心等即可。第二件事右键BD选择Create HDL Wrapper弹窗里选“Let Vivado manage wrapper and auto-update”。这一步会生成一个Verilog或VHDL的顶层文件里面例化了整个BD并导出了所有对外的端口。选了“Let Vivado manage”之后每次BD端口变化Vivado会自动更新这个Wrapper文件省掉手动同步的麻烦。第三件事把Wrapper文件里的端口信号连接到新工程的实际顶层。这一步是最容易出错的地方。旧工程里BD对外连接的信号名、位宽跟新工程的顶层不一定匹配。我的习惯是打开Wrapper文件逐个检查端口声明和顶层模块里的信号连线重点关注以下几类时钟端口BD里的时钟输出通常是pl_clk0、pl_clk1这样要确认新工程里这些时钟接到的模块需要多少MHz的频率。复位端口Vivado自动生成的Wrapper里外部复位端口的命名和极性要仔细核对搞反极性的问题我见过不止一次。AXI接口和中断BD导出的AXI接口在新工程里往往要重新连接地址映射也要重新核对。高速串行口如果BD里有GTX/GTH这类高速收发器导入后要特别注意引脚分配因为这类端口通常需要手动指定物理引脚约束。这三件事做完BD文件的复用基本就算完成了。整个流程如果顺利从打开新工程到Wrapper适配完通常十分钟以内就能搞定。这也是为什么在很多场景下直接复制.bd文件比脚本方式更快——它跳过了重新执行搭建动作的过程直接拿到的是最终结果。3.3 同工程内复制BD的注意事项上面讲的是跨工程复制BD但还有一种情况经常出现同一个工程里需要在现有BD的基础上生成一个副本A版本不带PCIeB版本带PCIe两个版本并行维护。在Vivado里这个操作可以在Sources窗口里右键BD选择Copy Block Design。复制出来的BD会以原名字加后缀的方式命名例如pl_platform_copy1。复制完成后对新副本的修改不会影响原BD两个版本可以并行生成不同的比特流。但这里有几个细节必须注意复制后必须重命名。copy1这种系统自动起的名字没有可读性过两天你自己都想不起来哪个是哪个。建议复制后立即右键重命名成一个有业务含义的名字比如pl_platform_pcie_on。复制后必须重新Generate Output Products。因为新BD是一个独立的实例它的输出产物需要单独生成。这一步容易被忽略结果就是新BD在综合时报一堆“missing product”的错误。地址映射必须重新检查。虽然复制过来的BD内部连接关系保持原样但新副本可能挂在新的AXI主端口下面地址分配可能冲突需要去Address Editor里确认。老实说同工程内复制BD这个操作本质上是Vivado提供的一个“另存为”功能并不复制底层IP的实现网表所以复制后的第一次综合会比正常情况慢一些因为要重新跑一遍相关IP的合成。4. 两种方法的核心对比与选择建议4.1 能力与适用场景对照表我在项目里两种方法都反复用过这里把它们的差异放在一张表里方便大家在动手之前先判断用哪条路。对比维度脚本复用write_bd_tcl sourceBD文件复用Import .bd文件创建方式记录搭建过程脚本重放复制设计结果直接导入操作耗时分钟级脚本执行生成产物分钟级导入生成产物跨版本Vivado兼容性好可用-replace_all自动升级IP一般导入后通常要手动Upgrade IP版本管理友好度极高文本脚本可diff、可review低BD文件差异不直观灵活性高可参数化、可修改脚本内容低导入后基本是原样修改靠手动环境依赖依赖IP仓库配置依赖.bd文件完整性和路径适合场景团队协作、多版本分支、自动化流程快速把已验证的BD搬到新工程从这个表格能看出来脚本复用更“工程化”BD文件复用更“直接”。如果项目有严格的版本管理要求和自动化构建需求脚本方式会是性价比更高的选择如果只是临时要把一个调好的平台搬到另一个工程验证一下直接复制.bd文件反而更省心。4.2 混合使用策略两种方法配合的最佳实践我自己在实际项目中并不是二选一而是把两种方法混合起来用的。我的做法是以脚本为主以BD文件为辅。具体来说一个BD验证稳定后我会用write_bd_tcl把它的搭建脚本导出统一放到一个专门的bd_scripts目录下并且用Git管理。当新工程需要复用这个BD时优先用source脚本的方式搭建。如果source过程顺利就不管它如果source过程中因为某些原因总是卡在某个IP上比如自定义IP路径丢失、IP核版本不在当前仓库里这时候我就会退回备用方案——直接从旧工程的.srcs目录里复制.bd文件到新工程导入。还有一种更细的配合方式有些时候一个BD的验证版本是用Vivado GUI手动调的还没整理成脚本但新版本Vivado已经装好了重新搭一遍BD浪费时间直接用write_bd_tcl导出脚本后在新版本里source又会遇到IP版本升级的连锁问题。这种情况下我的选择是先复制.bd文件导入新工程让Vivado自动Upgrade IP然后用Upgrade之后的工程再执行一次write_bd_tcl。这等于把“手动调好的结果”通过Vivado的升级功能过了一遍最后产出的是适配新版本的脚本后续团队其他人再用这个脚本就通畅了。这种混合策略的好处是无论遇到什么环境问题都有两条路可以走不至于被某一个工具的报错卡死。5. 复用BD过程中的常见问题与排查技巧5.1 脚本source报错IP版本不匹配与自定义IP路径缺失write_bd_tcl导出的脚本在新工程里source时最常见的报错就是类似“IP version x.x.x not found in the current Vivado IP Catalog”这样的提示。这个问题的本质是新版本Vivado的IP库里已经没有旧版本的IP了解决方法是source之前确认是否加了-replace_all。如果脚本是之前导出的没有带这个参数最简单的办法是重新导出一次执行write_bd_tcl -force -replace_all C:/project/scripts/pl_platform_bd.tcl然后再source。需要注意的是-replace_all虽然能把IP自动替换成新版本但个别IP在升级后其内部配置可能会变化特别是DDR控制器、PCIe这类复杂IP升级后要重点检查配置寄存器、时钟频率、位宽等参数是否与硬件设计一致。我遇到过MIG在升级后默认DDR型号变了导致板卡跑不起来的案例所以不要相信“替换完就万事大吉”至少要过一遍关键配置。另一个高频报错是“Cannot find referenced IP”或者“IP repository not found”。这通常是BD里用了自定义IP比如团队自己封装的AXI接口加速模块。脚本里会引用IP仓库路径但路径中可能写死了旧工程的绝对路径或者路径里带中文导致解析失败。解决办法是在新工程里先设置好IP仓库路径set_property ip_repo_paths {C:/my_ip_repo C:/other_ip_repo} [current_fileset] update_ip_catalog然后再source脚本。如果脚本里硬编码了旧路径可以在source之前用文本编辑器全局替换路径字符串或者把自定义IP的仓库路径统一放到一个固定的、团队共用的路径下比如C:/ip_lib这样脚本在每台电脑上都能跑通。5.2 BD文件导入后提示需要升级IP复制.bd文件导入新工程后经常会出现一个黄色警告说当前工程使用的Vivado版本比创建BD时的版本新需要Upgrade IP。这个提示不算错误但如果不处理后续综合可能会失败。处理方式是在Sources窗口里选中BD下的任意一个IP右键选择Upgrade IP或者通过菜单Reports - Report IP Status在弹出的对话框里逐个勾选需要升级的IP然后升级。这里我特别提醒一点升级IP是一个不可逆操作一旦升级完成旧版本IP相关的配置可能被改写。所以操作前建议先把原工程的.bd文件备份一份。万一升级后发现关键配置错了还能回退。升级完成后重新Generate Output Products再跑一遍综合。如果综合过了但时序不满足优先检查升级后IP的时序约束是否发生变化。MIG和高速收发器这两类IP升级后时序变化是最明显的它们的约束文件往往跟着版本更新。5.3 复用后仿真跑不起来仿真模型与文件缺失很多人复用BD后综合和实现都正常但一到仿真就报错提示找不到glbl模块、找不到IP的仿真模型或者仿真时输出一堆X态。这个问题绝大多数情况下是Generate Output Products时没有生成仿真相关的文件。正确做法是右键BD - Generate Output Products在弹出的对话框里把“Simulation”选项勾上或者直接在Tcl Console里执行generate_target simulation [get_files pl_platform.bd]生成完之后检查一下工程目录下是否出现了simulation相关的子目录里面有IP的.v或.sv源文件。另外Vivado仿真时还需要添加全局复位模块glbl这个文件通常在Vivado安装目录/data/verilog/glbl.v如果仿真报找不到glbl需要手动把这个文件加入到仿真文件集合里。还有一类仿真问题出在中间层。BD里如果挂了PL-PS中断、AXI性能和跨时钟域逻辑仿真时需要额外的初始化序列否则DDR模型或PS端不会正常启动。这种情况下仿真testbench里要加上DDR模型的初始化代码或者直接参考原工程里已经跑通的testbench而不是从零写一套。5.4 Wrapper端口不匹配顶层信号连接错位这个问题用BD文件复用或者脚本复用后都可能遇到而且报错方式千奇百怪有时候是位宽不匹配有时候是名字对不上有时候是明明连了线但综合时被优化掉了。先说最基础的Wrapper文件由Vivado自动生成理论上BD有什么端口Wrapper就会有什么输出。但如果你在Create HDL Wrapper时选了“Let me edit”而不是“Let Vivado manage”后续BD端口变化时Wrapper不会自动更新这时候手动改Wrapper很容易漏改或者改错。我的建议是除非万不得已一律选“Let Vivado manage wrapper and auto-update”。再说顶层连接。一个BD导入新工程后如果顶层模块里引用的端口名和Wrapper导出的端口名不一致综合会直接报错。但有一种情况更隐蔽顶层信号名跟Wrapper端口名恰好相同但位宽不匹配。比如BD导出的m_axi_awaddr是32位顶层连接的这个信号声明成了64位综合不一定报错而是自动截断或扩展但仿真时数据完全对不上。排查这种问题没有捷径只能逐个端口对照位宽和方向。最后还有一个我踩过几次的坑如果新工程的顶层代码里没有把用到的BD端口约束到物理引脚上某些端口会在综合时被优化掉。现象是综合报告里显示资源使用量异常低或者I/O端口列表里少了很多信号。解决方法是检查约束文件确认所有需要对外连接的信号都分配了引脚或者在顶层加一段防止优化的属性比如(* keep true *)标记。5.5 路径问题与工程环境设置这个坑很小但一旦踩上非常浪费时间。Vivado对中文路径和空格路径的支持一直不算好write_bd_tcl生成的脚本尤其敏感。如果工程路径或者IP仓库路径里有中文、空格、特殊符号source脚本时经常报出很奇怪的错误比如找不到文件、无法解析路径、甚至Vivado直接崩溃。我的建议是所有FPGA相关工程、脚本、IP仓库一律使用英文路径并且路径层级不要太深。我见过最夸张的一个案例一个工程放在C:/Users/张三/桌面/2024项目/新版本/最终版/下面Vivado每次综合都要报几个莫名其妙的警告最后把整个工程移到C:/fpga_prj/下面所有问题都没了。所以别跟工具较劲路径规范是FPGA开发的基础素养。另外复用BD时新工程里最好提前确认一下目标芯片型号和封装是否正确匹配。如果旧工程用的是XC7Z020-CLG484你新工程建成了XC7Z020-CLG400BD里的引脚约束、某些带有物理位置的IP比如MGT bank位置直接就会报错。这类错误跟BD本身没关系是工程环境没对齐所以新建工程时务必先核对芯片型号。写在最后的一点个人体会BD复用这件事看起来只是一个操作技巧但实际做不做得好直接决定了项目迭代的速度。我见过有的团队一个简单的平台改动要拖上一周原因就是每次都在重新搭BD、重新配置IP也见过一个团队能在半天内把完整的功能平台从旧芯片迁到新芯片差别就在于是否把BD的复用流程固化下来了。我个人这两年养成的习惯是每验证通过一个BD第一时间用write_bd_tcl把脚本归档再把对应的.bd文件同步备份到固定目录。看起来是双份工作量但每次新项目启动时这种“双保险”都能让我少折腾好几个小时。如果你也在做多项目、多版本的FPGA开发真心建议从现在开始就试着把这两个方法用起来前期花点时间把脚本和文件整理好后面能省的时间绝对超出你的预期。
返回列表