ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

FPGA LVDS接收实战:SelectIO IP配置、自动训练与仿真全流程

FPGA LVDS接收实战:SelectIO IP配置、自动训练与仿真全流程 干FPGA这行的迟早要跟LVDS打交道。我第一次调LVDS接收是在一块Artix-7板卡上示波器测差分对P端N端波形都正常可FPGA采出来的数据就是乱码。一开始怀疑硬件焊接问题后来一块块排查才发现是Vivado里SelectIO IP的参数配错了再加上完全没有做训练对齐才折腾了大半天。这篇就拿Vivado SelectIO IP为主线把LVDS接收从IP配置、管脚约束、自动训练到仿真验证的完整流程捋一遍附上能直接跑的仿真代码给准备做LVDS接收的朋友们省点时间。1. 为什么LVDS接收要用SelectIO IP而不是自己折腾原语1.1 LVDS接收到底难在哪LVDS低电压差分信号是FPGA跟ADC、DAC、相机Sensor、SerDes桥接芯片之间最常见的接口之一。它的物理层特点是用一对差分线传数据摆幅只有350mV左右速率可以跑到几百Mbps甚至上Gbps。虽然协议本身不复杂但在FPGA里做接收时难点主要集中在四个方面差分信号转单端后的采样时钟沿对齐、串并转换的位置对齐、跨时钟域处理以及高速信号在布局布线上的电气约束。如果你直接用原语比如IBUFDS、IDDR、ISERDES这些自己拼也不是不行但对于刚接触LVDS的工程师很容易掉进一个坑ISERDESE2的位序bit order和Bitslip逻辑稍微配错输出数据就是乱的而且这种乱不是完全乱是看起来好像有规律、实际上每几个字节错一位排查起来非常痛苦。1.2 IP封装的核心价值Vivado的SelectIO IP本质上是把IBUFDS、IDELAYE2、ISERDESE2、Bitslip Controller这些底层资源做了一整套封装并且给你暴露了一个模块化的配置界面。它解决的不只是“省事”更重要的是把以下几个业界经验固化进了IP里输入延迟的自动校准机制、Bitslip的控制逻辑、并行数据的位宽转换、以及跟Xilinx原语命名一致的控制端口。对大多数项目来说用IP比自己拼原语至少要少写两三百行代码而且不容易出现那些由于原语约束遗漏导致的静态时序问题。需要说明的是这个IP并不限制你只能做纯LVDS它也支持MISC、TMDS等模式但是本文只围绕LVDS接收来说。注意如果目标器件是Versal或者UltraScaleSelectIO IP的某些端口名称和时序模型会和7系列有差异但配置思路基本一致。2. Vivado里把SelectIO IP配好每个参数背后是什么2.1 接口方向与Bank电压选择新建IP时第一步选接口方向。做接收就选“Receiver”发送就选“Transmitter”如果收发一体选“Duplex”。这里建议先只选Receiver把链路调通再加发送通道免得一开始就被回环干扰思路。Bank电压必须在IP实例化前就确定LVDS在7系列里通常对应HR bank需要VCCIO接2.5V很多板子LVDS就配2.5V。当然有些器件支持LVDS with Termination这时还需要确认外部是否已经接了100欧终端电阻。如果设置里选了Internal Termination而板子上又接了外部电阻会造成等效端接变50欧信号反射会明显增加这一点是很多板卡数据错乱的真凶。2.2 时钟结构内部时钟还是外部时钟配置页里会出现Clocking相关选项核心决定是你由FPGA内部PLL生成采样时钟还是直接使用外部输入的随路时钟Forwarded Clock。对于LVDS接收我大多数情况推荐“Use Forwarded Clock”也就是随路时钟。因为LVDS发送端一般会把数据和时钟同步发过来接收端用这路随路时钟去采数据是最可靠的不需要自己估计相位。IP内部会把这路随路时钟当作高速时钟再经过BUFR或BUFIO分配到ISERDES的时钟网络。如果误选成内部PLL生成时钟你需要自己保证时钟和数据之间的时序关系调试难度立刻上了一个台阶。2.3 数据通道宽度与双倍速率的关系接下来是并行数据宽度。LVDS通常以DDR方式传输一个差分对的一个时钟周期内传2bit所以IP里的“Parallel Data Width”实际上是指你希望在FPGA逻辑侧看到的并行位宽。比如一个通道的串行速率为700Mbps随路时钟350MHzDDR表示一个周期两个采样点。如果你设并行宽度为7对应一个时钟周期解出7bit那么内部ISERDES的串并转换比例就是7:1。这个数值必须匹配实际协议因为Bitslip本质上就是为这种非整数比对齐服务的。这里有个容易忽略的点当并行宽度不是8时后续逻辑尽量用valid信号去使能数据处理而不是简单按字节对齐否则自动训练做完你还是不知道数据从哪个bit开始算有效。2.4 训练相关参数怎么选SelectIO IP实际上分两部分数据通道IP和Training IP。在做LVDS接收时Training IP不是必选项但它提供的“自动训练”能力非常关键。训练的核心是可控的Bitslip和延迟调整。Xilinx的Training IP一般会对接一个Memory Interface的Training状态机但对LVDS这种不是内存接口的场景很多工程师喜欢自己写训练状态机来控制IP的数据输出。在IP配置页面里如果你看到“Enable Training”这类选项建议勾选因为IP会生成一个training interface方便你接入自己的对齐状态机。如果不想用IP自带的也可以不勾自己通过原语控制bitslip信号。两种方式各有利弊IP自带的好处是经过官方验证坏处是接口复杂想自定义训练图案时反而不方便。3. 从IP到板级信号差分线连接与硬件注意事项3.1 IBUFDS_DIFF_OUT和电阻匹配在IP内部接收端首先调用的是IBUFDS_DIFF_OUT把一对差分信号转成单端。然后这路单端信号会直接接到IDELAYE2和ISERDESE2。IP生成文件里你可以看到它用了一个名为ibufds_inst的原语实例这就是物理层入口。外部电路上LVDS需要一个100欧的终端电阻并联在差分对的P和N之间用来吸收反射。这里需要强调“终端电阻放在靠近接收端芯片引脚的地方”理想情况是PCB走线末端因为一旦走线超过几厘米反射就会显著影响采样。选型时一般用0402封装的100欧电阻精度1%即可。3.2 差分线高值和低值怎么判断好坏标题里提到的“LVDS差分线高值和低值”实际上指的就是P端和N端的共模电压以及差分摆幅。你可以用示波器差分探头测P-N电压差。正常LVDS信号差分电压在250mV到450mV之间共模电压在1.2V左右。如果测到的差分电压低于100mV接收端很可能因为噪声而频繁翻转这时再好的训练算法也救不了。还有一种常见情况P和N标签接反。虽然在PCB布局上LVPECL和LVDS串接能工作但LVDS对极性很敏感接反后波形直接是反相FPGA采出来全是全0或者全1。遇到这种问题先用示波器确认P端相对N端的逻辑电平别急着改代码。3.3 Bank、引脚分配和布局布线的坑SelectIO IP生成的端口最终要映射到具体Bank和引脚。需要注意LVDS差分对必须放在同一Bank内且是配对引脚P和N相邻。如果手动分配时不使用成对引脚Vivado在布局布线阶段会报错或者自动使用额外的路由资源导致时序变差。很多实际项目还会把LVDS引脚约束成IO_STANDARD LVDS但如果你同时设置了DIFF_TERM TRUE会额外使用片内差分终端。7系列HR bank是支持DIFF_TERM的但这种终端是有电阻网络实现的等效精度远不如外部电阻。实测下来除非板子空间极有限否则不建议启用。另外如果你的FPGA是多die封装比如一些大容量的UltraScale不同die之间的LVDS路由限制和普通信号不一样。这时要在Vivado里打开Device视图看引脚是否落在同一个die的相邻区域否则即使通过时序收敛信号质量也可能不稳。4. 自动训练状态机从Bitslip到Word Alignment4.1 为什么明明配置对了还会错位LVDS接收最让人头疼的是物理波形正常、时钟正常、串并转换也正常但并行数据的“字边界”是不知道的。比如7:1的串并转换一个输出周期得到7比特但协议里数据可能是按8比特甚至16比特组织的所以这7比特并不一定和协议字对齐。如果直接用相当于从数据包的中间开始读所有内容全都错位。这就是必须引入自动训练的原因。训练的目标就是通过调整Bitslip让串并转换输出的数据与协议规定的字边界对齐。对齐的标准一般是一个预先约定的训练图案发送端会在链路空闲时周期性发送这个图案接收端检测到它后认为对齐完成。4.2 训练状态机设计思路我这里给出一个我自己常用的简化状态机设计思路适合初调。状态IDLE等待外部指令或上电完成然后跳出到ALIGN。 状态ALIGN不断读取ip输出数据检测训练图案。如果检测到进入LOCKED如果超过N个周期没检测到触发Bitslip并继续检测。 状态LOCKED正常运行但需要每隔一定周期检查训练图案是否仍然对齐。如果连续多次失败则回到ALIGN。这里有一个细节Bitslip操作不是立即生效的它需要一个clock周期来翻转ISERDES内部的采样顺序。因此每触发一次bitslip至少要等待一个clk最好等上三个clk再继续检测否则会出现检测结果滞后的假象。另外训练图案检测不能用简单的“如果数据等于pattern就认为对齐”因为IP输出的并行数据在未对齐前数据位本身已经经过了串转并某个并行字看起来像pattern可能是巧合。稳妥的办法是连续检测到多个完整pattern帧或者以帧同步位作为主要判断依据。4.3 训练失败超时与重试训练状态机必须要有超时功能。假设发送端没发训练图案或者P/N接反或者终端电阻坏了状态机会永远停在ALIGN。工程上通常设一个超时计数器比如100us后仍未锁定就置一个train_error标志并重新执行一遍延迟扫描或全复位。有些LVDS接收还需要做“延迟扫描”也就是通过IDELAYE2逐个步进延迟找到采样眼图中心。Step一般是1/64的taps具体延迟值跟器件电压温度有关所以跑在极限速率时自动训练不只是Bitslip对齐也要在训练里加入IDELAY扫描。SelectIO IP自带的Training IP就支持两个方向的扫描如果自己写建议至少把延迟扫描单独做成一个可调用模块。5. 仿真先行附上可跑的LVDS接收仿真代码5.1 测试平台怎么构建仿真的目的是在一行硬件代码没下板之前先把对齐状态机和数据通路跑通。测试平台里需要一个模拟发送端产生训练图案和有效数据然后通过差分信号实际用P和N两个单端信号表示送给DUT。如果你的IP是全差分输入仿真时可以直接给P/N两个信号不需要精确建模差分电气特性。5.2 仿真代码主体下面这段是我在Vivado 2022.2下验证过的顶层测试代码核心是让DUT的Bitslip和训练状态机可观测同时用一个虚拟发送端产生带字边界的串行数据。代码里去掉了器件相关原语使用生成IP后的行为级模型。// testbench_lvds_rx.v timescale 1ns/1ps module tb_lvds_rx; reg clk_fast; reg clk_slow; reg rst_n; reg lvds_p; reg lvds_n; wire [7:0] rx_data; wire rx_valid; wire train_done; // 模拟LVDS时钟350MHz initial clk_fast 0; always #1.428 clk_fast ~clk_fast; // 模拟逻辑侧时钟50MHz initial clk_slow 0; always #10 clk_slow ~clk_slow; initial begin rst_n 0; lvds_p 0; lvds_n 1; #100; rst_n 1; end // 虚拟发送端产生训练图案 0x5A之后随机数据 reg [7:0] shift_reg; integer i; initial begin shift_reg 8h5A; wait (rst_n 1); repeat (200) begin for (i 0; i 8; i i 1) begin lvds_p shift_reg[7-i]; lvds_n ~shift_reg[7-i]; #2.856; // 对应350MHz DDR end end // 之后发随机有效数据 repeat (500) begin shift_reg $random; for (i 0; i 8; i i 1) begin lvds_p shift_reg[7-i]; lvds_n ~shift_reg[7-i]; #2.856; end end $finish; end // 实例化被测设计IP封装例化名按自己工程修改 lvds_rx_top dut ( .clk_fast (clk_fast), .clk_slow (clk_slow), .rst_n (rst_n), .lvds_p (lvds_p), .lvds_n (lvds_n), .rx_data (rx_data), .rx_valid (rx_valid), .train_done (train_done) ); // 检测训练完成后有效数据是否对齐 reg [7:0] expected; always (posedge clk_slow) begin if (train_done rx_valid) begin // 此处可加断言rx_data 应该等于 shift_reg 串转并的值 end end endmodule这段代码的核心价值在于在PC上先验证训练状态机的行为特别是Bitslip触发次数。实际使用中你还需要给IP的复位和锁存信号加一个安全的异步复位同步释放逻辑避免在复位释放瞬间触发训练状态机的误动作。5.3 仿真中验证自动训练效果跑完仿真后重点看两个信号train_done应该在发送端持续发训练图案时拉高rx_data在随后发随机数据时应该稳定对齐。如果在train_done拉高后仍出现字节错位说明训练状态机的对齐条件太宽松比如只在识别到一次pattern就锁定。这种情况下建议改成一个“连续N次检测到pattern”再加锁的状态。N取3或5都行N太大会导致锁定时间过长太小则容易误锁。这个N的值建议做成参数方便后续根据协议调整。6. 上板实测Vivado常见报错与排查链路6.1 DRC RTSTAT-2 这类报错的定位热搜里不少人遇到DRC RTSTAT-2这个错误通常跟时钟网络或者BUFG/BUFIO的使用有关。使用SelectIO IP时随路时钟一般会接到BUFIO或BUFR上如果IP配置里面选择不使用BUFIOBUFR又没有放在合适的时钟区域Vivado就会报DRC错误。定位方法先看Messages窗口里DRC具体指向哪个cell和net再回到IP配置里检查“Clock Resources”是否跟实际约束一致。一句话别把随路时钟随便接到BUFG上因为BUFG是全局时钟网络不擅长处理高速IO时钟会带来很大的时钟偏斜。6.2 多Die FPGA带来的约束差异多die的FPGA在布局布线时不同die之间的时钟与IO资源不是完全等价的。LVDS接收端如果引脚在不同die上SelectIO IP生成的例化会被拆到多条die边界上训练状态机里Bitslip的时序关系也会更复杂。如果遇到这一类器件建议在工程早期就固定好引脚分配并且在约束文件里给LVDS通道标注PACKAGE_PIN和IO_STANDARD。不要依赖Vivado全自动引脚分配因为它不会主动规避跨die长走线。判断是不是跨die问题可以在Device视图里看所选pin的颜色区域颜色不同的基本就是不同die。6.3 实测数据不稳定的排查顺序上板后如果数据时好时坏我建议按下面的顺序排查而不是一上来就改代码。先用示波器测差分电压和共模电压确认电平和摆幅在LVDS规范内。检查IP配置里的时钟结构是不是用随路时钟以及BUFIO/BUFR是否在正确时钟区域。观察训练状态机有没有锁定如果没锁定排除发送端有没有发训练图案。如果锁定但数据错误试着不断增加延迟扫描步进看是否有采样裕量。检查是否存在地弹或电源纹波尤其是多个LVDS通道同时工作时Bank的供电不足会导致所有通道同时出错。这5步看起来简单但实际项目里能解决绝大多数“随机错误”问题。我见过最容易忽略的是发送端训练图案只在启动瞬间发一次接收端一旦复位晚一点就会错过训练窗口导致一直无法锁定。这种情况最好在协议层约定训练图案要周期发送或者接收端主动向发送端发训练请求。7. 这套流程还能怎么扩展完成了LVDS接收和自动训练之后这套思路可以直接迁移到MIPI D-PHY的LP/HS切换、ADC并行数据采集、以及一些自定义串行协议里。比如MIPI转LVDS的应用本质上也是在做高速串行数据的接收和对齐区别是MIPI的D-PHY需要额外处理LP状态下的信号而LVDS接收相对简单一些。另外很多项目最终还要把接收到的数据通过UART或者Ethernet上传到上位机这时候你可以复用前面调好的训练状态机把它封装成一个独立的对齐模块上位机看数据流是否稳定可以快速排查是链路问题还是发送端问题。我自己在实际使用中还有一个体会是不要过度依赖IP自带的行为模型尤其是Training IP在不同版本Vivado下的仿真模型差别不小。建议在关键节点打上$display把训练状态和Bitslip次数打出来这样一旦上板出现问题可以拿仿真log和ILA抓到的信号做对比很快能定位是状态机没跑对还是实际硬件时序和仿真不一致。最后再分享一个细节开发过程中建议把SelectIO IP的仿真模型、综合后行为、以及上板实测三者结果分别存一份log。前两者一致但上板不对基本可以断定是硬件问题只有前两者就不一样那大概率是IP配置或约束问题。这个习惯能帮你省下大量基于直觉的排查时间。
返回列表