十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

FPGA编译时间太长?Vivado全流程加速实战:从13小时压缩到5小时

FPGA编译时间太长?Vivado全流程加速实战:从13小时压缩到5小时 做FPGA的谁没被编译时间折磨过特别是到了大工程、复杂时序约束的项目一次全量编译跑上十几个小时那真是家常便饭。我见过不少团队早上上班点一下综合下午下班还没跑完布局布线第二天早上来一看时序违例改了代码重新来一轮又一天过去了。这种状态持续下去项目进度全靠运气迭代效率低到让人崩溃。我自己经历过最夸张的一次是在一个多时钟域、带PCIE和DDR接口的图像处理项目上Vivado全量编译跑了整整13个小时。当时是真急芯片选型定了、板上资源也评估完了结果卡在编译上每天啥也没干就等结果。后来被逼着系统性梳理了整个编译流程从工程结构、综合策略、实现配置到机器环境全调了一遍硬是把时间压到了5小时左右。今天就把这套完整的加速方案、操作细节和踩坑记录整理出来给还在等编译的同学一个参考。1. 为什么你的FPGA编译那么慢先搞清楚时间都去哪了加速的起点不是盲目加机器配置而是先量化耗时分布。就像做时序优化你得先知道关键路径在哪才能对症下药。1.1 一次完整编译的四大阶段一个完整的FPGA工程编译在Vivado里通常包含四个耗时大户综合Synthesis、布局Placement、布线Routing和比特流生成Bitstream Generation。综合阶段把RTL代码转换成门级网表这个过程涉及逻辑推断、技术映射和优化。工程越大、层次越深综合越慢。布局阶段把网表中的逻辑单元映射到FPGA物理资源上这个过程要不停评估时序和拥塞是一个迭代优化的过程很多综合出来的大设计在这里能跑几个小时。布线阶段最耗时尤其对时序收敛要求高的设计布线器要反复尝试不同路径分配来满足时序约束。比特流生成相对快但大工程也会消耗不少时间。我说句实话很多人等13个小时不是因为电脑太差而是布线阶段拖了后腿或者综合阶段策略没有设置对导致整个流程在无效迭代。1.2 瓶颈判断先量化再优化我第一次尝试优化时直接在Vivado的Log窗口里翻了半天运行报告把每个步骤的时间记录下来。建议你也这么做在Tcl Console下执行综合和实现命令时Vivado本身就会打印每个阶段的耗时比如opt_design、place_design、phys_opt_design、route_design各自的Running时间。以我那个13小时的项目为例时间分布大概是这样的阶段耗时占比synth_design1小时40分约13%opt_design45分钟约6%place_design3小时30分约27%phys_opt_design1小时20分约10%route_design5小时30分约42%write_bitstream15分钟约2%看到这个分布我心里大概就有数了。布线占了将近一半时间place_design也不短。单纯靠换CPU提升有限必须从策略和流程上做优化。1.3 硬件基础别让机器拖后腿这里要说清楚硬件永远是个绕不开的基础。FPGA编译和普通软件编译的最大区别是它对内存带宽和I/O吞吐的要求极高。综合和布线过程会生成大量中间文件这些小文件动辄几个GB硬盘速度直接决定了读写等待时间。我的经验是三个关键硬件指标CPU核心数、内存容量、SSD读写速度。Vivado的布局布线是支持多线程的CPU核心越多并行计算能力越强内存不够会导致系统开始交换内存速度断崖式下跌机械硬盘读写大量小文件的时候延迟会让你怀疑人生。如果预算有限优先换NVMe SSD效果立竿见影然后再考虑加内存最后考虑CPU升级。2. 加速的底层逻辑算力、并行度与复用搞清楚时间分布后接下来的思路就是三件事把能并行的并行起来把能省掉的步骤省掉把能复用的结果复用好。2.1 合理配置Vivado线程不是越大越好Vivado支持多线程布局布线但很多人不知道的是OMP线程数并不是設置得越大越好。Vivado在布局布线阶段对线程的使用有自己的一套逻辑线程数设置过高反而会导致线程调度开销增大甚至出现内存带宽争抢。我的推荐是先看机器物理核数。建议使用物理核心数而不是逻辑线程数因为超线程对Vivado这类计算密集任务的作用很有限有时反而会因为缓存争用导致性能下降。比如一台8核16线程的机器设置max_threads 8最稳妥。设置方法很简单在Vivado的Tcl Console里执行set_param general.maxThreads 8或者在综合和实现时加上-max_threads选项。这里有个细节综合阶段的synth_design -max_threads 8和布局布线阶段的place_design -max_threads 8是分开的都要设置。2.2 综合策略面积优先往往更省时间这个观点可能有人不认同但我是实测过的。在synth_design阶段Vivado默认的优化策略是area_optimized_high但这个策略在综合时做了大量逻辑重整和面积优化综合时间明显变长而且优化后的网表有时反而会给布局布线增加负担。我在对比测试中发现使用runtime_optimized综合策略可以显著减少综合时间而且对最终布线后的时序影响很小。原因很简单现代的FPGA资源已经很丰富面积优化的边际收益越来越低但时间成本是实打实的。除非你资源利用率超过80%否则没有必要在综合阶段做极限面积优化。命令行下的添加方式synth_design -top top_module -part xc7k325tffg900-2 -mode out_of_context -retiming -directive RuntimeOptimized2.3 工作目录的学问别把中间文件放机械盘这一步经常被忽略。Vivado在编译过程中会在工程目录下生成大量中间文件包括检查点、报告、临时数据库等。如果工程放在机械硬盘上光I/O等待就能让你多等20%-30%的时间。我自己的做法是把整个工程目录放到NVMe SSD上同时把Vivado的缓存目录和临时文件目录也重定向到SSD。这样布局布线过程中频繁的读写操作等待时间能压到很低。另外一个进阶技巧是如果你的内存足够大比如64GB以上还可以考虑把工程放到tmpfs内存文件系统上编译速度还能再上一个台阶但要注意内存掉电丢失的风险编译完成后及时拷回磁盘。3. 工程级加速实操从13小时到5小时的落地下面这几步是我那台机器上最终把13小时压到5小时左右的核心操作。按顺序做每一步都能省下不少时间。3.1 项目模式设计分模块编译与增量很多人在用Vivado的时候习惯把所有RTL文件一股脑全加进工程然后全量编译。这在工程前期没问题但到了中后期每次只改了一小段代码全量编译的代价就太大了。我强烈建议使用Vivado的Out-of-ContextOOC模式。简单来说就是把设计里的某些模块单独拎出来综合生成独立的综合检查点顶层综合的时候直接复用这些检查点不用重新综合这些模块。如果你有一个模块被改了只需要重新综合这个模块其他模块的综合结果保持不变。实际操作上在Vivado的工程设置里给每个子模块右键选择OOC综合方式或者可以在XDC约束文件里对某个模块设置OOC属性。OOC模式还有一个额外好处是可以并行综合多个模块因为模块之间是独立的可以同时跑多个综合任务。我自己用脚本的方式去控制并行度实测6个模块并行综合的速度非常可观。3.2 实现阶段的策略配置与增量布局布线布局布线是最耗时的阶段也最值得花时间去调策略。Vivado里的place_design有几种Directive默认是Default属于平衡策略但并非最快选择。我的推荐是Quick模式。在这个模式下布局器会减少迭代次数降低优化深度从而大幅缩短布局时间。对于大多数设计来说Quick模式带来的时序影响可以通过后面的布线阶段弥补回来。我的13小时项目里place_design从3.5小时降到了1小时40分效果很明显。布线阶段同样也有Directive可以选择。默认的Default已经比较均衡如果你不太约束资源拥塞可以试试Quick或RuntimeOptimized。但有一点要提醒布线阶段的加速是有代价的如果设计时序余量本来就很小还是要用默认策略否则时序收敛不了整体时间反而更长。当你每次只修改了少量代码时可以尝试增量布局布线。在Tcl中加上-incremental参数让布局布线基于上一次的结果进行局部调整而不是全盘重来。命令如下place_design -incremental route_design -incremental增量方式最快能省掉60%左右的实现时间但有一个硬性要求必须基于上一次布局布线生成的检查点文件而且工程结构发生了大改的场合不适用。一般来说只改动部分逻辑、时钟结构没变、资源变动不大时增量效果最好。3.3 Checkpoint复用能省则省的大杀器如果你经常做小改动的迭代CheckpointDCP文件复用这个思路一定要掌握。默认情况下就算你只改了一行代码整个综合和实现也要从头跑。但自己的工程脚本里如果保存了综合后的检查点synth.dcp那么在改动RTL后可以先评估改动的影响范围。如果只是改了某个组合逻辑的表达式没有改动模块接口和时钟结构那就可以考虑跳过综合直接加载之前的综合检查点进去改网表。虽然这种操作适用范围有限但在大型工程的局部修改中能省掉好几个小时的等待时间。更重要的是在实现阶段实现完成后生成的route_design.dcp如果下次改动很小可以加载这个检查点做增量布局布线这样的迭代路径是非常快的。我在实际项目里甚至用脚本控制自动判断「若逻辑改动量低于某个阈值就走增量流程」。3.4 综合阶段必须开启retimingRetiming重定时这个选项很多老工程师会忽略。Vivado的retiming会将组合逻辑跨寄存器边界重新分布在不动功能的前提下优化关键路径。这对时序收敛帮助很大更重要的是它能减少后续布局布线阶段的迭代次数。开启方法很简单在综合时加-retiming选项。我实测过开启retiming会让综合时间增加10%左右但在布局布线阶段能省回来更多总耗时反而下降了。这是一个典型的“以时间换时间”但收益是正的。综合脚本的完整版我的习惯是这样组织read_verilog [list rtl/xxx.v rtl/yyy.v] read_xdc constraints/top.xdc synth_design -top top_module -part xc7k325tffg900-2 \ -directive RuntimeOptimized -retiming -max_threads 8 write_checkpoint -force ./checkpoints/synth.dcp实现阶段的脚本open_checkpoint ./checkpoints/synth.dcp opt_design -directive RuntimeOptimized place_design -directive Quick -max_threads 8 phys_opt_design -directive AggressiveExplore route_design -directive RuntimeOptimized -max_threads 8 write_checkpoint -force ./checkpoints/route_design.dcp这里多说一句phys_opt_design它在布局布线之后做物理优化用来修正布局阶段遗留的时序问题。这个阶段不要开太重AggressiveExplore和RuntimeOptimized之间的取舍要看时序余量。时序紧张就AggressiveExplore时间紧就RuntimeOptimized。3.5 Tcl脚本批量并行编译的进阶玩法如果你管理的是一个大型工程或者多个工程手动点Vivado GUI是效率极低的。我个人的习惯是全部用Tcl脚本驱动然后在多个终端里并行跑不同的OOC模块综合任务。下面是我常用的并行综合脚本思路# 并行综合多个OOC模块 for module in mod_a mod_b mod_c mod_d; do vivado -mode batch -source synth_ooc.tcl -tclargs $module log_${module}.log 21 done wait每个OOC模块的综合是独立的可以并行进行而且不会互相干扰。这个思路对于那些顶层综合时间很长、模块比较多的工程可以省下大量时间。我在一个包含8个主要模块的工程上测试过并行综合比串行综合快了3倍以上前提是你的CPU核心数足够分配。4. 典型问题与排查经验优化过程中一定会遇到各种坑。下面这几个是我踩过以后总结出来的对照着做可以省很多冤枉时间。4.1 线程设置不当导致系统卡死我一开始图省事把max_threads设成了16结果机器在place_design阶段直接卡死鼠标都动不了最后只能强制重启整个编译白跑了。原因在于Vivado的布局布线不仅吃CPU还吃内存带宽。线程数超过物理核心后线程间的数据同步开销暴增而且超线程共享执行单元反而导致性能下降。我的建议是在任务管理器或htop里看清楚CPU型号和核心数然后设置成物理核心数。如果机器上还跑着其他任务最好减少1-2个线程留出余量。另外编译期间尽量不要做其他重负载操作这会明显拖慢编译速度。4.2 增量布局布线报错检查点不匹配用-incremental时最常见的问题就是提示检查点不匹配或者跑着跑着报出类似Reference checkpoint is not compatible的错误。这个问题的根源通常是源文件列表变了、约束文件变了或者顶层模块接口变了。排查思路很简单确认增量运行时加载的DCP是上一次完整编译后的DCP同时确认RTL和XDC没有发生结构性变化。如果只是修改了内部逻辑没有增删端口和寄存器一般都能顺利通过。另外要注意打开检查点时不要额外执行read_verilog或read_xdc这会污染检查点中的数据库。4.3 布线阶段内存溢出布线是内存消耗大户特别是大型器件、高利用率设计。有一次我在一台32GB内存的机器上跑一个资源占用90%以上的设计route_design跑到一半就报内存不足程序被系统杀掉了。解决方案有几种一是加大交换空间给系统留出更多缓冲余地但速度会下降很多二是直接加内存这是最推荐的做大型FPGA设计建议32GB起步64GB不算多还有一招是通过set_param route.directive Quick降低内存占用Quick模式布线内存消耗会比默认低一截。如果你的设计非常复杂建议先用资源评估报告看看预计的资源占用率再决定是否要换机器或者调策略。4.4 时序确实变差了怎么办加速方案有时候确实会牺牲一点时序结果尤其是用了Quick布局策略或者RuntimeOptimized布线策略后可能发现WNS最差负时序余量变小了甚至出现违例。我的应对思路是分层级处理。第一是先把综合改为RuntimeOptimized配合-retiming加强逻辑优化深度第二是布局阶段改回Default保留更多布局迭代次数第三是布线上别用Quick改用默认策略最后再用phys_opt_design做一轮积极优化。一般来说经过这几步调整时序能够恢复到正常水平而整体耗时相比优化前还是有明显减少。总结下来我的优化效果是这样的配置项优化前优化后综合策略DefaultRuntimeOptimized retiming布局策略DefaultQuick保留phys_opt布线策略DefaultDefault时序优先线程数48物理核数OOC模块无全部分层OOC工程存储机械盘NVMe SSD总耗时13小时5小时10分4.5 头顶上的坑时序约束对编译时间的影响时序约束写得好不好也会直接影响编译时间。有些工程师为了省事XDC里写了非常宽松的约束这看起来让时序收敛容易了但布线器会花大量时间去找最优解反而拖慢编译。另一些人约束过紧布线器反复尝试也满足不了一直迭代到最后时限才退出也慢。好的做法是按实际需求分组设置时钟约束对跨时钟域路径用set_false_path或用专门的CDC约束告诉工具“不要在这里花时间”。对不需要在编译时就严格收敛的路径比如测试逻辑、调试接口能设false path就设false path布线器就不用在这些路径上反复折腾。这一步对大型工程影响极大有时能把布线时间缩短30%以上。5. 调试期与收敛期的差异化策略最后分享一个我个人习惯用的思路把FPGA编译分为两种场景一种叫调试期一种叫收敛期两种场景的编译策略完全不一样。调试期的目的是快速验证功能逻辑这时候时序收敛与否不是重点我基本会用最快的策略组合综合用RuntimeOptimized布局布线用Quick甚至直接关闭phys_opt_design。这个阶段只要能烧进板子、功能跑通就行。我在调试期可以把编译时间压到2小时以内大幅提升了迭代反馈速度。收敛期就是准备出版本、跑时序验收的时候这时候我会把策略切回时序优先综合用Default或RuntimeOptimized布局用Default布线用Default再加上phys_opt_design -directive AggressiveExplore。这个阶段时间会长一些但时序收敛的可靠性才是第一位的毕竟编译时间再短时序不过版也是白搭。这种区分策略的思路很简单不要用一套固定配置应付所有阶段。调试期追求速度收敛期追求质量这样效率才能真正上去。根据我个人的使用感受FPGA编译时间长这个问题并不是无解的玄学。只要把时间分布看明白把OOC结构、策略配置、线程设置、存储介质这些环节逐一优化到位把十几小时的编译压到5小时甚至更短完全是能做到的。对这个流程的改造值得每一个做大型FPGA工程的人投入时间去做。
返回列表