ARTICLE DETAIL

资讯详情

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

FPGA工程中Synplify与Vivado协同设计全流程解析

FPGA工程中Synplify与Vivado协同设计全流程解析 先把结论摆在前面FPGA工程里同时出现Synplify和Vivado并不是“二选一”而是两个工具分工协作。Synplify负责RTL综合把Verilog/VHDL变成网表Vivado负责实现把网表布局布线成比特流。问题在于现代FPGA工程基本离不开IP核而IP核恰好是协同设计里最容易翻车的地方。你单独用Synplify综合RTL会遇到IP核被当成黑盒导致时序丢失单独用Vivado又享受不到Synplify在复杂逻辑综合上的优势。这篇文章就以一个带IP核的实战工程为例把Synplify和Vivado怎么串起来、IP核在两边怎么处理、每一步的操作细节和踩坑实录全部讲透。适合正在做FPGA开发、尤其是老项目要迁移到Vivado或者团队综合流程需要统一工具的工程师参考。1. 为什么要把Synplify和Vivado放在一起用1.1 两个工具各自的定位Synplify是Synopsys出品的FPGA综合工具在业界口碑不错的点在于综合速度快对大规模RTL代码的时序优化比较激进尤其擅长处理跨时钟域逻辑、状态机编码和面积/速度的折中。它输出的是EDIF网表.edf/.edn或者特定厂商格式的网表不负责布局布线。Vivado则是Xilinx现在叫AMD的完整开发套件从综合、实现到下载调试一条龙。Vivado自带综合器Vivado Synthesis其实综合能力也不弱而且在处理Xilinx原生IP上有天然优势因为IP的生成、约束、网表都是配套的。既然Vivado综合也能用为什么还要绕一圈用Synplify我在实际项目里遇到的主要有几类情况老项目从ISE或Quartus迁移过来历史代码一直跑在Synplify综合流程上换成Vivado综合后时序或者资源利用率变了要重新调很久。团队里有统一的综合规范比如固定用Synplify做代码质量检查Lint、跨时钟域报告希望整个流程都走同一套综合口径。某些第三方IP或者加密RTL只支持特定的综合工具或者只提供了面向Synplify的工程接口。复杂控制逻辑为主的设计Synplify综合出来的频率确实更高这是综合算法差异决定的。这里要说明一个核心点Vivado和Synplify不是竞争关系而是上下游关系。Synplify把RTL变成网表后Vivado拿到网表继续做布局布线两者的职责天然就可以拆分。1.2 Synplify Vivado协同的适用边界也不是所有工程都适合协同设计。如果设计里大量依赖Xilinx原生的高速接口IP比如Aurora 8B/10B、MIPI CSI-2、PCIe我建议直接全程用Vivado综合省事、少坑。因为这类IP和Vivado实现的耦合度太深硬要拆开反而麻烦。相反如果设计里主要是自研逻辑、状态机、协议解析IP核只有少数几个简单的FIFO、BRAM、除法器那就很适合用Synplify的流程。Synplify综合主逻辑IP核以黑盒或网表形式挂进去最后在Vivado里合并实现。一句话结论协同设计适合“自研逻辑复杂、IP核相对独立”的工程。如果你的工程是“IP核堆出来的”老老实实用Vivado全流程更划算。1.3 协同设计整体流程整个流程可以概括为5步在Vivado里创建IP核生成网表和对应约束。把IP核封装处理让Synplify在综合时把它当黑盒。在Synplify里读入RTL和约束完成综合导出EDIF网表。回到Vivado创建工程把Synplify输出的网表和IP核的网表一起加入。在Vivado里完成布局布线、时序收敛生成比特流。这里面最关键的环节是“IP核在Synplify和Vivado之间怎么传递”。处理不好时序约束会丢、网表会乱、DRC会报错。下面专门讲这块。2. IP核在协同设计里的处理方式2.1 先搞清IP核的“交付物”在Vivado里生成一个IP核并不是只生成一个文件而是一整套东西。以AXI4-Stream Data FIFO或者Block Memory Generator这类简单IP为例生成后会看到ip_name.xciIP核的配置信息记录参数、版本、接口。ip_name.dcp如果选择了OOC综合Out-of-Context这个文件是IP核综合后的网表包含逻辑和约束。ip_name_stub.v黑盒声明文件供上层例化时做仿真和综合。ip_name_sim_netlist.v仿真模型。ip_name.xdcIP核自身的时序约束有时候约束会在dcp里不一定单独给xdc。在协同设计流程里我们最关心的是两点让Synplify知道IP核的存在哪怕只是黑盒让Vivado在实现阶段能找到IP核的真实网表。2.2 Synplify端IP核黑盒处理Synplify不认识Vivado的.xci文件但它支持Verilog/VHDL的模块例化。所以常规做法是在RTL里直接实例化IP核模块模块名和端口和Xilinx生成的保持一致。给综合工具加黑盒声明告诉Synplify这个模块内部你不需要知道端口留着就行。Synplify综合时会把这个模块当成一个“不透明盒子”只保留端口连接关系。黑盒声明有两种常见写法。一种是在RTL里用(* synthesis black_box *)属性另一种是在Synplify约束文件.fdc里用define_attribute命令。以Verilog为例(* synthesis black_box *) module fifo_ip ( input wire clk, input wire rst, input wire [31:0] din, input wire wr_en, output wire [31:0] dout, output wire full, output wire empty ); endmodule如果不想改RTL也可以新建一个黑盒声明文件比如ip_blackbox.v里面只放这些空壳模块在Synplify工程里把它加进去就行。我更推荐这种方式尽量不动原来的RTL避免版本管理上的混乱。2.3 Vivado端加载IP网表与dcpSynplify综合完成后输出的EDIF网表里IP核还是以黑盒形式存在的只有端口的连接关系没有内部逻辑。这时候必须回到Vivado把IP核实际的内容补充进来。Vivado工程里添加Synplify输出的EDIF同时确保IP核已经生成并综合过。在Vivado实现阶段如果检测到某个模块是黑盒会自动去找同名的IP核网表或者dcp来“填空”。这里有一个很多人踩过的坑IP核如果之前在Vivado里没有综合过只生成了xciSynplify的流程下会报黑盒找不到。解决办法是在Vivado里对IP核执行OOC综合生成dcp文件或者在生成IP时勾选综合选项确保IP核的网表在实现时可用。还有一点要注意IP核的stub文件里如果有(* black_box *)属性在Vivado综合时没问题但如果Synplify也读了同一个stub文件属性可能会冲突。干脆在两边各用各的声明文件避免互相干扰。3. 协同设计实操全流程从RTL到比特流3.1 版本选型与安装工具版本匹配是协同设计的第一道坎。最简单的原则Synplify要支持对应厂商和器件系列Vivado版本别太老Synplify版本也最好用较新的。以Synplify 2019.03、2021.03为例它们支持的Xilinx器件系列覆盖了UltraScale、7系列等主流型号。Vivado 2018.2之后的版本对EDIF网表的兼容性比较稳定我实际用下来Vivado 2019.1和Synplify 2019.03、Vivado 2020.1和Synplify 2021.03搭配都没遇到大问题。安装上就是常规操作源码安装或者普通方式安装SynplifyVivado按官方流程安装。有一点提醒Vivado安装时如果只装Vivado HL Design Edition就够用了不需要装Vivado Lab Edition后者主要是给实验室调试用的没有完整综合实现功能。3.2 在Vivado中生成并导出IP拿一个实例工程说明。假设我的设计里用到了一个Block Memory Generator IP做图像行缓存。一个FIFO Generator IP做跨时钟域数据缓冲。一个浮点除法器IP或者固定点的除法器IP做坐标计算。一个自定义的UART RX IP核自研RTL或者Xilinx的UART 16550 IP。我只挑其中两个有代表性的说FIFO IP和存储IP。在Vivado里用IP Catalog创建FIFO IP配置位宽32位、深度1024、独立时钟。生成时会看到选项“Global”和“Out of Context (OOC)”。这里注意协同设计流程下必须选择OOC综合这样会生成独立的dcp文件方便后面独立综合。生成完成后在工程目录的ip_output目录下会有fifo_ip.dcp、fifo_ip_stub.v、fifo_ip.xci这些文件。把这些文件的路径记下来后面Vivado工程要用。如果你用的是Vivado脚本流程对应Tcl命令大致是create_ip -name fifo_generator -vendor xilinx.com -library ip -version 13.2 -module_name fifo_ip set_property -dict [list \ CONFIG.Fifo_Implementation {Independent_Clocks_Block_RAM} \ CONFIG.Input_Data_Width {32} \ CONFIG.Input_Depth {1024} \ ] [get_ips fifo_ip] generate_target all [get_ips fifo_ip] synth_ip [get_ips fifo_ip]synth_ip这条命令就是为了生成dcp。3.3 在Synplify中综合RTLSynplify工程的建立方式不细说了GUI操作很直观。重点是几个关键配置第一器件型号必须和Vivado工程一致。比如Vivado里选的xc7z020clg484-1Synplify里也要选同样的器件否则最后的网表不匹配。第二约束文件用Synplify的.fdc格式或者直接读入SDC。Synplify支持读SDC但这个SDC主要管综合时的时序约束。要注意Synplify和Vivado对时钟约束的语法细节有差异在Synplify里用的create_clock到了Vivado可能还要再转换一次。我习惯在Synplify里只约束最关键的系统时钟其余的放在Vivado的XDC里统一处理减少转换成本。第三顶层模块名必须和Vivado工程里设置的top一致。尤其是端口名字EDIF网表里顶层端口名会和RTL里的端口名保持一致Vivado里就得按这个名字添加约束。在Synplify里添加源码时把RTL文件和IP黑盒声明文件都加进去。黑盒声明文件我用单独的ip_blackbox.v里面像刚才说的那样声明所有用到的IP核空壳。这样Synplify综合后IP核在生成的EDIF网表里会变成“未连接内部逻辑的模块”。开始综合后Synplify会输出.edf文件和.srs报告。重点看报告里的时序预估和资源利用率如果这里时序已经烂到没法看基本可以断定RTL逻辑有问题赶紧回头改别等Vivado里再折腾。3.4 在Vivado中实现并生成比特流回到Vivado新建一个工程器件选型和Synplify一致。然后按顺序做添加Synplify输出的EDIF网表文件design.edf为设计源。导出或者添加之前生成的IP核的dcp文件确保IP核实现时能找到。添加XDC约束文件里面包含管脚约束和时序约束。设置顶层为EDIF网表的顶层模块。运行综合Vivado会用它自己的综合器处理EDIF网表这一步其实只是“读入”网表然后运行实现。这个过程里Vivado的“读入网表”和“综合”是两个概念。对EDIF网表Vivado在综合阶段会把网表映射到器件原语上相当于做一次“网表综合”。如果IP核的dcp在黑盒会被自动解析不需要额外操作。跑实现的时候重点关注布局布线后的时序报告和DRC报告。如果出现IP核相关的问题比如“Black Box”被保留就要回去检查IP核的dcp有没有正确加载。4. 关键问题排查与避坑实录4.1 “黑盒”一直解不掉这是协同设计里最典型的问题。现象是在Vivado实现后打开综合后的原理图看到某个IP核模块还是空的里面什么都没有或者报告里出现“Unresolved black box”之类的警告。排查步骤确认Vivado工程里是否包含了IP核的xci源文件。如果只添加了EDIF和dcp但没添加xciVivado可能无法正确绑定IP核。确认IP核的dcp文件确实生成成功了并且路径没有包含中文或空格。确认Synplify里黑盒声明的模块名和IP核实际模块名一字不差。大小写也得一致Linux环境下尤其要小心。在Vivado的Tcl Console里执行get_files -all查看dcp是否在工程文件列表里。4.2 综合后管脚错位EDIF网表的顶层端口顺序和RTL一致但有些粗心的工程师在Vivado的XDC里写了IO约束结果实现后发现某个信号绑错管脚。原因往往是Synplify综合时做了端口重排或者信号优化把某些悬空端口给优化掉了。解决方法是在Synplify里关闭顶层端口优化或者把顶层端口都加(* syn_preserve true *)属性。另外XDC里的管脚名要以EDIF网表的端口名为准可以在Vivado里用get_ports查看。4.3 时序约束跨工具“丢约束”Synplify里用SDC做了create_clock导出EDIF后Vivado实现时没看到这些时钟约束导致时序报告显示全是理想时钟。原因在于Synplify的时序约束默认并不会完整地写入EDIF网表里除非你在Synplify里勾选了“Write SDC”之类的选项。而且即便写了格式也可能和Vivado不完全兼容。我的做法是Synplify里只做初步评估真正的约束在Vivado的XDC里重新写一遍。具体来说Synplify里约束是“为了综合结果更准确”Vivado里约束是“为了实现按时序收敛”。两边分开写互不依赖反而少出问题。4.4 DRC报错DRC RTSTAT-2热词里提到vivado 报错 drc rtstat-2这个我遇到过。现象是跑完实现后DRC报告里报RTSTAT-2: 某个信号时序没有约束住或者某些路径未被时序分析覆盖。在协同设计流程里出现这个报错最常见的原因是IP核内部的异步复位或者跨时钟域路径没有约束。比如FIFO IP的两个时钟之间是异步的需要在XDC里设置set_clock_groups -asynchronous。如果忘了写DRC就会认为这些路径时钟关系未约束。另外有些自研逻辑里复位信号没有同步也会导致DRC报类似的警告。复位信号亚稳态问题这里顺便提一句跨时钟域的复位释放必须做同步处理最简单的方法是做一个两级触发器同步释放别图省事。4.5 IP核版本与Vivado版本不匹配有时候从老工程拷过来的IP核在Vivado新版本里会提示“IP版本太低需要升级”。如果直接升级IP核的端口、时序特性可能发生变化协同流程里黑盒声明和实际网表对不上。经验是升级前先在Vivado里生成一份升级说明对比端口变化如果是简单的FIFO、ROM、除法器IP升级后一般不用改RTL但仿真模型要重新生成。4.6 编译顺序与增量编译问题Synplify的工程在Vivado里做完一次布局布线后再次修改IP核参数需要重新生成IP核的dcp并且要让Vivado重新综合EDIF。很多人懒得删旧文件直接覆盖结果Vivado还是用旧的dcp。正确做法是改了IP核后用Tcl命令reset_target清除旧的生成文件再重新generate_target和synth_ip确保Vivado拿到的是新网表。4.7 一个避坑速查表现象可能原因排查/解决Synplify综合后IP核模块消失RTL里例化了但没加黑盒声明加syn_blackbox或新建黑盒声明文件Vivado实现后黑盒未展开dcp缺失或xci未添加确认OOC综合生成dcp并加入工程实现后DRC报RTSTAT-2异步时钟未约束设置set_clock_groups时序收敛困难约束未从Synplify传过来在Vivado XDC里重新约束管脚错位端口被优化或约束名不对用get_ports确认后用syn_preserveIP核版本升级后功能异常版本差异对比端口重新生成仿真模型5. 面向团队协作的一些经验5.1 工程目录规划协同设计流程涉及的文件比单工具流程多目录如果不规划好后期维护很痛苦。我推荐一个比较顺手的目录结构project/ ├── rtl/ # 自研RTL源码 ├── ip/ # IP核的xci、dcp、stub等生成文件 ├── synplify/ # Synplify工程文件和综合输出edf ├── vivado/ # Vivado工程文件 ├── constraints/ # XDC约束 ├── sim/ # 仿真测试平台 ├── scripts/ # Tcl、shell脚本 └── report/ # 时序报告、资源报告归档5.2 版本管理与回归Synplify和Vivado的工程文件都比较“重”不适合直接塞进Git里反复diff。我的做法是只把RTL、XDC、EDF、xci、脚本这些文本或核心产物纳入版本管理综合和实现的中间产物用脚本一键生成保证任何人拉下来代码都能重建工程。同时建议在每次重大改动后跑一遍完整的Synplify综合到Vivado实现的回归记录时序余量和资源占用。回归脚本可以用Makefile或者Python脚本封装Tcl命令跑完自动归档报告。这样哪一步引入的问题回头看报告就能定位。5.3 什么情况下回到Vivado综合协同设计不是万能的。我遇到过两个场景最后都乖乖回到Vivado综合一是带大量高速收发器的工程。Aurora 8B/10B、PCIe这类IP核工程里还不止一个用Synplify黑盒处理不仅时序约束麻烦IP核之间的交互也容易出问题。二是工程里用了Vivado的新特性比如动态功能交换DFX、多die约束或者比较新的器件系列Synplify的支持可能滞后。所以在项目启动阶段就要想清楚如果预估IP核比例超过一半、高速接口居多就全程用Vivado反过来自研逻辑为主再考虑Synplify协同。6. 一些补充经验从入门到落地的细节6.1 仿真验证不要依赖综合工具Synplify综合后的网表仿真在协同流程里其实是麻烦的因为IP核的内部逻辑来自Vivado的dcp两边网表要拼起来才能做后仿真工具链复杂不说调试也费劲。我更推荐的做法是功能验证在RTL层面完成用Vivado自带仿真器或者Modelsim直接跑IP核的仿真模型。Synplify阶段只看综合报告不做门级仿真。省下来的时间用来把实现后的时序报告看仔细比门级仿真更实在。6.2 Vivado里SDK/嵌入式流程的协同如果工程里带了MicroBlaze软核或者Zynq硬核也就是用到了Vivado SDK或Vitis情况会复杂一些。因为处理器子系统本身也是IP核而且涉及硬件工程和软件工程的联动。在Synplify协同流程下处理器子系统同样可以被当作黑盒处理综合结果导出EDIF后再回Vivado里补充硬件网表。但要注意这些子系统IP的dcp生成必须完整导出硬件描述文件.xsa或.hdf时要以Vivado里的完整网表为准。简单说带Soft Core的工程做协同设计建议“硬件IP和处理器子系统留在Vivado纯逻辑代码拿去Synplify综合”。这样两边都能发挥优势也不至于让工具链复杂到失控。6.3 关于多die FPGA的一点提醒热词里有“多die fpga languna约束”这个其实是指Versal和部分UltraScale器件的多SLRSuper Logic Region约束。在Synplify协同设计里如果器件有多die跨die的路径约束和物理约束强烈建议在Vivado里用XDC的Pblock和set_property SLR来处理。Synplify里虽然也能做物理约束但粒度不够细不如Vivado里来得直接。6.4 脚本化是正道不管是Synplify还是Vivado都支持命令行/脚本模式。Synplify有synplify_premier命令行接口Vivado可以跑vivado -mode batch -source run.tcl。把整个协同流程脚本化之后不仅可复现还能接到CI系统里自动跑回归。我在实际项目里用一个Python脚本管理整个流程解析配置→调用Synplify综合→调用Vivado实现→解析时序报告→输出一步到位的汇总结果。整个流程跑一遍大概十几分钟比手动在GUI里点来点去高效太多也更不容易出错。6.5 一个小技巧保留综合选项的备份在Synplify的GUI里设置了很多选项后如果长期维护建议把工程配置导出成.prj文件和RTL一起提交到版本库。这样就算有人不小心改了GUI配置也能用命令直接恢复。Vivado那边同理把write_project_tcl生成的脚本归档以后重建工程只需要一行命令。最后说点个人体会玩FPGA这些年我越来越觉得工具链不是越复杂越好而是越可控越好。Synplify和Vivado协同设计这条路适合那些对综合流程有执念、或者历史包袱比较重的团队它真正解决的问题是“怎么在不动老流程的前提下享受到新工具链的实现能力”。如果你正卡在IP核黑盒、时序约束传递、版本不匹配这些坑里建议按我文章里的目录顺序捋一遍大多数问题都能解决。最后再分享一个小技巧开始协同设计之前先在Vivado里用纯Vivado流程把整个工程跑通一遍确认IP核没问题然后再切换到Synplify综合。这样后面万一出问题你知道大概率是工具协同的问题而不是IP核本身的问题。这个前置步骤能省掉你一半的排查时间。
返回列表