
做FPGA开发的朋友十有八九都被VIVADO的时序约束折磨过。尤其是刚接触RGMII、ADC这类接口时一打开VIVADO的时序报告满眼红色违例最后只能靠试——随便把set_input_delay改大改小跑到不报错为止。这种玄学调参我也经历过直到把set_input_delay的原理彻底想明白后才真正能做到算出来什么值就填什么值填完就收敛。这期就集中把VIVADO里Input Delayset_input_delay的来龙去脉讲透。从约束原理到系统同步、源同步两大模型再到RGMII接口这种具体应用场景手把手带你把这个命令吃透。1. 为什么Input Delay是绝大多数时序问题的源头先不看命令看一条真实的信号路径。假设你有一颗ADC芯片通过一组并行数据线连到FPGA。数据从ADC内部产生经过ADC的输出寄存器、PCB走线最终到达FPGA的输入引脚然后还要经过FPGA内部的布线才能到达IOB里触发器的D端。这一路上有四个延迟你不能忽略ADC内部的时钟到输出延迟Tco、PCB走线的延迟Tdly、FPGA内部从引脚到触发器的走线延迟Tnet、以及FPGA触发器本身的建立/保持时间要求Tsu/Th。问题来了VIVADO做布局布线时它知道自己内部的Tnet能走多少也清楚IOB触发器的Tsu/Th是多少这些在工艺库里有但它完全不知道外面的Tco和Tdly是多少。如果没有set_input_delayVIVADO只能用最保守的假设认为数据可能在任何时刻到达或者干脆不分析这条路径。最终结果要么是大量路径显示unconstrained未约束要么是布局布线根本没法优化白白浪费FPGA的性能。1.1 VIVADO到底在等什么信息set_input_delay本质上是告诉VIVADO外部数据到达FPGA引脚时相对于参考时钟的边沿到底早了多久、晚了多久。这句话要拆成两半来理解。第一它描述的是发射端的时间关系。比如ADC的时钟是125MHzADC在时钟上升沿之后Tco的时间点把数据推出来经过PCB走线数据到达FPGA引脚。那相对于125MHz时钟的上升沿数据最早什么时候到最晚什么时候到这两个时间就是-set_input_delay -min和-set_input_delay -max。第二VIVADO拿到这个时间窗口后会拿它去和FPGA内部的采样时钟约束做比较。假设FPGA内部也用一个125MHz时钟去采这个数据VIVADO会自动计算数据到达引脚的时间 FPGA内部布线延迟能不能满足IOB触发器的建立时间要求相对于采样时钟沿。能就认为这条路径时序收敛不能就报违例。所以核心理解是set_input_delay描述的是数据到达引脚的时间窗口而VIVADO把这段窗口当作外部逻辑的一部分放在整个时序路径的起点。后面你再去查report_timing_summary会发现所有输入路径的起点都是外部延迟终点是IOB触发器这就是set_input_delay参与分析的方式。1.2 很多新手分不清的两个概念发射时钟与采样时钟这是绕不开的第一个坑。set_input_delay命令里有一个 -clock选项它指定的是发射时钟——也就是上游器件ADC、PHY芯片用来输出数据的那个时钟而不是FPGA内部用来采样的时钟。举个反面例子。你用MMCM/PLL生成了一个125MHz时钟来采ADC数据就想当然地在set_input_delay里填这个PLL输出时钟的名字结果VIVADO报错或者时序分析结果完全不对。原因就是ADC根本不知道你FPGA内部PLL的存在它只知道自己的参考时钟沿。你要让VIVADO分析从ADC到FPGA的路径必须先告诉VIVADOADC发射数据时参考的是哪个时钟沿。如果ADC的时钟就是从FPGA送过去的那你应该用FPGA输出给ADC的那个时钟通常约束在输出引脚上作为参考如果是PHY芯片随路送来的RXC时钟就要用RXC这个时钟要么在RXC引脚上创建时钟约束要么创建一个虚拟时钟来作为参考。搞清楚发射时钟和采样时钟的区别后set_input_delay的约束思路就清晰了。1.3 两种时钟模式决定了两套计算方法外部接口按时钟结构分成两大类系统同步和源同步。这两类的set_input_delay计算方式完全不同网上一搜一大把公式但很少人讲清楚公式到底怎么来的。我直接说结论再讲为什么。系统同步接口发射端和接收端共享同一个时钟源时钟通过PCB分叉分别送到两个芯片。这时算input delay要看三个量上游器件的Tco、PCB走线延迟、以及时钟到达FPGA相对于到达上游器件的偏斜。源同步接口发射端把数据和时钟一起送过来比如RGMII的RXC和RXD时钟和数据走的是平行的PCB线。这时时钟到达FPGA的时间基本和数据到达时间是同步的你要关心的主要是时钟与数据之间的相对相位关系。center-aligned中心对齐和edge-aligned边沿对齐两种源同步接口计算方式也不一样。后面两章分别把这两个模型拆开讲配上具体数字你照着算就行。2. set_input_delay的约束原理与两大经典模型2.1 命令格式与关键选项拆解set_input_delay的命令格式如下set_input_delay -clock clock_name \ -max delay_value \ [get_ports port_name] set_input_delay -clock clock_name \ -min delay_value \ [get_ports port_name]展开说明一下各选项的作用。-clock指定发射时钟的名字必须是VIVADO已经认识的一个时钟。可能是你在某个引脚上创建的时钟create_clock也可能是PLL/MMCM的输出时钟还可能是虚拟时钟create_clock -name virtual_clk -period 8不带get_ports。-max指定数据路径的最大延迟也就是数据最晚什么时候到达引脚。在时序报告里它直接影响建立时间setup检查。-min指定数据路径的最小延迟也就是数据最早什么时候到达引脚。它直接影响保持时间hold检查。-clock_fall表示约束的数据是相对时钟下降沿发射的。对于DDR接口上下沿都有数据、RGMII这类双沿接口必须同时约束上升沿和下降沿否则另一沿的数据会被漏掉。-add_delay用于在同一个端口上同时追加上升沿和下降沿的约束。没有这个选项时第二次对同一个端口设置set_input_delay会覆盖第一次的值。2.2 模型一系统同步共同时钟输入延迟计算系统同步最常见于并行的ADC/DAC接口、SRAM接口等。上游器件和FPGA共享同一个时钟源时钟从振荡器出来分两路一路到上游器件一路到FPGA。这种情况下数据到达FPGA引脚的时间可以写成set_input_delay -max Tco_max Tdly_max - Tclk_delay set_input_delay -min Tco_min Tdly_min - Tclk_delay每个量的含义和取法如下Tco上游器件从时钟沿到数据输出的延迟。查器件数据手册会给出min和max两个值。Tdly数据线在PCB上的走线延迟。一般按6 mil/ps约150 ps/inch估算当然最好用PCB设计工具提取。Tclk_delay时钟到达FPGA的时间减去时钟到达上游器件的时间。如果时钟是先到上游器件再到FPGA数据方向的同方向这个量是正的反过来就是负的。PCB上可以近似用时钟线长度的差值除以传播速度算出来。举个实例。某ADC芯片Tco_min 2nsTco_max 5ns数据线上PCB走线等长约0.5ns两个方向的走线延迟合并后约1ns时钟线走线比数据线短时钟到达FPGA时间相对上游器件早0.3ns取正0.3ns作为Tclk_delay。代入公式set_input_delay -max 5 1 - 0.3 5.7ns set_input_delay -min 2 1 - 0.3 2.7ns算出来之后把这两个值约束上去VIVADO就会认为数据最早在时钟沿后2.7ns到达引脚最晚在5.7ns到达。接下来它会自动去检查假设FPGA内部用同一个125MHz时钟采样内部布线延迟加上5.7ns的数据到达时间能不能满足建立时间同理2.7ns的最早到达时间会不会和上一拍的数据发生保持冲突2.3 模型二源同步随路时钟输入延迟计算源同步接口里发射端把数据和时钟一起送过来。典型的有RGMII、DDR、LVDS接口。时钟和数据是一起走的关系所以系统同步里那个时钟偏斜Tclk_delay在这里要换成数据和时钟之间的相对偏斜Tskew。计算式变成set_input_delay -max Tco_max Tdly_data_max - Tdly_clk_min Tskew set_input_delay -min Tco_min Tdly_data_min - Tdly_clk_max - Tskew看着吓人但实际工程中没那么复杂。大多数源同步接口特别RGMII讲究的是数据和时钟在PCB上等长走线也就是说Tdly_data和Tdly_clk基本相等真正起作用的是接口协议定义的相位关系。所以工程界更常用的计算方式是按接口协议来。比如RGMII在1Gbps模式下时钟周期8ns数据在时钟上升沿和下降沿都被采样数据跳变沿应该位于时钟沿的中间位置也就是数据相对于时钟边沿的建立时间和保持时间都是4ns这是理论值。这时直接把set_input_delay -max设成4.2ns、-min设成3.8ns留一点余量就成了大多数工程默认的起点。源同步接口最关键的概念是对齐方式是center-aligned中心对齐还是edge-aligned边沿对齐。RGMII是中心对齐数据跳变沿位于时钟沿两侧离时钟边沿各约半个周期SDR/DDR同步接口很多是边沿对齐数据在时钟边沿附近跳变建立/保持窗口很小。边沿对齐接口算input delay时-max和-min之间的差值会很接近0甚至出现负数处理起来更谨慎。具体怎么算第三章用RGMII完整走一遍。2.4 max/min与建立/保持时间之间的映射关系理解-max/hold的映射关系是看懂时序报告的前提。一句话总结-max决定setup-min决定hold。展开说。VIVADO检查setup时找的是这一拍数据最晚到达的时间所以它把input delay的最大值-max加上内部路径延迟去和采样时钟沿比看还能不能留下足够的建立时间裕量。检查hold时找的是这一拍数据最早到达的时间它把input delay的最小值-min加上内部路径延迟去比上一拍的采样边沿看会不会因为数据到达太早直接把上一拍的数据冲掉。所以如果你只设了-max忘掉-minVIVADO会默认把-min当成-max的负值或其他值hold检查就会完全失真时序报告里出现大量假违例或漏报违例。同理只设-min忘了-maxsetup检查会严重乐观。这两个值必须成对出现。再补充一个常见误区hold违例不能通过降低时钟频率来修复因为hold检查比较的是同一时钟沿附近的数据相对关系和频率无关。只有真正约束准了input delay的-min才能把hold检查拉回正轨。我见过有人在hold违例时疯狂调约束的-min把本来正确的0.5ns改成-2ns结果setup、hold全乱套。正确的做法是先算清楚外部延迟的物理上限和下限再决定约束数值。3. 三种核心应用场景实操拆解3.1 场景一普通并行总线ADC/GPIO/LCD并行ADC是最经典的input delay场景。芯片手册一般会给出Tco范围PCB走线了然于胸照着系统同步模型直接算。假设ADC时钟125MHz由FPGA提供时钟经过PCB走线到ADC数据再回来。ADC Tco_min1.5nsTco_max6ns。数据走线约1.2ns等效延迟时钟走线略长使得时钟到FPGA的偏斜为0.2ns意味着FPGA看到的数据会更早一点因为时钟比数据晚到。计算set_input_delay -max 6 1.2 - 0.2 7.0ns set_input_delay -min 1.5 1.2 - 0.2 2.5ns实际操作时可以写一个循环批量约束set max_delay 7.0 set min_delay 2.5 foreach pin {adc_d[0] adc_d[1] adc_d[2] adc_d[3] adc_d[4] adc_d[5] adc_d[6] adc_d[7]} { set_input_delay -clock clk_125m -max $max_delay [get_ports $pin] set_input_delay -clock clk_125m -min $min_delay [get_ports $pin] }这里有个很容易踩的坑ADC的数据总线往往还有DVALID这类控制信号控制信号和数据的时序特性通常不完全一样。如果手册上控制信号的Tco和数据总线不同必须在约束里区分开不能图省事套同一个值。控制信号采错一拍整个系统行为直接乱掉但时序报告多半是干净的——因为约束本身就没建对。LCD接口类似。LCD控制器作为接收端时FPGA是发射端要用set_output_delay但很多LCD模组内部还带一个状态回读引脚比如忙标志这个回读信号对FPGA来说是输入同样需要set_input_delay。别只约束了输出方向就以为接口时序做完了。3.2 场景二DDR接口SDR/DDR双沿采样DDR接口的核心特征是时钟上下沿都传数据。对应的一条数据引脚上同时存在上升沿发射的数据包和下降沿发射的数据包。约束时必须在同一个端口上同时设置上升沿和下降沿两组input delayset_input_delay -clock ddr_clk -max 2.8 [get_ports ddr_dq[0]] set_input_delay -clock ddr_clk -min 1.2 [get_ports ddr_dq[0]] set_input_delay -clock ddr_clk -clock_fall -add_delay -max 2.8 [get_ports ddr_dq[0]] set_input_delay -clock ddr_clk -clock_fall -add_delay -min 1.2 [get_ports ddr_dq[0]]注意这个命令里没有-add_delay时后面的-clock_fall约束会直接覆盖前面的。传统上很多人会写两句完整的约束一句上升沿一句下降沿每句后面都带-add_delay。在实践中我发现更稳妥的写法是四条命令都写全并且每次都用-add_delay这样即使删掉中间某条也不会无意中覆盖掉另一沿的约束。还有一个细节DDR接口的数据和DQS数据选通之间是源同步关系DQS到达FPGA后通常会先进IOB里的延迟链或MMCM做相位调整再作为采样时钟。在约束层面数据参考的是DQS而不是系统主时钟。所以创建时钟时要对DQS引脚单独建一个时钟或者用虚拟时钟代表DQS的相位。否则VIVADO内部分析时找不到正确的采样沿hold和setup检查结果都会失真。3.3 场景三RGMII千兆以太网接口RGMII是源同步接口的典型代表也是很多工程师第一次接触set_input_delay的原因。RGMII在1Gbps模式下TXC或RXC是125MHz数据线TXD/RXD和TX_CTL/RX_CTL都采用DDR方式上下沿同时传数据。因为数据线本来就只有4bit加一个控制信号靠双沿才能做到每时钟周期8bit换算成125MHz x 8 1Gbps。RGMII的时序定义在标准里很明确数据跳变沿位于时钟沿的正中间也就是说在1Gbps的8ns周期里相对于每个时钟沿数据的建立时间和保持时间都是约4ns。PHY芯片手册里通常会给一个总的Tskew范围比如总Tskew典型值500ps那你就可以把-max设为4.2ns、-min设为3.8ns把Tskew余量吃进约束里。set_input_delay -clock rgmii_rxc -max 4.2 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}] set_input_delay -clock rgmii_rxc -min 3.8 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}] set_input_delay -clock rgmii_rxc -clock_fall -add_delay -max 4.2 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}] set_input_delay -clock rgmii_rxc -clock_fall -add_delay -min 3.8 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}]这里出现了一个关键问题rgmii_rxc这个时钟你是直接在RXC引脚上创建的还是建了一个虚拟时钟两种做法对RGMII的实际效果有微妙差别也是网上讨论最多的话题之一。我一并放到第四章单独展开。4. RGMII接口时序约束完整实战4.1 RGMII时序特性中心对齐到底指什么RGMII的中心对齐指的是数据线的跳变沿对准时钟的两个边沿的中间。用大白话说时钟上升沿前后各2ns处1Gbps模式下是数据的跳变点所以在时钟上升沿采样时数据距离上一次跳变已经有4ns距离下一次跳变还有4ns建立和保持都宽裕。图在心里画一下就行横轴是时间时钟上升沿在T0和T8ns下降沿在T4ns。数据第一次跳变在T-2ns左移2ns对应本拍数据的开始第二次跳变在T2ns。所以上升沿T0采样时数据在4ns前已经稳定且要到2ns后才变化建立时间和保持时间分别约4ns和2ns相对上升沿。相对下降沿也是对称的。由此可以直接得出结论在1Gbps模式下约束值时序窗口应该是-max约等于4ns含余量取4.2ns-min约等于4ns含余量取3.8ns两个值很接近。在100Mbps模式下时钟周期80ns中心对齐同样成立所以-max和-min同样可以取40ns左右但实际PHY和FPGA可能工作在10M/100M/1000M自适应模式约束一般按最高速率125MHz来设低速模式下时序自然更宽松。RGMII还有一个麻烦发送方向FPGA给PHY的TXC/TXD和接收方向PHY给FPGA的RXC/RXD是对称但方向相反。接收方向的处理方法上面已经写了发送方向要用set_output_delay参考时钟是FPGA内部产生的TXC设置逻辑和input delay基本对称。很多人做完input delay忘记output delay结果上板测试通过率不稳定其实就是发送路径的时序没约束好。4.2 虚拟时钟与真实时钟怎么选对RGMII接收方向参考时钟RXC来自PHY芯片是PHY随数据一起送出来的。它有两种约束方式。第一种直接在RXC引脚上创建真实时钟create_clock -name rgmii_rxc -period 8.0 [get_ports RXC]这种方式下RXC本身成为FPGA内部的一个真实时钟如果你后续用BUFG IDDR来采样RXD那么RXC以及它的延迟、抖动都会被纳入时序分析。好处是贴近物理实际情况缺点是RXC是从PHY送来的普通时钟信号它经过引脚IBUF、BUFG之后到达内部触发器的时钟端这个时钟路径的质量skew、jitter会被VIVADO严格分析如果RXC没有接到专用时钟引脚MRCC/SRCC时序报告里会出现很大的时钟插入延迟甚至无法满足。第二种创建虚拟时钟create_clock -name rgmii_rxc_virt -period 8.0注意没有get_ports所以它是凭空存在的一个理想时钟只用来给set_input_delay当参考不连接任何实际引脚。这种方式的好处是set_input_delay只需要告诉VIVADO数据相对于一个125MHz的理想时钟在这个时间窗口到达而RXC的实际物理路径对分析不产生影响。如果你FPGA内部其实是把一个本地PLL产生的125MHz时钟当作采样时钟那用虚拟时钟更合理因为它代表了PHY内部发射时钟的理想模型不需要把PHY的RXC引脚路径牵连进来。我个人的建议是如果RXC确定接在FPGA的MRCC/SRCC引脚上而且你的设计里就是用RXC经BUFG作为采样时钟或者作为IDDR的时钟那直接用真实时钟约束分析结果最贴近实际。如果RXC只是接到普通IO或者你打算用本地PLL时钟来采样甚至加IDELAY做相移那虚拟时钟更干净也更容易收敛。可以肯定的一点是无论哪种方式set_input_delay里的发射时钟名字必须是上面定义的rgmii_rxc_virt或rgmii_rxc两者保持一致不能一会儿用这个一会儿用那个。4.3 完整Tcl约束脚本示例一个典型的RGMII RX约束片段长这样以虚拟时钟方案为例# Step 1: 创建虚拟时钟代表PHY内部发射RXD的125MHz时钟 create_clock -name rgmii_rxc_virt -period 8.0 # Step 2: 约束RGMII接收数据RXD与RX_CTL set_input_delay -clock rgmii_rxc_virt -max 4.2 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}] set_input_delay -clock rgmii_rxc_virt -min 3.8 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}] set_input_delay -clock rgmii_rxc_virt -clock_fall -add_delay -max 4.2 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}] set_input_delay -clock rgmii_rxc_virt -clock_fall -add_delay -min 3.8 [get_ports {rxd[0] rxd[1] rxd[2] rxd[3] rx_ctl}]如果你用的是真实时钟方案把rgmii_rxc_virt替换成你已经创建好的RXC引脚时钟名即可。有一点必须牢记以上4.2/3.8是常用起点值不是万能值。不同PHY芯片RTL8211、88E1512、KSZ9031等的Tco、Tskew参数都不一样板级走线也有差异严谨的做法是查PHY手册里的输出时序参数按源同步公式返回去算。手册给的是Tco和Tskew时可以这么算Tcycle 8ns1Gbps 数据相对时钟中心的偏斜 Tskew set_input_delay -max Tcycle/2 Tskew_max set_input_delay -min Tcycle/2 - Tskew_max比如Tskew_max 300ps那-max就是4.3ns-min就是3.7ns。这样填出来的值才是真正符合你这颗PHY的约束。5. 常见问题与排查技巧实录5.1 常见错误对照表把这几年带新人时最容易遇到的问题整理成一张速查表方便大家定位。现象可能原因排查思路时序报告大量输入路径显示unconstrainedget_ports端口名写错、时钟名写错、约束没生效用report_timing -from [get_ports xxx]查看路径是否出现在报告中setup违例严重但实际单板工作正常-max设得过大把不可能的真实延迟也算进去了检查PCB走线长度估算是否偏大、Tco是否取错hold违例反复出现-min设得过小甚至负数或采样时钟有交叉时钟域未设false_path重新核实数据最早到达时间检查跨时钟域路径RGMII上板偶发丢包约束值没把Tskew余量吃进去或PHY与FPGA之间走线不等长用set_input_delay再留0.2~0.5ns余量检查PCB走线DDR接口约束了但总有一半路径没法收敛忘了加-clock_fall或-add_delay导致下降沿数据路径缺失确认每个端口都有上升沿和下降沿两组约束5.2 从VIVADO时序报告定位约束错误VIVADO的时序报告入口是report_timing_summary打开后重点看Input相关的路径。一个实用的操作是直接跑report_timing -from [get_ports rxd[0]] -to [get_pins {logic_slv0/inst/.../D}] -setup -max_paths 20观察报告里的Input Delay Type显示的是max还是min如果显示max但路径类型却是hold那多半是约束方向搞反了。还有一个高频踩坑点VIVADO在报告里会把外部input delay值和内部布线延迟合并显示很多人看到一个很大的data path delay以为是自己FPGA内部布线太差其实里面包含了你设置的4.2ns外部延迟这是正常的。判断内部布线质量要看Source Latency和Destination Clock Latency的差而不是看总的data path延迟。另外设了set_input_delay之后可以用report_timing -input_pins来检查每个输入引脚的约束状态。如果某个引脚没有出现在列表里说明约束没覆盖到最常见的原因是port名字拼写不一致比如代码里的端口名是rxd[3]你写成rxd_3VIVADO不会报错只是静默忽略。用get_ports配合通配符能降低这类错误get_ports rxd*约束前先跑一下这条命令确认返回的端口列表和你预期的完全一致再执行set_input_delay能省去很多无意义的排查时间。5.3 我踩过的几个坑第一个坑为了保险把-max和-min都往大了设。曾经调一个ADC接口时序收敛不了。我心想数据晚点总没错把-max从6.0ns改到8.0ns结果setup违例更多了。后来想明白原因-max越大说明数据到达越晚FPGA内部留给建立时间的窗口就越小。-max不是用来放宽要求的它是精确描述数据实际最晚到达时间的物理量你改大它不会让时序变好只会让约束失真。正确的修复手段是缩短FPGA内部走线或调整采样时钟相位。第二个坑RGMII的RX约束用的参考时钟是本地PLL的输出结果PHY和FPGA之间明明工作得好好的VIVADO却报告大量负slack。后面发现PLL输出时钟和外部RXC在频率上相同但相位关系没有被约束VIVADO会随便选一个最差的相位来分析。改用虚拟时钟后问题消失。这就是为什么前面花那么长篇幅讨论虚拟时钟和真实时钟的选择——选错了时序报告就是一堆意义不明的红字。第三个坑批量约束时忘记给控制信号单独设约束。RGMII的RX_CTL和TXD一样走DDR但它的Tco和RXD可能不同。PHY数据手册里通常会给RX_CTL和RXD各自的Tco如果直接照抄RXD的值偶发错误帧很难排查。我现在的习惯是把数据和控制信号分开约束宁可多写几行也不图省事一把梭。6. 把这些串起来的个人体会回看set_input_delay这一个命令实际上是把FPGA外部的时序世界翻译成VIVADO能理解的语言。外部Tco、PCB走线、时钟偏斜、接口协议的对齐方式最终都汇成两个数字一个最大延迟和一个最小延迟。约束填对了VIVADO内部的布局布线优化就有明确目标时序报告也干净约束填错了要么红字一片要么表面全绿、上板概率性出错——后者更可怕因为查起来更费劲。这里再分享一个接地气的经验如果拿不准某个值怎么填与其在工程里反复试不如先把外部器件的时序模型和数据手册完整读一遍在纸上画一条时间轴把时钟沿、数据跳变沿、采样窗口都标上所有值自然就出来了。我在接手过的每一个接口类项目里都会在项目文档里留一张这样的手绘时序图调试时省了无数回头看的功夫。最后补充一点扩展思路set_input_delay只是input方向的基础真正复杂的场景往往还需要配合set_output_delay、set_clock_groups、set_multicycle_path来组合使用。尤其在做DDR、RGMII这类源同步高速接口时除了静态约束很多工程还会用IDELAY/OSERDES/IDDR这些原语做动态相位调整。把input delay的原理吃透你才能理解为什么IDELAY的tap值要往哪个方向调为什么同一条路径在常温下能工作、高温下就丢包——这背后全是时序窗口在变化。后续有时间再单独写一篇关于Vivado output delay与IDELAY动态补偿的实战笔记到时可以把这几个概念串成一条完整的链路。