RGMII接口时序约束实战:input/output delay计算与收敛技巧

发布时间:2026/10/6 15:32:00
RGMII接口时序约束实战:input/output delay计算与收敛技巧 1. 从一个真实的翻车案例说起RGMII接口的时序约束是我见过最容易让人掉以轻心的坑。表面上看它只有4根数据线加1根时钟跑在125MHz的DDR模式下数据速率1000Mbps好像随便约束一下就能过。但实际情况是我见过太多项目在实验室常温下跑得好好的一到高低温测试就丢包或者换一批PCB板子就出现链路不稳定。问题十有八九出在input/output delay约束没有做对。这篇文章面向的是已经能跑通FPGA基本流程、但对时序约束还停留在“抄模板”阶段的开发者。我会把RGMII接口的input delay和output delay约束从头拆一遍讲清楚每个参数是怎么算出来的、为什么这么算、以及怎么根据实际PCB和器件手册去调整。读完你应该能做到拿到一块新板子看一眼PHY芯片手册和PCB走线报告就能自己写出靠谱的约束而不是到处找别人的xdc文件复制粘贴。需要说明的是下面涉及的具体数值都是基于常见工程实践给出的示例实际项目一定要以你手上的器件手册和PCB实测为准。我踩过的坑会重点标出来这些是文档里不会写的。2. RGMII接口的时序本质与约束思路2.1 为什么RGMII的时序这么难搞RGMII的全称是Reduced Gigabit Media Independent Interface它是GMII的精简版。GMII用了8根数据线跑125MHz的SDR单沿采样RGMII把它砍到4根数据线但为了保持同样的吞吐量改成了DDR双沿采样时钟还是125MHz。这样一来每个时钟周期要传8个bit数据速率就是125M × 8 1000Mbps。问题就出在这个DDR上。发送端FPGA的output方向需要把数据和时钟一起送出去接收端FPGA的input方向需要从PHY送来的数据和时钟里把数据采回来。RGMII标准里定义了一个关键的时序关系发送端的数据和时钟之间有一个约2ns的偏移具体值取决于PHY接收端则要求数据和时钟满足一定的建立保持窗口。这里有个很多人忽略的点RGMII有两种模式一种是延迟模式delay mode一种是直连模式non-delay mode。延迟模式下PHY内部会对时钟或数据做1.5~2ns的延迟让采样点落在数据眼图的正中间。直连模式下这个延迟需要FPGA侧或者PCB走线来提供。你拿到的PHY芯片到底工作在哪种模式直接决定了你的约束怎么写。我见过有人照着别人的xdc抄了一通结果PHY是直连模式约束里却按延迟模式算链路能通才怪。2.2 input delay和output delay到底在约束什么先说一个基本概念。FPGA的时序分析工具不管是Vivado还是Quartus在做时序收敛时需要知道FPGA外部信号的时序关系。对于FPGA输出到PHY的信号工具需要知道PHY端在时钟沿到来之前多久数据必须稳定建立时间以及之后多久数据必须保持稳定保持时间。对于FPGA从PHY接收的信号工具需要知道PHY送出的数据和时钟之间的偏移是多少。这些信息工具自己是不知道的必须由你来告诉它。这就是input delay和output delay约束的作用。它们本质上是把FPGA外部的时序要求“翻译”成工具能理解的约束语言。对于output delay约束的是FPGA输出信号相对于输出时钟的相位关系。对于input delay约束的是输入信号相对于输入时钟的相位关系。注意这里的“相对于”很关键因为RGMII的时钟和数据是同向传输的时钟不是独立的参考时钟而是和数据一起从发送端传到接收端的。2.3 约束的核心参数从哪里来写约束之前你需要准备三样东西第一PHY芯片的数据手册。里面会给出PHY在发送和接收方向上的时序参数包括建立时间setup time、保持时间hold time、输出延迟output delay等。不同厂家的PHY这些参数差别很大比如有些PHY的接收建立时间要求是1.2ns有些只要0.8ns。第二PCB走线报告。FPGA到PHY之间的走线长度差异会影响时钟和数据之间的偏移。虽然RGMII的速率不算特别高但如果走线长度差了几百mil在125MHz的DDR模式下也可能吃掉不少时序余量。第三FPGA器件手册。FPGA的IO单元本身有输入输出延迟这个延迟和IO标准、驱动强度、片内端接等都有关系。工具在做时序分析时会自动考虑这些但你需要知道它们的存在否则算出来的余量和实际对不上。提示很多人写约束时只看PHY手册完全忽略PCB走线的影响。在RGMII这种源同步接口里时钟和数据走线如果不做等长处理偏移会直接吃掉采样窗口。我建议在约束里把PCB走线差异也考虑进去具体方法后面会讲。3. Output Delay约束的完整推导与实操3.1 先搞清楚PHY需要什么Output delay约束的是FPGA发送给PHY的信号。以最常见的延迟模式为例PHY内部会对接收到的时钟做延迟然后用自己的内部时钟去采样数据。这时候PHY手册里通常会给出两个关键参数接收建立时间tSU和接收保持时间tH。假设你用的PHY手册上写着tSU 1.0nstH 0.5ns。这意味着PHY要求数据在采样时钟沿到来之前至少1.0ns就稳定并且在时钟沿之后至少0.5ns还要保持稳定。注意这里的采样时钟是PHY内部经过延迟后的时钟不是你FPGA送出去的时钟。那么FPGA送出的数据和时钟之间应该是什么关系呢在延迟模式下PHY会对时钟做延迟所以FPGA侧的数据和时钟可以基本对齐skew很小。这时候output delay的约束值可以这样算最大输出延迟max 时钟周期 - tSU 8ns - 1.0ns 7.0ns最小输出延迟min tH 0.5ns等等这个算法对吗其实更准确的说法是output delay的max值对应的是数据最晚到达的时间min值对应的是数据最早到达的时间。在Vivado的set_output_delay命令里max和min是相对于时钟沿的。让我换一种更直观的方式来解释。假设FPGA输出的时钟在0ns时刻有一个上升沿PHY在内部把这个时钟延迟了2ns所以在2ns时刻采样数据。PHY要求数据在2ns - 1.0ns 1.0ns之前稳定并且在2ns 0.5ns 2.5ns之后才变化。那么FPGA输出的数据相对于输出时钟0ns沿的约束就是数据最晚必须在1.0ns之前稳定所以max output delay 1.0ns数据最早可以在2.5ns之后变化所以min output delay -2.5ns这里符号容易搞混。实际上Vivado的set_output_delay是这样理解的它告诉工具外部器件在时钟沿之后多久需要数据稳定。对于max delay它约束的是数据到达外部器件的最晚时间对于min delay它约束的是数据到达外部器件的最早时间。我习惯用这样一个公式来算对于上升沿采样的输出set_output_delay -max tSU - clock_to_data_skewset_output_delay -min -tH - clock_to_data_skew其中clock_to_data_skew是PCB上时钟走线和数据走线之间的延迟差。如果时钟走线比数据走线长skew为正反之为负。3.2 一个具体的计算示例假设条件如下RGMII时钟频率125MHz周期8nsPHY接收建立时间tSU 1.2nsPHY接收保持时间tH 0.8nsPCB上时钟走线比数据走线长200mil微带线延迟约150ps/inch所以skew 200mil × 150ps/inch / 1000 0.03ns等等200mil 0.2inch0.2 × 150 30ps 0.03ns。这个值很小可以忽略。但如果走线差是2000mil呢那就是0.3ns就不能忽略了。假设skew 0.3ns时钟比数据长set_output_delay -max 1.2 - 0.3 0.9nsset_output_delay -min -0.8 - 0.3 -1.1ns在Vivado的xdc里写出来就是set_output_delay -clock [get_clocks rgmii_tx_clk] -max 0.9 [get_ports {rgmii_txd[*]}] set_output_delay -clock [get_clocks rgmii_tx_clk] -min -1.1 [get_ports {rgmii_txd[*]}]注意这里我用了-clock而不是-clock_fall因为RGMII是DDR数据和时钟都是在上升沿和下降沿都变化的。对于DDR接口通常需要对上升沿和下降沿分别约束或者使用set_output_delay的-clock_fall选项。实际上更常见的做法是创建一个虚拟时钟然后用set_output_delay同时约束上升沿和下降沿。但RGMII的时钟是随路时钟不是独立时钟所以需要先在FPGA内部生成这个时钟然后把它约束到输出端口上。3.3 DDR输出的特殊处理RGMII的output是DDR的意味着数据在时钟的上升沿和下降沿都会变化。在FPGA内部通常用ODDR原语来实现。ODDR有两个数据输入D1和D2分别对应上升沿和下降沿的输出数据。对于ODDR的输出时序约束需要同时考虑上升沿和下降沿。在Vivado里可以这样写set_output_delay -clock [get_clocks rgmii_tx_clk] -max 0.9 [get_ports {rgmii_txd[*]}] -rise set_output_delay -clock [get_clocks rgmii_tx_clk] -max 0.9 [get_ports {rgmii_txd[*]}] -fall set_output_delay -clock [get_clocks rgmii_tx_clk] -min -1.1 [get_ports {rgmii_txd[*]}] -rise set_output_delay -clock [get_clocks rgmii_tx_clk] -min -1.1 [get_ports {rgmii_txd[*]}] -fall但这里有个问题-rise和-fall的约束值通常是一样的因为PHY对上升沿和下降沿的采样要求是对称的。不过有些PHY手册会分别给出上升沿和下降沿的建立保持时间这时候就要分别设置。注意ODDR的D1和D2如果来自同一个时钟域工具会自动做时序分析。但如果D1和D2来自不同的时钟域或者经过了额外的逻辑就需要特别注意。我见过有人在ODDR前面加了一个寄存器结果引入了额外的时钟到输出延迟导致时序余量被吃掉。3.4 输出时钟的约束RGMII的输出时钟也需要约束。在FPGA内部这个时钟通常也是用ODDR生成的D1接1D2接0这样就能输出一个和时钟同频的方波。对于这个输出时钟需要创建一个generated clock然后设置它的输出延迟。create_generated_clock -name rgmii_tx_clk -source [get_pins oddr_clk/C] -divide_by 1 [get_ports rgmii_txc] set_output_delay -clock [get_clocks rgmii_tx_clk] -max 0 [get_ports rgmii_txc] set_output_delay -clock [get_clocks rgmii_tx_clk] -min 0 [get_ports rgmii_txc]这里把输出时钟的延迟设为0是因为我们把它当作参考时钟数据的延迟都是相对于它来算的。实际上输出时钟本身也有延迟但因为我们是用它作为参考所以它的延迟被归一化为0了。4. Input Delay约束的完整推导与实操4.1 PHY送来的数据长什么样Input方向是PHY发送数据给FPGAFPGA来接收。这时候PHY是发送端FPGA是接收端。PHY手册里会给出输出时序参数通常包括输出数据相对于输出时钟的延迟tCO以及输出时钟的周期抖动等。在延迟模式下PHY会对它送出的时钟做延迟这样FPGA收到的时钟和数据之间就有一个固定的相位关系。FPGA需要用这个时钟去采样数据。假设PHY手册给出tCO_max 2.5nstCO_min 1.5ns。这意味着PHY送出的数据在时钟沿之后1.5ns到2.5ns之间变化。FPGA收到的时钟和数据之间的偏移就是这个tCO。那么FPGA的input delay约束应该这样算set_input_delay -max tCO_max PCB_skewset_input_delay -min tCO_min PCB_skew假设PCB上时钟和数据走线等长skew 0set_input_delay -max 2.5nsset_input_delay -min 1.5ns在Vivado里写出来set_input_delay -clock [get_clocks rgmii_rx_clk] -max 2.5 [get_ports {rgmii_rxd[*]}] set_input_delay -clock [get_clocks rgmii_rx_clk] -min 1.5 [get_ports {rgmii_rxd[*]}]同样因为是DDR需要对上升沿和下降沿分别约束set_input_delay -clock [get_clocks rgmii_rx_clk] -max 2.5 [get_ports {rgmii_rxd[*]}] -rise set_input_delay -clock [get_clocks rgmii_rx_clk] -max 2.5 [get_ports {rgmii_rxd[*]}] -fall set_input_delay -clock [get_clocks rgmii_rx_clk] -min 1.5 [get_ports {rgmii_rxd[*]}] -rise set_input_delay -clock [get_clocks rgmii_rx_clk] -min 1.5 [get_ports {rgmii_rxd[*]}] -fall4.2 输入时钟的处理RGMII的输入时钟是PHY送过来的它和数据一起到达FPGA。在FPGA内部这个时钟需要先经过IBUF然后可能经过BUFG或者BUFIO再送到IDDR或者IDELAY。对于输入时钟需要创建一个时钟约束create_clock -name rgmii_rx_clk -period 8.0 [get_ports rgmii_rxc]但这里有个问题这个时钟是随路时钟它的抖动和偏移会直接影响采样。在延迟模式下PHY已经对时钟做了延迟所以FPGA收到的时钟沿应该落在数据眼图的中间。但实际PCB走线会引入额外的偏移所以需要根据实测来调整。我通常会在约束里加一个set_clock_latency或者set_clock_uncertainty来模拟PCB走线的影响。不过更准确的做法是在PCB设计阶段就做好等长然后在约束里用set_input_delay的skew参数来补偿。4.3 IDDR和IDELAY的使用在FPGA内部接收RGMII数据通常用IDDR原语把DDR数据转成SDR数据。IDDR有两个输出Q1和Q2分别对应上升沿和下降沿采到的数据。如果时序余量不够还可以使用IDELAY来调整输入数据的延迟。IDELAY是一个可编程的延迟单元可以以一定的步进比如78ps来延迟输入信号。通过调整IDELAY的值可以把采样点移到数据眼图的最佳位置。使用IDELAY时需要在约束里加上set_input_delay -clock [get_clocks rgmii_rx_clk] -max 2.5 [get_ports {rgmii_rxd[*]}] -rise set_input_delay -clock [get_clocks rgmii_rx_clk] -min 1.5 [get_ports {rgmii_rxd[*]}] -rise然后通过IDELAYCTRL和IDELAYE2原语来动态调整延迟值。这个调整过程通常需要在系统启动时做一次校准找到最佳采样点。提示IDELAY的参考时钟必须是200MHz对于7系列FPGA这个时钟的精度直接影响延迟步进的准确性。如果参考时钟有偏差IDELAY的实际延迟就会偏离预期值。我建议用FPGA内部的MMCM来生成这个200MHz时钟而不是从外部引入。4.4 一个完整的input约束示例假设条件PHY输出数据延迟tCO_max 2.2nstCO_min 1.2nsPCB上时钟走线比数据走线短500milskew -0.075ns时钟比数据短所以数据比时钟晚到实际上skew -0.075ns意味着数据比时钟晚到0.075ns那么set_input_delay -max 2.2 (-0.075) 2.125nsset_input_delay -min 1.2 (-0.075) 1.125ns在xdc里create_clock -name rgmii_rxc -period 8.0 [get_ports rgmii_rxc] set_input_delay -clock [get_clocks rgmii_rxc] -max 2.125 [get_ports {rgmii_rxd[*]}] -rise set_input_delay -clock [get_clocks rgmii_rxc] -max 2.125 [get_ports {rgmii_rxd[*]}] -fall set_input_delay -clock [get_clocks rgmii_rxc] -min 1.125 [get_ports {rgmii_rxd[*]}] -rise set_input_delay -clock [get_clocks rgmii_rxc] -min 1.125 [get_ports {rgmii_rxd[*]}] -fall5. 时序收敛的实战技巧与常见问题5.1 时序报告怎么看写完约束后跑implementation然后打开时序报告。重点看两个部分Input Delay和Output Delay的时序路径。对于output路径工具会报告从FPGA内部寄存器到输出端口的时序余量。如果余量为负说明数据到达输出端口的时间不满足外部器件的建立或保持要求。对于input路径工具会报告从输入端口到FPGA内部寄存器的时序余量。如果余量为负说明数据从输入端口到达内部寄存器的时间不满足建立或保持要求。看报告时要注意RGMII的时序路径通常会被工具归到“Source Synchronous”或者“System Synchronous”类别里。如果约束写错了工具可能会把它当成异步路径不做时序分析这时候报告里看不到相关路径但实际链路可能有问题。5.2 常见问题速查表问题现象可能原因排查方法链路能通但丢包率高采样点偏离眼图中心调整IDELAY值扫描最佳采样点高低温下链路不稳定时序余量不足看时序报告增加余量换板子后链路不通PCB走线差异大测量实际走线长度重新计算skew时序报告显示路径未约束约束写错或时钟未创建检查create_clock和set_input/output_delayODDR输出数据错误D1/D2连接错误检查ODDR例化代码IDDR采样数据错误IDELAY未校准运行IDELAY校准逻辑5.3 我踩过的坑第一个坑以为约束写完就万事大吉。实际上RGMII的时序余量通常都不大尤其是在125MHz的DDR模式下一个周期只有8ns建立保持窗口可能只有1~2ns。如果PCB走线没做好等长或者PHY的时序参数比较苛刻很容易出现负余量。我的经验是约束写完后一定要看时序报告如果余量小于0.5ns就要考虑优化PCB或者调整IDELAY。第二个坑忽略PHY的工作模式。有些PHY可以通过寄存器配置成延迟模式或直连模式如果你在FPGA侧按延迟模式约束但PHY实际工作在直连模式时序就会完全对不上。我建议在系统启动时先读PHY的寄存器确认它的工作模式然后再加载对应的约束。第三个坑IDELAY校准逻辑没做。IDELAY的延迟值会随温度和电压变化如果不做动态校准时间长了采样点可能会漂移。我通常会在FPGA里加一个简单的校准状态机定期扫描IDELAY值找到误码率最低的那个点。第四个坑时钟约束写错。RGMII的输入时钟是随路时钟不是独立时钟。如果你用create_clock直接约束输入端口工具会把它当成一个独立的时钟源但实际上它和数据是一起传输的。正确的做法是用create_clock约束输入端口然后用set_input_delay来告诉工具数据和这个时钟的关系。5.4 时序收敛的优化手段如果时序余量不够可以尝试以下几种方法第一调整IDELAY值。这是最直接的方法通过扫描IDELAY值找到最佳采样点。通常IDELAY的步进是78ps扫描范围是0到31对应0到2.4ns左右的延迟。第二优化PCB走线。如果条件允许重新设计PCB让时钟和数据走线严格等长。对于RGMII通常要求走线差异控制在100mil以内。第三降低时钟频率。如果应用允许可以把RGMII时钟从125MHz降到25MHz100Mbps模式这样时序余量会大很多。但这样会牺牲带宽。第四使用FPGA内部的PLL来调整时钟相位。有些FPGA的PLL可以以很小的步进调整输出时钟的相位通过调整时钟相位来优化采样点。第五在PHY侧调整延迟。有些PHY支持通过寄存器调整内部延迟值可以配合FPGA侧的IDELAY一起优化。6. 从约束到收敛的完整流程总结6.1 标准操作流程我把整个流程整理成以下步骤你可以直接照着做阅读PHY手册找到发送和接收方向的时序参数tSU、tH、tCO等。获取PCB走线报告计算时钟和数据走线之间的skew。根据公式计算output delay和input delay的max/min值。在xdc文件中创建时钟约束和input/output delay约束。跑implementation查看时序报告。如果余量为负调整IDELAY值或优化PCB。在系统启动时运行IDELAY校准逻辑找到最佳采样点。做高低温测试和长时间稳定性测试确认链路可靠。6.2 参数计算速查参数计算公式说明output delay maxtSU - skewskew为时钟走线延迟减数据走线延迟output delay min-tH - skew注意符号input delay maxtCO_max skewskew为数据走线延迟减时钟走线延迟input delay mintCO_min skew同上6.3 最后分享几个实用技巧第一个技巧在约束文件里加注释写清楚每个参数的来源和计算过程。这样过几个月回头看或者交接给同事时能快速理解。第二个技巧用Tcl脚本自动生成约束。如果你有多个板子或者多个PHY型号可以写一个脚本根据PHY手册的参数自动生成xdc文件避免手动计算出错。第三个技巧在FPGA里加一个简单的误码率测试逻辑。通过发送伪随机序列在接收端比对可以快速评估链路质量。这个逻辑不需要太复杂一个LFSR加一个比较器就够了。第四个技巧如果时序实在收敛不了可以考虑用FPGA内部的SerDes或者SelectIO的高级特性比如Xilinx的ISERDES和OSERDES它们对源同步接口的支持更好时序余量也更大。不过这会增加设计的复杂度需要权衡。我在实际项目中的体会是RGMII的时序约束没有想象中那么难但也没有那么简单。关键是要理解每个参数背后的物理意义而不是机械地套公式。一旦你搞清楚了PHY需要什么、FPGA能提供什么、PCB引入了什么约束就是水到渠成的事。