ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ZYNQ7020 LVDS差分信号实战:OBUFDS与IBUFDS原语详解

2026/10/7 4:36:36 拓冰建站 浏览量
ZYNQ7020 LVDS差分信号实战:OBUFDS与IBUFDS原语详解 1. 为什么LVDS在ZYNQ7020上值得单独拎出来讲LVDSLow-Voltage Differential Signaling这个接口标准做FPGA的人几乎都绕不开。它的核心思路很简单用一对差分线以大约350mV的摆幅、1.2V左右的共模电压来传输数据。摆幅小意味着翻转速度快、功耗低差分结构意味着共模噪声会被接收端自然抵消掉。这两点加起来让LVDS在几百Mbps到1Gbps级别的板级传输里非常吃香摄像头、显示屏、高速ADC采样数据回传到处都能看到它。ZYNQ7020这颗芯片PL侧的I/O资源对LVDS的支持是分档的。不是随便一个引脚都能跑LVDS这一点新手特别容易踩坑。它的Bank分HRHigh Range和HPHigh Performance两类HR Bank的VCCO通常要2.5V或3.3VHP Bank的VCCO一般是1.8V或更低。LVDS的共模电压要求大约1.2V所以HR Bank在3.3V供电下并不能直接输出标准LVDS需要外部电阻网络或者用TMDS之类的变通方式而HP Bank在1.8V供电下配合内部二极管可以比较干净地做出LVDS电平。这个细节决定了你在画原理图阶段就得把Bank类型和供电规划好否则后面代码写得再对信号也出不来。我这次要聊的是一个很典型的场景用ZYNQ7020的PL侧通过OBUFDS原语把内部单端信号转成LVDS差分对发出去同时用IBUFDS原语把外部进来的LVDS差分对接收成内部单端信号。这套东西听起来就两个原语但真正落地时会牵扯到引脚约束、Bank供电、端接电阻、时钟资源、时序约束还有调试时怎么判断到底是原语没生效还是硬件有问题。下面我按实际做项目的顺序把每个环节拆开讲。2. 整体设计思路与方案选型2.1 为什么用原语而不是IP核Xilinx现AMD的Vivado里其实有SelectIO IP核可以图形化配置差分接口。但我个人在LVDS这种相对固定的场景下更倾向于直接用OBUFDS和IBUFDS原语。原因有几个第一原语是底层硬件结构的直接映射综合出来的结果可预测不会因为IP版本升级带来意外第二代码量极小一个模块几行就写完了维护和移植都方便第三调试时信号路径清晰ILA抓波形时不会有一堆IP内部逻辑干扰判断。SelectIO IP更适合那种需要动态改变I/O标准、需要复杂串并转换比如ISERDES的场景。如果只是单纯的差分收发原语是更轻的选择。这个取舍逻辑和写RTL时“能用一行assign解决就不写always块”是一个道理。2.2 收发链路的整体结构整个链路我分成三块来设计。发送侧内部逻辑产生单端信号经过OBUFDS转成差分对送到顶层端口再通过引脚约束绑定到具体的差分引脚对上。接收侧外部差分对先进IBUFDS转成单端信号再送给内部逻辑或者时钟网络。中间如果需要环回测试就把发送的差分对通过板上的走线或者外部短接线接到接收差分对上形成一个自测闭环。这个结构里OBUFDS和IBUFDS是核心但它们不是孤立的。发送侧要考虑输出驱动能力和端接接收侧要考虑输入端的100欧姆匹配电阻是否已经存在。很多开发板已经在差分对之间焊了100欧姆端接你就不需要再在FPGA内部用DIFF_TERM否则会双重端接导致信号幅度异常。这个判断必须看原理图不能想当然。2.3 Bank与供电的前置规划前面提到Bank类型的问题这里展开说。ZYNQ7020的HP BankVCCO典型值是1.8V。在1.8V供电下LVDS的共模电压要求约1.2VXilinx的HP Bank内部有可配置的二极管和电阻网络可以在输出时把电平拉到符合LVDS规范的范围。所以做LVDS输出优先选HP Bank。如果你手上板子的差分对恰好落在HR Bank上VCCO是3.3V那就不能直接输出标准LVDS。这时候常见的做法是用外部电阻网络做电平转换或者改用TMDS_33标准HDMI常用但TMDS和LVDS电气特性不同接收端未必兼容。所以最稳妥的方案是在硬件设计阶段就把LVDS信号分配到HP Bank。这一点我在项目里吃过亏板子回来才发现差分对在HR Bank最后只能飞线改到HP Bank的引脚上非常被动。3. OBUFDS与IBUFDS原语的核心细节3.1 OBUFDS的端口与工作方式OBUFDS的全称是Output Buffer Differential Signaling。它的端口很简洁一个输入I单端两个输出O和OB差分对O是正端OB是负端。例化的时候I接内部单端信号O和OB直接连到顶层output端口。OBUFDS #( .IOSTANDARD(LVDS_18) // 根据Bank供电选择 ) OBUFDS_inst ( .I (tx_data), // 内部单端信号 .O (lvds_p), // 差分正端 .OB (lvds_n) // 差分负端 );这里有个细节IOSTANDARD参数必须和Bank的VCCO匹配。HP Bank在1.8V下用LVDS_18如果写成LVDS_33综合可能不报错但实现阶段会报I/O标准与Bank电压冲突。我建议在XDC约束里也显式写一遍IOSTANDARD双重保险。OBUFDS内部其实就是一个差分驱动器它把单端信号的逻辑电平转换成差分对上的互补电平。当I为高时O拉高、OB拉低I为低时反过来。差分摆幅由IOSTANDARD决定LVDS_18下大约是350mV。3.2 IBUFDS的端口与端接配置IBUFDS是Input Buffer Differential Signaling。端口是I和IB差分输入对输出O单端。例化如下IBUFDS #( .IOSTANDARD(LVDS_18), .DIFF_TERM(TRUE) // 是否启用内部差分端接 ) IBUFDS_inst ( .I (lvds_p), .IB (lvds_n), .O (rx_data) );DIFF_TERM这个参数是重点。如果板上差分对之间已经有100欧姆端接电阻这里必须设成FALSE否则内部端接和外部端接并联等效阻抗变成50欧姆信号幅度会掉一半接收可能直接失败。反过来如果板上没有端接电阻就设成TRUE用FPGA内部的端接。判断依据只有一个看原理图。我见过太多人默认写TRUE结果板子上有外部电阻调了半天以为是时序问题。3.3 差分对的引脚约束写法引脚约束是原语能不能真正生效的关键。差分对在XDC里要用两个独立的引脚约束并且这两个引脚必须是芯片手册里标注的差分对_P和_N。set_property PACKAGE_PIN F14 [get_ports lvds_p] set_property IOSTANDARD LVDS_18 [get_ports lvds_p] set_property PACKAGE_PIN E14 [get_ports lvds_n] set_property IOSTANDARD LVDS_18 [get_ports lvds_n]注意差分对的P和N引脚在芯片内部是固定的配对关系不能随便选两个引脚凑一对。Vivado在实现阶段会检查这个配对如果选错了会报错。查配对关系最可靠的方法是看ZYNQ7020的封装引脚表package pinout或者用Vivado的I/O Planning视图它会自动显示哪些引脚可以组成差分对。提示如果XDC里只约束了P端没约束N端Vivado有时会自动推导但推导结果未必是你想要的引脚。养成P和N都显式约束的习惯。4. 完整实操流程与关键环节4.1 工程创建与顶层端口定义先在Vivado里建一个RTL工程器件选xc7z020对应的封装比如clg400或者clg484取决于你的板子。顶层模块定义差分端口module lvds_top ( input wire lvds_rx_p, input wire lvds_rx_n, output wire lvds_tx_p, output wire lvds_tx_n, input wire sys_clk, output wire led );这里我把收发都放在一个顶层里方便做环回测试。sys_clk接板上的晶振led用来指示接收到的数据活动调试时一眼就能看出链路通没通。4.2 发送逻辑与OBUFDS例化发送侧我做一个简单的递增计数器把计数值的最高位或者某一位送到OBUFDS。这样发送的是方波接收端如果正常led会以固定频率闪烁。reg [7:0] tx_cnt; always (posedge sys_clk) begin tx_cnt tx_cnt 1b1; end OBUFDS #( .IOSTANDARD(LVDS_18) ) u_obufds ( .I (tx_cnt[7]), .O (lvds_tx_p), .OB (lvds_tx_n) );选tx_cnt[7]是因为它翻转频率是sys_clk的1/256肉眼能看清led闪烁。如果选tx_cnt[0]频率太高led看起来是常亮反而不好判断。4.3 接收逻辑与IBUFDS例化接收侧把差分对转成单端然后驱动ledwire rx_data; IBUFDS #( .IOSTANDARD(LVDS_18), .DIFF_TERM(FALSE) // 假设板上有外部100欧姆端接 ) u_ibufds ( .I (lvds_rx_p), .IB (lvds_rx_n), .O (rx_data) ); assign led rx_data;如果做环回测试把lvds_tx_p/n和lvds_rx_p/n在板上用短接线连起来led应该跟着tx_cnt[7]闪烁。如果不闪问题就在原语配置、引脚约束或者硬件连接上。4.4 时序约束的补充LVDS信号如果速率不高比如几十Mbps时序约束可以相对宽松。但如果速率上到几百Mbps就必须做输入输出延迟约束。发送侧用set_output_delay接收侧用set_input_delay参考时钟用create_clock定义。create_clock -period 10.000 -name sys_clk [get_ports sys_clk] set_output_delay -clock sys_clk -max 2.0 [get_ports lvds_tx_p] set_output_delay -clock sys_clk -min -0.5 [get_ports lvds_tx_p]这些数值需要根据外部器件的建立保持时间来计算不能随便填。如果只是板内环回测试可以先用默认值跑通功能再逐步收紧约束看时序余量。4.5 实现与比特流生成综合、实现、生成比特流这三步重点看实现后的Report。打开I/O Report确认差分对的引脚、IOSTANDARD、端接设置都符合预期。再看Timing Report确认没有时序违例。如果I/O Report里显示IOSTANDARD是LVCMOS而不是LVDS说明原语没生效或者约束被覆盖了要回去检查代码和XDC。下载比特流后用示波器或者逻辑分析仪测差分对上的波形。正常LVDS波形应该是两条互补的方波摆幅约350mV共模电压约1.2V。如果摆幅明显偏大或偏小检查端接和IOSTANDARD。5. 常见问题与排查技巧实录5.1 原语综合报错“I/O standard not compatible”这个报错几乎都是Bank供电和IOSTANDARD不匹配。比如HP Bank的VCCO是1.8V你却写了LVDS_33。解决办法是查原理图确认Bank电压然后改IOSTANDARD。如果Bank电压确实不支持LVDS就得考虑换引脚或者加外部电平转换。5.2 接收端数据一直是高或一直是低先确认DIFF_TERM设置和板上端接是否匹配。如果板上没有端接电阻DIFF_TERM又设成FALSE差分对处于悬空状态接收端会随机翻转或者固定在某一个电平。这时候把DIFF_TERM改成TRUE或者在外部分别加100欧姆端接。另一个可能是差分对的P和N接反了。LVDS的P和N反接不会损坏器件但接收数据会取反。如果发现数据极性不对先检查引脚约束里的P/N是否和原理图一致。5.3 环回测试不通但单端测试正常这种情况说明原语本身没问题问题在差分路径上。排查顺序先量差分对上的波形确认发送侧有信号再量接收侧差分对确认信号到达最后检查端接和共模电压。我遇到过一次是板上的差分走线中间有个过孔虚焊信号在发送侧正常到接收侧就没了补焊后立刻通了。5.4 高速下误码率偏高速率上去之后如果误码率偏高优先看时序余量和信号完整性。时序上检查set_input_delay/set_output_delay是否合理必要时用IDELAY做输入延迟调整。信号完整性上检查差分走线是否等长、是否远离其他高速信号、参考平面是否完整。LVDS虽然抗干扰能力强但前提是差分对本身走线质量过关。问题现象可能原因排查方法综合报I/O标准冲突Bank电压与IOSTANDARD不匹配查原理图确认VCCO改IOSTANDARD接收数据固定不变端接缺失或P/N反接检查DIFF_TERM和引脚约束环回不通差分路径硬件问题分段量波形查虚焊和走线高速误码时序余量不足或信号完整性差收紧时序约束检查走线等长注意DIFF_TERM和外部端接只能二选一同时启用会导致信号幅度异常。这个坑我在两个项目里都踩过每次都是查了半天时序最后发现是端接重复。6. 几个容易被忽略的实操心得第一个心得是关于原语例化时的信号命名。我习惯把差分对的顶层端口命名为xxx_p和xxx_n内部原语输出命名为xxx_int。这样在ILA里抓信号时一眼就能区分是芯片外部引脚还是内部单端信号调试效率高很多。第二个心得是关于环回测试的接线。如果板上没有预留环回通路可以用杜邦线把发送差分对和接收差分对连起来。但杜邦线不是差分线长度稍长就会引入很大噪声导致高速下误码。所以环回测试建议在低速下做确认功能通了之后再上高速用正规差分线或者板内走线。第三个心得是关于IOSTANDARD的写法。Vivado里LVDS_18和LVDS这两种写法在不同版本里行为可能不同。我一般显式写LVDS_18并且在XDC里也写一遍避免工具自动推导出意外结果。这个习惯在跨版本迁移工程时特别有用。第四个心得是关于Bank供电的确认。不要只看原理图上标的电压最好用万用表实测一下Bank的VCCO引脚。我遇到过原理图标1.8V但实际焊的是2.5V电阻的情况导致LVDS输出波形完全不对。实测一次省下后面几小时的调试时间。这套OBUFDS加IBUFDS的用法说到底就是把FPGA内部的单端世界和板上的差分世界连起来。原语本身很简单难的是围绕它的供电、端接、约束和调试。把这几块都理顺了LVDS链路基本就是一次通。