ARTICLE DETAIL

资讯详情

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

跨die FPGA时序收敛实战:从频繁违例到稳定收敛的完整流程

跨die FPGA时序收敛实战:从频繁违例到稳定收敛的完整流程 做FPGA开发也有几年了我越来越觉得评判一个设计有没有档次关键就看两件事一是能不能把功能跑通二是能不能把时序收住。而在这两件事里“跨die”一定是最能拉开差距的一个词。单die时代你只要把时钟约束写清楚让工具按默认策略跑一版大部分路径都能收回来但到了跨die FPGA之前那套“顺手”几乎全部失效。SLR之间、die之间的路径延迟、时钟偏斜、温度电压不一致都会让时序收敛变成一场硬仗。这篇文章不打算做概念科普而是直接把我实际项目中把一块多die FPGA从“频繁违例”打磨到“稳定收敛”的过程拆开给你看。不管你是做图像采集、高速接口还是信号处理只要芯片是多die/SLR架构这套思路基本都能复用。文中涉及的代码和命令以Xilinx环境为主Intel Quartus的操作逻辑类似关键区别我会单独说明。1. 先弄清楚跨die FPGA是怎么来的难点又在哪1.1 单颗die装不下了于是有了多die/SLR架构很多刚接触的人一听到“跨die”就懵我明明用的是同一块FPGA哪来的die其实所谓die就是芯片里那块真正做逻辑的裸片。传统单die FPGA所有LUT、FF、BRAM、DSP都集中在一块die上内部互联由统一的布线网络完成时序模型相对简单。但工艺和容量到了一定程度后一颗die的面积并不能无限做大良率、光罩成本、散热全是问题。于是厂商换了一条路把多颗die封装在一起通过interposer、硅桥或者封装基板上的高速互联把它们连起来对外看起来还是一颗FPGA内部其实是多个die协同工作。这个概念在Xilinx VU系列、VU系列里对应的是SLRSuper Logic Region在Intel的Stratix 10、Agilex上则是更彻底的chiplet设计。你可以把每个SLR想象成一个独立的“小FPGA”它们片内的布线资源是完整的但跨SLR的数据传递必须经过die-to-die接口走一段比片内布线长得多的物理路径。我在调试时遇到的最直观现象就是同样的逻辑放在同一个SLR内时序轻松收住一旦被工具拆到两个SLR路径延迟直接翻倍甚至更多。所以跨die设计的第一个认知要扭转过来你不能再把FPGA当成一整块“画布”而是要把它当成一个由2到4个独立区域组成的集合体让数据流尽量在区域内完成减少跨区搬运。这就直接决定了后续所有时序收敛策略的走向。1.2 跨die到底难在哪时序模型完全不同单die设计里setup timing主要看逻辑级数和布线长度只要约束写得好工具通常能自动优化链路。跨die设计里多了一个让所有工程师头疼的变量die-to-die接口延迟。这个延迟不仅数值大而且随温度、电压的变化也更大很难像片内布线那样精确预测。我举个例子假设系统时钟300MHz周期3.33ns。单die内一条典型路径触发器的Tcko约0.2ns组合逻辑延迟约1.5ns布线延迟约0.8ns目标触发器的Tsu约0.1ns那么slack大约还有0.73ns算是个健康的设计。但如果把路径放到跨die场景die-to-die接口延迟往往要增加1.5ns到3ns。就这一下slack直接变负时序不收敛就成了板上钉钉的事。更麻烦的是时钟。片内时钟网络比如BUFG能把整个die的clock skew控制在很小的范围内但跨die的时钟树要经过die-to-die互联skew和jitter都明显变大。如果你还习惯性把单个高频时钟扇出到所有SLR那你很可能同时踩中两个坑一是时钟skew过大导致同一条路径的launch和capture不在一个节奏上二是跨die数据路径上setup margin被接口延迟吃掉一大块。所以跨die设计的难点本质上不是某一个点出了问题而是数据路径、时钟路径、物理约束、工具策略四个方面都得重新适配。2. 跨die时序收敛的底层逻辑预算拆解与设计策略2.1 跨die路径的时序预算怎么拆在做任何布局和约束之前我强烈建议先把要用的路径按延时性质分类做一次时序预算。所谓预算就是把周期、触发器延迟、逻辑延迟、布线延迟、跨die接口延迟、时钟偏斜全部列出来看看每条关键路径还剩多少margin。这一步很多工程师觉得废话但跨die设计里预算做没做清楚直接决定你后面是修路径还是推倒重来。我常用的拆法是把路径分成三类片内路径寄存器到寄存器且在同一SLR内、跨die路径launch和capture位于不同SLR、IO路径到外部引脚/收发器。片内路径的预算和单die一致跨die路径则要额外预留die-to-die延迟而且这个预留值不要按理想值算最好按datasheet里的最大延迟再留20%到30%的余量。原因是die间传输对电压降和温度梯度更敏感实测值往往比静态时序分析报出来的更大。再往下拆你要评估每个功能模块放在哪个SLR更合理。比如图像传感器采集进来的数据流经过预处理、DMA搬运、DSP处理最后输出到显示接口。每一步涉及不同的IP和存储资源如果你不提前分配SLR工具会自动把相关逻辑拆到离资源最近的位置可能让关键数据通路横跨三个SLR一条流水线被拉出好几ns延迟。预算的意义就在这里它逼你在RTL阶段就意识到哪条路径不能跨die哪有跨die必须用异步FIFO或者同步打拍来兜底。2.2 从架构期就开始“设计收敛”而不是等跑完布局再救我见过太多跨die项目RTL阶段完全按单die思路写等综合布局布线出来时序一片红才开始加约束、插寄存器、改流水线。这种“事后补救”在单die时代还能勉强收住在跨die时代基本是无底洞。因为工具本身不会理解你的业务数据流它只能尽量把连接紧密的逻辑放到一起跨die路径一旦形成你删多少级流水都不一定救得回来。正确的做法是在架构设计期就把“SLR/Die划分”当成一个模块级设计决策来对待。具体来说每个大模块明确归属哪个die模块之间的接口尽量收敛到少数几条宽总线而不是散布大量细碎控制信号。宽总线走die间传输反而划算因为die-to-die接口按带宽而不是按信号数量计费逻辑上你还能打包成AXI-Stream这类流式接口用异步FIFO天然隔离时钟域和die边界。另外时钟方案也要在架构期定下来。我的经验是高频率时钟尽量在各自die内部生成也就是每个die里用自己的PLL/MMCM然后通过跨die的同步握手或者异步FIFO做数据交换而不是把单个高频时钟用BUFG拉到所有die。低频慢速控制信号可以跨die传但复位和模式配置这类信号必须做同步处理。这套做法本质上是把跨die问题从“物理路径收敛”转化为“跨时钟域设计”而后者的方法论已经很成熟了时序压力会小很多。提示跨die设计的核心不是“事后修”而是“从一开始就不要让关键路径乱跨die”。物理约束只能帮你微调架构决定了时序收敛的上限。3. 实战流程从die级布局到约束落地的完整操作3.1 工具链与工程准备我主要在Vivado里做这类项目下面以它为例讲整套流程。Intel Quartus的操作思路很接近只是命令名称不同我会在关键步骤标注差异。工程准备阶段有三件事必须做扎实。第一确认芯片的SLR数量和每个SLR包含的资源Xilinx在Device view里能看到每个SLR的坐标范围Vivado里也可以用get_slrs命令直接列出当前器件下所有SLR。第二把综合选项里的-flatten_hierarchy设置为rebuilt或none保留层次结构这样后续给模块施加物理约束时才能按模块名精确锁定。第三综合后马上用report_utilization -slr看一眼每个SLR的资源占用如果某个关键模块被打散了优先通过综合属性保持层次而不是等到布局后再切。项目里一般会有很多个时钟域跨die设计中时钟情况会比单die复杂得多。我建议在建工程时就把主时钟、派生时钟、异步时钟组的关系整理成一张表后面写SDC会轻松很多。比如数据通路用300MHz、DDR控制器用200MHz、串口慢速时钟用50MHz一上来就分清哪些时钟域之间需要同步、哪些是异步关系能避免后续乱加约束。3.2 第一步die级物理规划与Pblock约束跨die项目里我最先做的事情不是写时序约束而是做物理约束。说白了就是手工指定哪个模块放到哪个SLR。Vivado里最常用的手段是Pblock。你可以先创建Pblock把某个模块的cell全部加进去再把Pblock resize到目标SLR的范围内。示意命令如下# 创建Pblock并添加模块cell create_pblock pblock_axis add_cells_to_pblock [get_pblocks pblock_axis] [get_cells -hierarchical -filter {NAME ~ *axi_data_pipe*}] # 将Pblock锁定到SLR0对应区域具体坐标以目标芯片为准 resize_pblock [get_pblocks pblock_axis] -add {SLR_X0Y0}这里的SLR_X0Y0是Vivado里区域坐标的一部分实际器件不同坐标写法可能有差异。如果你不确定可以先打开Device视图勾选显示SLR信息手工框选区域后用resize_pblock的操作面板生成对应命令比手敲坐标更可靠。Pblock刚创建时工具可能不会把你的模块全部塞进指定区域这时需要检查report_pblock看有多少primitive落在区域外。区域锁定不是一锤子买卖我通常会迭代两三轮先按架构设计把大模块锁到die跑一次布局看跨die路径数量和时序情况再细调Pblock边界或者把部分子模块挪到另一个SLR。直接一次锁死反而会让布线资源失衡导致局部拥塞。3.3 第二步时钟与跨die接口约束物理规划完成后接下来是时序约束。跨die设计里第一个要处理的是异步时钟组。如果你按前面说的方案让不同SLR使用独立时钟源那么这些时钟之间必须用set_clock_groups声明异步否则工具会默认把它们当成可分析路径跨die的假路径和不必要的timeout约束会刷屏。# 异步时钟域隔离clk_a属于die0clk_b属于die1 set_clock_groups -asynchronous -group {clk_a} -group {clk_b} # 跨die接口同步器的max_delay约束 # cdc_ff[0]在die0cdc_ff[1]在die1路径上通过两级同步器 set_max_delay -from [get_cells -hier -filter {NAME ~ *sync_cdc_ff[0]}] \ -to [get_cells -hier -filter {NAME ~ *sync_cdc_ff[1]}] 20这里有个细节要注意跨die的异步FIFO或同步器路径通常不需要严格的单周期时序收敛它们的重点是保证数据稳定窗口和数据传输的正确性。所以用set_max_delay而不是让工具把同步器路径也按普通寄存器到寄存器路径去死磕。你可以给这类路径一个足够宽裕的约束值比如10ns或20ns原理是让它比die间延迟大就行这样既不会过度约束又不会完全放飞。除了异步时钟组还要检查有没有跨die的同频时钟路径。如果存在同频但相位不同的时钟跨die传输必须核对datasheet里的die间skew值必要时设置set_clock_uncertainty把比片内更悲观的clock uncertainty写进去。否则静态时序分析会和实测差很远我遇到过看起来有正slack上位机运行一会儿就偶发错误的现象最后发现就是clock uncertainty没加够。3.4 第三步跨die路径的时序修整与迭代物理约束和时钟约束都做完之后跑布局布线看时序报告。跨die设计里第一次运行时序报告全绿是极小概率事件多数情况是少数几条关键路径红掉。这时候不要急着改逻辑先打开时序报告里违例路径的详细信息检查它真正的delay构成。如果红掉的路径是片内路径说明资源放置得不够集中优先调整Pblock边界或把相关逻辑合并到同一SLR。如果红掉的路径是跨die路径无非三种情况跨die接口延迟超出预算、时序约束太严、或者跨die路径数量太多导致接口拥堵。接口拥堵的典型特征是FIFO两端逻辑都在自己die里但违例路径都集中在同一组die-to-die接口上。这时要做的不是去修剪某条路径而是把数据搬移分散到多个die接口或者把部分处理挪到接收端die减少单向传输数据量。工具层面Vivado里可以给Pblock设置物理综合相关的属性比如set_property STEPS.SYNTH_DESIGN.ARGS.DIRECTIVE AlternateRoutability [current_run]这类综合策略以及布局阶段的directive切换。跨die项目我一般会把综合和布局布线的directive都调成偏物理性的选项比如Explore、AggressiveExplore跑出来的结果经常比默认策略好很多。但代价是运行时间明显变长适合在项目后期做最终收敛不要每次迭代都用蛮力。还有一个实用技巧在布局完成后、布线之前先跑一遍report_timing_summary -path_type full看跨die路径的预布局估计。Vivado的早期时序评估虽然不准但能快速暴露物理划分的问题。我通常用这个报告决定是继续细化物理约束还是直接进入布线。布线后如果仍然有人不过的跨die路径再逐条查看是否存在可优化逻辑级数的地方。4. 跨die设计常见问题与排查技巧实录4.1 接口路径不长却严重违例问题出在哪有一种情况很迷惑时序报告里某条跨die路径的逻辑级数只有三四级看上去完全健康但slack就是负得很厉害。排查这类问题我第一件事是看路径的Route Delay。单die设计里Route Delay通常占比较小但跨die接口路径的Route Delay可能是片内路径的好几倍因为数据要先走到die边缘经过接口再从另一个die的边缘走到capture寄存器。我在一个项目里遇到过一次A die到B die的路径只有两级寄存器但report_timing显示整条路径延迟超过7ns明显不合理。后来打开Device视图高亮这条路径发现数据从A die底部绕了大半个die才到接口。原因是Pblock没有把相关模块放到靠近die-to-die接口的位置。解决方式是调整Pblock坐标让源模块和目标模块都贴近接口区域路径延迟立刻降了将近一半。所以不要只看逻辑级数要看物理位置。另外有的芯片die-to-die接口数量有限如果设计里大量信号直接跨die接口区域的布线资源会被占满产生严重拥塞。我在排查时发现一旦接口区域的congestion超过一定阈值不管哪条路径只要经过附近就会疯狂绕线。此时与其修单条路径不如优化数据通路的打包方式比如把32比特信号打包成AXI-Stream总线配合异步FIFO整组传输接口占用会小很多。4.2 复位亚稳态跨die最容易被忽视的坑如果你在热搜里看到过“fpga复位信号亚稳态”这类词说明这确实是高频痛点。跨die架构下复位信号一旦直接从一块die的复位管理单元拉线到另一块die的触发器大概率出问题。die间路径长、skew大复位释放时刻在所有寄存器上根本不是同时到达有些寄存器已经释放开始工作有些还没有整个状态机直接跑飞。我踩过这个坑之后总结了一套固定打法。复位信号进入每个die时必须在die内部先做“异步复位、同步释放”用本地时钟打两拍再作为这个die所有逻辑的复位。跨die的复位传递只保留原始的异步复位断言释放统一交给各die自己处理。这样复位释放的时序在每个die内部是可控的跨die的问题就被隔离掉了。// 在die内部处理复位释放 reg [1:0] rst_sync; always (posedge clk_local or posedge rst_raw_async) begin if (rst_raw_async) rst_sync 2b0; else rst_sync {rst_sync[0], 1b1}; end assign rst_local rst_sync[1];这种做法的代价是复位释放延迟增加了两个本地时钟周期但对于绝大多数系统完全不是问题。如果你有跨die的CDC信号与复位同时出现更要警惕一前一后的时序怪象建议将跨die状态机的所有状态位都用格雷码或增加握手协议保证不会因为复位路径延迟产生非法状态。4.3 温度电压变化与多die收敛的不确定性单die芯片内部的温度相对均匀跨die封装里的多个die之间温度可能相差不少尤其当某个die专跑DSP、另一个die主要做IO时。温度差大会让die间接口延迟出现明显漂移这也是我前面强调要在时序预算里留余量的原因。做signoff的时候不能只看典型条件下的时序报告一定要跑多个corner尤其是最慢corner和最差电压组合。Intel Quartus里有专门的跨die时延分析报告Xilinx Vivado在时序分析里会展示路径经过的SLR和die间跳转。我在项目后期养成了一个习惯每次温度从0度到85度做高低温测试时都把关键时钟频率和收发器误码率记录下来对比常温数据。如果误码率或功能错误与温度强相关十有八九是跨die路径的时序余量不足而不是逻辑功能问题。这时处理手段主要有两个一是继续扩大跨die路径的max_delay约束给同步逻辑更多裕量二是降低跨die接口数据率比如在FIFO写侧增加半满/半空阈值让突发数据不会密集冲击接口。很多工程师只会在RTL里硬调其实时序收敛是一个软硬协同的活数据流整形和时序约束双管齐下才最有效。4.4 从工具报表里精确定位跨die路径跨die项目里工程师要学会的第一件事不是写约束而是看报表。Vivado的时序报告默认显示路径延迟占用的明细里面有Logic Delay、Route Delay、Clock Skew等数值。如果你看到Route Delay比Logic Delay大很多尤其是路径起点和终点不在同一个SLR时先想到跨die。更直接的办法是在Report Timing Summary里右键路径选择Highlight in Schematic/Device看布局窗口里这条路径到底跨过了哪些SLR。我常用的排查流程是先report_clock_interaction看跨时钟域路径数量再report_timing_summary -max_paths 100排出所有违例路径按起点所在的Pblock分组。如果违例路径集中出现在两个die的边界区域那就明确是die-to-die布局问题优先做物理调整。如果违例路径分散在各个SLR内部反而说明是整体逻辑延迟过长考虑插流水或优化组合逻辑深度。注意Vivado的默认时序报告里跨die路径未必会直接显示“crossed SLR”字样。你需要在路径报告里看Source和Destination的Site坐标如果它们的SLR编号不同就能确认它是一条跨die路径。养成这个习惯能省下大量排查时间。5. 和高速IO、图像处理等场景的衔接经验5.1 高速接口场景下的die间数据搬运跨die FPGA大量用于高速接口场景比如LVDS接收、MIPI、SRIO、PCIe等等。芯片面积一大既要做协议栈又要做数据缓存还要和主控逻辑交互跨die几乎无法避免。我的经验是所有高速数据进入FPGA后第一时间应该落到靠近收发器所在die的FIFO/BRAM里完成数据格式转换或位宽拼接然后以burst方式搬运到另一个die的处理区块。这样做的核心原因是收发器的硬核资源通常固定在特定die上如果你让数据一进来就横穿多个die去做对齐、做校验那跨die路径会遍布整个设计时序根本没法收敛。我在设计里会指定“采集侧die”和“处理侧die”中间的数据通路用AXI-Stream 异步FIFO总线位宽尽量大一点。例如300MHz时钟下用64bit甚至128bit的FIFO总线把数据突发压低跨die接口压力会小非常多。如果你做MIPI或者LVDS接收还要特别注意字节对齐、通道对齐逻辑尽量放在同一个die内完成。这类逻辑需要频繁比较各个通道的延迟状态跨die做的话通道间skew会变得非常难以控制。我看到不少做图像采集的同行在跨die项目上卡住最后发现根本不是算法问题而是把对齐逻辑打散到了不同die导致通道间差距超过协议容忍范围。5.2 图像处理链路中的跨die布局原则图像处理相关的关键词在热搜里扎堆出现像“fpga图像采集”“fpga双线性插值”“fpga实现rgb转tmds”等等。图像处理链路的典型特征是数据流连续、吞吐量大、算法模块多。跨die项目里最容易犯的错是把整条图像流水线按模块顺序一个模块占一个dieA做灰度、B做滤波、C做缩放数据从左到右横穿整个芯片结果每级之间都跨die整条链路时序红成一片。更合理的做法是把图像处理链路按照“存储密集”和“逻辑密集”来划分。存储密集的部分如行缓存、帧缓存、DMA写DDR放在靠近BRAM/DDR控制器资源的die逻辑密集的部分如滤波、缩放、边缘检测放在另一个die然后让整块逻辑在一个die内跑完算法只把最终结果送出去。这样跨die路径只在链路的边界出现数量少且可控。如果你非要把一些算法模块放在不同die那就必须在它们之间插入足够深度的FIFO并且让每个模块内部尽量无反馈回路。图像算法里常见的回环结构比如迭代滤波、递归滤波特别不适合跨die因为反馈路径跨die会形成很长的循环收敛难上加难。我通常的做法是把这类算法用流水线展开或者把反馈状态留在本die内的BRAM跨die只传加速后的数据流。这套原则做透了图像处理链路在跨die器件上依然能跑得很稳。6. 一点我自己的体会最后说点系统性的东西。跨die设计做了几个项目之后我心里的技术优先级已经非常明确架构设计时先画die级数据流图RTL设计时把跨die接口全部做成流式接口加同步逻辑物理约束明确锁模块时序约束里优先隔离异步时钟域和设置跨die max_delay。只要这四层做好了后面的工具迭代就只是微调而不是失控的拉锯战。很多工程师一开始会把跨die当成一个纯工具问题觉得“跑个布局、加几条约束就完事”但真正踩过几次坑之后你会发现决定收敛的从来不是某一条命令而是你愿不愿意在设计早期就把die边界当成第一等公民来看。时序分析报表只是最后的成绩单真正的功课全在前面。希望这篇从概念到实战的总结能让你少走几步弯路。
返回列表