ARTICLE DETAIL

资讯详情

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

Vivado全流程多核加速实战:从综合到仿真性能优化

Vivado全流程多核加速实战:从综合到仿真性能优化 做FPGA的人应该都有过这种感觉在Xilinx Vivado里点完综合或实现盯着进度条看半天主机CPU利用率却始终没跑满风扇转得懒洋洋的。明明机器是十六核三十二线程工程该等一小时还是等一小时。于是大家开始折腾Vivado的多核性能从综合到仿真把各种参数来回试结果发现有时候调了一堆线程数反而更慢了。这篇我们就把Vivado全流程的多核加速思路拉通一遍。我不会给你念手册只讲我自己在多个中型工程里踩坑后留下的经验哪些地方真的能吃满多核哪些地方所谓并发只是伪并行以及综合、布局布线和仿真三段的实操加速策略。适合被编译时间折磨过的工程师也适合刚接触Vivado、想知道怎么少走弯路的同学。1. 先看清楚瓶颈Vivado各阶段的多核利用现状1.1 综合阶段为什么“看上去不用满”很多人第一次优化就是狂调线程数但实际效果不明显原因很简单Vivado综合阶段的核心算法本身就不是为大规模并行设计的。综合要做逻辑推断、工艺映射、优化存在大量前后依赖的串行任务再加上文件级编译共享内存数据线程一多同步和通信开销反而把收益吃掉了。以RTL综合为例默认情况下Vivado确实会利用多核做一些并行优化但主要集中在内置的并行编译和部分子任务上。如果你用set_param general.maxThreads 8去开线程会发现CPU占用率并不是想象中那么节节攀升更多时候在4到6核之间抖动。这不是设置没生效而是综合引擎的并行度本来就有上限。还有一个特别容易忽略的点综合过程中的IO等待很重。工程里几十个IP读网表、读约束、写中间文件这些都是磁盘操作。如果你的工程放在普通机械硬盘上哪怕有十六个核也得等硬盘。综合阶段想要真正提速第一步不是加线程而是把工程挪到NVMe SSD上或者至少确保中间文件目录在高速磁盘上。1.2 布局布线阶段的多线程和可并行策略到了布局布线阶段情况会好不少。place和route都支持真正的多线程加速官方在很多版本里也明确提过实现策略对多核利用率的影响。我在实际观察中place_design阶段能够比较稳地跑满4到8个线程route_design阶段则更吃资源线程开足后CPU占用能明显上去。但需要清醒一点place和route算法内部同样有关键路径依赖比如布局阶段的拥塞计算、布线阶段的时序更新都会周期性地退化成单线程。所以这里多核优化的收益是“最高能快30%-50%”而不是“翻倍”。真正能把多核性能放大到极致的是同时跑多个不同的实现策略或种子让每个策略各占一个Vivado进程。这样每个进程内部用4到6核两个进程并行才谈得上把十六核机器真正榨干。另一个值得养成的习惯是合理选择和调整directive。想快速验证能否布线通过就用RuntimeOptimized这类偏向运行时的策略不要一上来就跑PerformanceExplore。后者会让place/route尝试更多优化组合本身耗时更长但在多核并行下做最终收敛却是一把好手。1.3 仿真阶段真正的并行甜点区仿真和前面两者完全不同。Vivado自带的xsim在单个testbench上基本是单线程模型我很少看到它能流畅吃满多核。很多人以为仿真是CPU密集型调高线程就能跑得快其实单个仿真进程对多核利用很有限真正的加速点在“并行跑多个独立case”。做FPGA验证时经常一个IP要跑几十上百个随机激励case。串行跑一轮可能要几个小时但如果你把case拆开同时启动8个仿真进程每个进程跑不同的随机种子理论上时间能压到接近八分之一。这比你在任何一个仿真器里挖多线程潜力都实在。仿真阶段另一个瓶颈是波形记录。每个case如果都开全量VCD或FSDB磁盘和内存都会爆炸。用并行仿真时要学会给不同case分配独立的日志和波形目录否则多个进程写同一个文件轻则仿真结果串掉重则直接卡死。2. 动手前的基础优化环境、硬件和任务编排2.1 多核环境下的硬件和系统准备想榨多核性能先看机器底子。Vivado综合和实现非常吃内存尤其到了实现阶段一个中型UltraScale工程动辄吃掉20GB到30GB内存。如果你只有16GB内存还想并行开两个Vivado进程系统很快就会开始swap速度比串行还慢。我个人的经验是跑并行Vivado任务时内存预算按每个进程25GB到30GB算CPU按物理核数而不是逻辑线程数算。比如一台8核16线程的机器最好不要同时跑超过4个Vivado进程否则超线程带来的争抢会让总吞吐量下降。原因很简单Vivado的算法对CPU缓存很敏感逻辑线程之间的资源竞争反而拖慢计算。操作系统也值得留意。Windows下跑大型Vivado并行任务文件路径长度、杀毒软件实时扫描、OneDrive自动同步都会成为多核加速的隐形杀手。如果你有条件用Linux服务器我强烈建议把大型综合和实现放到Linux上不仅句柄限制和文件IO更稳而且通过SSH远程批量管理多个Vivado进程要方便得多。2.2 线程控制和Vivado全局参数Vivado里常用的线程控制参数是set_param general.maxThreads可以写在Tcl脚本最前面也可以在Vivado Tcl控制台直接执行。需要注意这个参数不会让综合阶段自动疯狂占满所有核心它更像一个“上限”而不是“加速开关”。在综合阶段设4到6通常够了实现阶段可以设到8再往上收益很小某些版本甚至会出现内存和缓存开销增大导致变慢。还有一点容易被忽略不同版本的Vivado对general.maxThreads的响应模式不完全一样。老版本里它主要影响综合时的并行度新版本里可能对place/route也有影响。建议在换版本后不要直接沿用旧脚本先跑一个小工程对比一下CPU占用和耗时确认参数是否仍然有正向效果。除了线程参数还可以通过-jobs控制Vivado在launch_runs阶段并行处理多个run时的并发数量。这个参数在并行跑多策略、多seed场景下特别有用。不过要注意-jobs控制的是Vivado任务调度层面的并行度和进程内部线程数不是一个概念不要混在一起调。2.3 并行任务的License与目录隔离并行跑多个Vivado进程最怕的是License不够用。Vivado的License按“座位数”来限制并发免费WebPack版通常不支持多个并发实例。很多人以为开了几个终端就能并行结果License冲突只有第一个进程在跑其他都在等待。遇到这种情况在脚本里加一句catch {source /path/to/license_env.tcl}不算麻烦关键是提前确认你的License允许几个并发。目录隔离是非常重要但最容易被忽略的一环。并行跑多个run时每个run都应该有独立的输出目录、日志目录和工作目录。如果两个Vivado进程共用同一个.Xil目录或同一条Tcl脚本生成的report文件轻则报告互相覆盖重则中间文件锁冲突导致run失败。我的做法是每个并行任务建一个独立文件夹Tcl脚本里用current_project -quiet打开自己的工程副本输出路径用变量拼接比如vivado -mode batch -source run_impl.tcl -notrace log_impl_$strategy.txt 21 这样每个进程的log和中间产物都能对得上号排查问题也方便。3. 综合阶段加速从单核瓶颈里抠时间3.1 用OOC模式和增量综合打破串行综合阶段最常见的浪费时间点是整个顶层跑一遍哪怕只改了一行代码也要把全部逻辑重新综合一遍。Vivado的OOCOut-of-Context模式就是为了解决这类重复劳动。把IP核、子模块设成OOC后它们会单独综合并缓存结果顶层再综合时就不用重新处理这些模块。工程越大OOC带来的收益越明显。我在工程里通常把DDR控制器、PCIe IP、serdes类IP都设成OOC。这些IP的综合往往要花很长时间但它们结构相对固定不太会频繁改动。设置OOC后顶层的综合时间能砍掉三分之一左右。如果你是刚接触Vivado打开综合设置里的-mode out_of_context选项按IP逐个勾选即可。增量综合是另一个好帮手。Vivado通过分析RTL改动影响范围只重新综合受影响的逻辑而不是全量重跑。这个能力对后期小改特别友好比如改了一个状态机的状态定义增量综合可能只需要原来三分之一的时间。但千万别神话它如果顶层接口结构变化很大增量综合会退化回全量综合还不如老老实实跑一遍。3.2 多进程并行跑不同综合策略综合阶段如果想要利用多核最直接的思路是让不同策略并行。Vivado综合的directive有多种倾向比如默认、面积优化、性能优化、运行时优化等。最终我们要拿到的是一份时序满足的网表但怎么综合可以多试几版。我的做法是同时启动多个Vivado进程每个进程采用不同的综合directive最后比较QoR和综合时间。这个方法在工程规模变大后尤其有价值因为不同directive对面积和时序的权衡不一样某些模块可能在RuntimeOptimized下综合就是比默认快20%而且逻辑资源基本不变。并行跑综合时要注意库文件编译的冲突。多进程同时调用read_verilog和read_ip时如果共享同一个工程目录容易因为锁文件产生冲突。我通常用copy_project把工程复制成多个独立副本再在各个副本上跑不同directive。虽然多占了一点磁盘空间但稳定性高很多。3.3 实际案例一个中型工程的综合时间优化我在一个规模约150万等效逻辑单元的工程上实测过工程里有PCIe Gen3x8 IP、DDR4控制器、DMA引擎和若干自定义处理逻辑环境是Ubuntu 20.04 8核16线程 NVMe SSD。默认设置下一次顶层综合大约需要62分钟。把几个大IP切到OOC模式后顶层综合降到40分钟左右。再配合set_param general.maxThreads 4和增量综合我后面做小改动时经常能在20分钟内走完一轮综合。如果你还在全量综合每次跑一个多小时不妨按这个顺序来先切OOC再试不同directive最后才是调线程数。线程数放到最后不是因为它没用而是它带来的收益远没有前两项大。综合阶段的瓶颈更多在算法依赖和IO等待不是在CPU核心数量上。4. 布局布线提速把多核用在刀刃上4.1 place/route的多线程设置与directive选择布局布线阶段是Vivado整个流程里最耗时的部分经常占掉整个构建周期的一半以上。这里多线程的收益比综合明显但还是那句话不要追求线程数最大化而是追求“刚好能压住瓶颈”。我在place和route阶段会独立设置不同策略。快速迭代时用RuntimeOptimized这个directive下place和route都会牺牲部分质量换取更快的运行时间。如果只是流程测试、看资源是否够用、有没有DRC错误这个策略能让我在半小时内跑完一轮。等到功能验证差不多了切到PerformanceExplore跑最终版虽然要两三个小时但时序余量会好不少。多线程方面我一般把set_param general.maxThreads 8写在实现脚本里。实测8核16线程机器上这个设置能较好地调动资源但如果同时跑多个Vivado进程单个进程内的线程数反而要降到4或6把剩余的核让给其他进程。另外还要盯一下Memory利用率默认place/route的策略有时会因为内存吃紧而频繁GC。4.2 并行跑多个实现策略的流程并行跑多个实现策略本质上就是同时创建多个Implementation Run每个Run使用不同的directive或seed最后比较结果。Vivado里可以通过GUI创建多个Run也可以通过Tcl脚本自动化。大体流程是给同一个综合结果配置多个实现run分别设置不同的Strategy然后统一启动。我在Linux服务器上常用一段简单的脚本来批量跑不同directivefor strategy in RuntimeOptimized PerformanceExplore Performance_Refined; do mkdir -p $strategy cat $strategy/run.tcl EOF open_tcl_session read_checkpoint ../synth/post_synth.dcp set_param general.maxThreads 6 place_design -directive $strategy if {[get_property SLACK [get_timing_paths]] 0.1} { route_design -directive $strategy } write_checkpoint -force ./post_route.dcp close_tcl_session EOF (cd $strategy vivado -mode batch -source run.tcl run.log 21) done wait这段脚本里从综合后的DCP开始分别跑不同place/route directive最后汇总各自的时序结果选最好的那版。注意脚本里的place_design、route_design在不同版本Vivado里的参数略有差异但整体思路是通用的。并行跑多个策略时尽量让每个进程独立目录避免跑到同一路径。4.3 踩坑盲目并行反而更慢并行跑多个实现策略听起来很美好但有个明显的坑place和route每个进程占用的内存非常大。我曾经在一台128GB内存的机器上同时开了4个实现run每个run占30GB左右结果总内存逼近极限系统开始swap。最后4个run全部变慢总耗时比串行还多。所以并行之前先算账。如果你有8个核、64GB内存跑3个place/route进程就已经是上限了。如果想要跑更多要么换更大内存的机器要么把中间过程的write_checkpoint定期存盘一旦失败还能从最近的检查点续跑而不是全部重来。还有一个容易被忽略的问题多个实现策略并行时如果每个进程都打开同一个工程文件Vivado会在工程目录里创建锁文件或临时文件互相干扰。我经历过几次诡异的“Implementation run state变成ERROR但log里没有明显报错”的问题最后都指向并行访问冲突。用独立的副本工程或从DCP启动是最省心的办法。5. 仿真与验证加速多核的另一种打开方式5.1 编译阶段的并行化技巧Vivado自带的xsim在做仿真库编译时其实可以利用多核但幅度有限。比较有用的做法是将仿真代码拆分成较小的编译单元利用make的-j参数并行编译多个模块。比如在Linux下用xvhdl/xvlog逐个编译源文件然后并行进行 elaboration。实际中我更多是直接放弃在单个xsim进程里抠多核性能转而用Verilator这类支持--threads的仿真器去做需要高吞吐量的逻辑模型仿真。如果你项目里大量逻辑都是可综合RTLVerilator可以把模型分成多线程执行跑复杂case时能明显感受到CPU占用率上来。不过Verilator毕竟不是完全替代xsim它不能很好地支持Vivado原语和IP的仿真模型。因此我的策略是分层纯RTL可综合逻辑用Verilator做快速回归涉及IP、硬核或UHDM波形的部分继续用xsim。这样既压榨了CPU又保留了Xilinx生态的完整性。5.2 多进程并行回归测试设计仿真阶段多核加速的核心是并行回归。一个testbench配很多seed或testcase天然适合拆成多个进程。重点在于脚本层面要把每个进程隔离好。我常用一个很简单的回归框架所有case的seed、参数、覆盖率收集目标都写在一个配置文件里然后用xargs或者shell循环按CPU核心数控制并发。每个case有独立的工作目录编译后的可执行文件复制到该目录然后各自执行仿真并输出log。所有case结束后统一收集pass/fail结果。命令大概长这样cat case_list.txt | xargs -P 8 -I {} bash -c ./run_one_case.sh {}xargs -P 8就是让最多8个case同时跑。这个方案几乎没有学习成本但收益非常直接尤其是随机激励多的模块。唯一需要留意的是仿真seed不要重复否则多个进程会跑出完全相同的激励浪费并行资源。5.3 波形记录和仿真开销控制并行仿真最怕的不是CPU是IO。每个case如果都吐一个几百MB的波形文件8个case同时写盘SSD也扛不住。我的习惯是开发阶段只记录关键信号的波形甚至只在出错时重新跑一遍并打开全量波形。普通回归case只在通过/失败状态里记录几个关键断言。xsim里可以通过Tcl或编译参数控制波形记录范围。如果你把波形打开放在顶层的全局信号上那每个case的仿真时间会被拖到原来的2到3倍。更聪明的做法是默认不记录波形当case失败时再用同样的seed重跑并开启波形这样既保留了调试能力又避免了大部分case的波形IO损耗。并行仿真另一个要注意的是随机数种子。很多时候我们用$random或者SystemVerilog随机约束如果每个并行进程都从同一个种子开始那等于重复劳动。在脚本里给每个case分配不同的seed尽量互相错开覆盖率会更全面。6. 全流程综合优化从工程组织到长期复用6.1 任务编排与资源监控优化Vivado多核性能核心其实不是某一个参数而是能不能把各种资源串成一个流水线。我习惯用Makefile来做顶层编排把综合、实现、仿真拆成不同target通过-j参数让能并行的任务并行跑。比如综合阶段同时跑两个directive实现阶段同时跑两个strategy仿真阶段并行跑多个case。资源监控也很重要。推荐在跑大型任务时开一个终端用htop看CPU和内存另开一个终端用iostat和free看磁盘和内存压力。很多时候你以为瓶颈在CPU实际上内存已经顶到95%了。一旦发现swap大量使用立刻减少并行进程数量否则只是假忙。在服务器上跑任务时我还会用nice和ionice降低后台任务的调度优先级避免影响其他同事。Vivado进程本身对响应时间不敏感优先级低一点不会影响最终结果但能让整个系统更稳定。6.2 中间产物管理DCP、网表和缓存多核加速的另一个角度是减少重复劳动。综合完的DCP是宝贵资产不管是后续改约束还是换实现策略都不需要重新综合。所以每次综合通过后第一时间write_checkpoint -force post_synth.dcp保存。实现阶段也一样place完成后单独存一份post_place.dcp如果只是改布线directive可以直接从place后的DCP开始省掉重新place的时间。Vivado的工程缓存和.Xil目录也值得定期清理。并行任务多了以后工程目录里的临时文件会快速增长不仅占磁盘还会拖慢文件搜索。我在CI服务器上会定期清理超过一周的临时目录保证后续任务的文件IO不会莫名其妙变慢。有些团队会把并行综合、实现的中间DCP上传到NAS或对象存储里做长期保存。这样换人、换机器时不需要把整个工程拷来拷去直接拉取DCP继续跑后面步骤。我自己比较喜欢这个方式因为DCP比完整工程小很多拷贝速度快还能避免多人同时打开工程的锁冲突。6.3 一套可落地的优化检查表给出一份我每次新工程启动前都会过一遍的检查表工程是否放在SSD上临时目录是否足够大。License支持多少个并发Vivado实例。大IP是否已设置为OOC综合。综合脚本里是否设置general.maxThreads有没有试过不同directive。实现阶段是否准备了独立工作目录每个run从DCP启动。仿真case是否拆分为独立目录是否有统一的seed管理和波形策略。是否需要并行跑多个策略或seed内存预算是否够。这套检查表不需要一次性全做但如果你发现机器很猛、工程还是跑得慢回头对照一下多半能揪出被忽略的环节。7. 常见问题与避坑实录7.1 典型问题速查表下面是我自己踩过或者帮同事排查过的高频问题整理成表格方便速查。现象可能原因处理建议多开Vivado后很快License报错并发实例数超过License限制改用浮动License或串行排布任务综合/实现时内存突然暴涨并行进程太多内存超配减少并发数预留20%内存余量并行run之间log互相覆盖所有进程使用同一输出路径每个task独立目录路径用变量区分线程数调大后CPU反而跑不满算法瓶颈或IO等待检查磁盘IO和内存不一定继续加线程仿真并行后case结果异常多个进程共用log/波形文件或随机种子重复每个进程独立目录seed错开从DCP启动时缺少IP模型没有正确配置IP的仿真/综合产物先synth_ip或import_ip再读DCP7.2 我的几点实测心得经过这些优化后我手上最典型的中型工程综合从60多分钟降到了20到40分钟区间实现迭代从每轮3小时以上缩短到1到2小时200个回归case从串行大半天变成并行一个小时出头。但我想强调一个真实感受Vivado的多核优化本质上是“把资源用在正确的任务拆分上”而不是单纯堆线程。如果你只记住一句话我觉得是这样综合阶段靠OOC和增量减少重复工作实现阶段靠并行跑多个strategy摊薄等待仿真阶段靠多进程跑case做乘法。与其跟单个进程内的线程数较劲不如从任务编排的角度重新想一遍全流程。最后分享一个非常实用的小习惯。每次跑大型综合或实现之前我会先在终端里用time包住Vivado命令记录整个过程的真实耗时和CPU峰值。优化不是一锤子买卖改一个参数、加一个并行进程效果是好是坏都是要靠数据说话。把这些数据积累下来时间长了你对自己工程在不同硬件条件下的表现会非常有数。
返回列表