ARTICLE DETAIL

资讯详情

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

FPGA实战:LVDS高速接口原理、Verilog实现与调试全攻略

FPGA实战:LVDS高速接口原理、Verilog实现与调试全攻略 做FPGA这些年遇到过不少头疼事LVDS高速接口调试绝对排得上前三。刚接触那会儿照着参考设计抄了个RGB转LVDS的模块一上板子屏幕上雪花点乱飞偶尔还直接黑屏查了整整三天最后发现竟然是发送端差分对P/N接反了。那会儿就在想如果有人把整个调试思路从头到尾捋一遍能少走多少弯路。这篇东西就是冲着这个来的。我会从LVDS的原理讲起再贴实际工程的Verilog代码手把手把发送端、接收端、引脚约束、时序约束、调试流程全部过一遍最后把我在项目里踩过的坑和排查思路也一并交代清楚。不管你是刚入门的FPGA小白还是被高速接口折磨过的老油条这篇文章都能给你一些实实在在的参考。1. LVDS接口原理与FPGA实现架构1.1 LVDS为什么能在高速下稳定传输LVDS全称是Low-Voltage Differential Signaling低压差分信号。它能在几百Mbps甚至Gbps级别跑得稳靠的是两个核心设计思路。第一差分传输。信号用两根线传一根是P端一根是N端接收端判断的是两端的电压差。干扰进来往往同时作用在两根线上差没变信号就没事。这相当于干扰进来的时候被直接抵消了。第二低压摆幅。LVDS的差分电压摆幅只有大概350mV比TTL的3.3V或2.5V小太多。电压变化幅度小意味着上升沿和下降沿时间短功耗低对外辐射也小可以跑得极快。打比方说TTL信号像一个大嗓门的人在嘈杂的房间里喊话声儿大但听不清LVDS就像两个人面对面用极小的声音说悄悄话虽然声音小但因为贴着耳朵说反而听得一清二楚。这就是LVDS在高速传输中能保证信号质量的底层逻辑。1.2 FPGA内部如何把并行数据变成高速差分串行FPGA内部的核心逻辑单元跑得没那么快一般几百MHz就到头了但LVDS接口往往需要跑到几百Mbps到几Gbps的线速率。这中间差了数量级怎么办靠的是FPGA内置的高速串行器。在Xilinx 7系列和Intel Cyclone系列里这玩意儿叫OSERDES和ISERDES也就是输出串行器和输入串行器。OSERDES负责把FPGA内部低速并行的多bit数据按一个高速时钟一位一位地送出去ISERDES反过来把外部进来的高速串行数据一位一位地接进来再拼成并行数据给内部逻辑处理。这里有个关键概念叫串并转换比。比如7系列FPGA的OSERDESE2可以支持3:1、4:1、6:1、7:1、8:1等不同比值。拿最常用的7:1来说如果线速率是700Mbps内部并行时钟只需要100MHz数据位宽是7bit。内部逻辑跑100MHz完全没压力但IO口上的数据速率已经到700Mbps了这就是高速接口的基本玩法——把快的速度堆在IO上内部保持低速率宽位宽降低时序收敛难度。1.3 先搞清楚你的FPGA平台支持什么动手写代码之前务必先翻器件手册确认你手里的FPGA的IO是否支持LVDS标准支持的速率上限是多少。以Xilinx为例7系列FPGA的HPHigh PerformanceBank和HRHigh RangeBank都支持LVDS但对电压的要求不同。HP Bank要求VCCO为1.8VHR Bank通常要求2.5V或3.3V。Intel Cyclone系列也是类似不同Bank支持的IO标准列表都不太一样建议在设计开始前就锁死Bank电压别到布局布线阶段才返工。另外速率上限也需要注意。普通的LVDS IO在某些系列上最高只能到1.25Gbps左右如果你要跑更高速率就得考虑GTP/GTX这类高速收发器那就是另一套玩法了。本文后面讲的代码和调试方法适用于一般的并行LVDS接口比如RGB转LVDS、ADC/DAC数据传输、相机sensor接口如果是SerDes高达数Gbps的链路还需要加更多CDR和均衡处理的逻辑。2. 调试前的准备工作硬件连接、时钟与关键约束2.1 硬件连接从原理图到焊盘LVDS接口的硬件连接核心就四个字差分等长。差分线必须走差分对也就是P和N两根信号在PCB上要相邻、平行而且长度尽量一致。等长的目的是保证P和N到达接收端的时刻基本一致减少共模噪声转化。具体做PCB的时候就算绕线也要把长度差控制在mil级别以内一般同组差分对内长度误差控制在5mil以内。还有终端匹配电阻。LVDS标准要求在接收端并接一个100欧姆电阻这个电阻把电流信号转成电压信号。有些FPGA芯片内部已经集成了可配的终端电阻但这个功能仅限于某些专门的IO并非所有Bank都支持。用外部电阻最稳妥100欧姆、精度1%尽量靠近接收端摆放。另外LVDS差分对的P/N极性千万不要搞反。我在实际项目中至少见过三次这个问题——PCB画板时两路网络名标反直接把数据全弄反了。设计完成后建议用万用表从FPGA引脚顺着走线到连接器端逐对确认P和N。2.2 时钟设计LVDS接口的命脉LVDS接口跑的是高速时钟设计做不好后面全白搭。先分清两类时钟串行时钟和并行时钟。串行时钟是IO口上输出的bit时钟频率等于线速率并行时钟是FPGA内部逻辑工作时钟频率等于串行时钟除以串并转换比。举个例子你要产生一个7:1的LVDS发送接口线速率700Mbps那串行时钟就是700MHz并行时钟就是100MHz。在FPGA内部生成这两个时钟一般用一个MMCM或PLL从同一个参考时钟分出两个频率倍数关系的时钟保证相位关系可预测。我在工程里习惯用Xilinx的MMCME2_ADV来做时钟生成输出两个时钟一个给OSERDES当串行时钟一个给用户逻辑当并行时钟。MMCM的相位对齐功能可以补偿时钟路径上的偏移确保OSERDES采并行数据时数据和时钟的建立保持时间有足够裕量。用不好MMCM最容易出现的问题是仿真全对一上板就随机出错原因就是并行时钟和串行时钟之间的相位关系没有约束好。2.3 引脚约束和时序约束怎么写FPGA工程里的XDC约束Xilinx或QSF约束Intel很多新手写得比较随意但对LVDS这种高速接口约束必须细化到引脚电平标准、差分对属性、时钟频率定义。引脚约束至少要包含三件事。第一管脚位置把信号锁定到具体引脚编号。第二电平标准设置为LVDS或LVDS_25这和Bank电压匹配。第三差分对属性如果工具不能自动识别P/N成对需要显式声明。比如Xilinx里只要把P端管脚约束成LVDSN端和P端在同一Bank里工具能自动关联但如果你的原理图里P/N命名不规范建议手动声明一下省得布局布线时报错。时序约束方面重中之重是定义好串行时钟频率。比如create_clock -name clk_ser -period 1.429 [get_pins mmcm_instance/CLKOUT0]如果用的是MMCM输出的串行时钟要约束MMCM的输入时钟然后让工具自动推导所有MMCM输出时钟的频率和相位关系。此外对OSERDES和ISERDES的数据路径工具本身会基于时钟频率自动分析时序但前提是约束文件里必须明确告诉工具这些时钟是多少。如果漏了约束工具默认按慢时钟优化布线出来的结果高速跑不稳这个坑我踩过。3. Verilog代码实战发送端和接收端完整实现3.1 发送端设计用OSERDES打包并行数据下面给出一个最基本的LVDS发送端例子基于Xilinx 7系列串并转换比7:1。module lvds_tx_7to1 ( input wire clk_ser, // 串行时钟如700MHz input wire clk_par, // 并行时钟如100MHz input wire rst_n, input wire [6:0] data_in, // 7bit并行数据 output wire lvds_p, output wire lvds_n ); wire ser_data; wire iob_data; // 例化OSERDESE27:1模式 OSERDESE2 #( .DATA_RATE_OQ (DDR), .DATA_RATE_TQ (BUF), .DATA_WIDTH (7), .SERDES_MODE (MASTER), .TRISTATE_WIDTH (1) ) oserdes_inst ( .OQ (ser_data), .OCE (1b1), .CLK (clk_ser), .CLKDIV (clk_par), .D1 (data_in[0]), .D2 (data_in[1]), .D3 (data_in[2]), .D4 (data_in[3]), .D5 (data_in[4]), .D6 (data_in[5]), .D7 (data_in[6]), .D8 (1b0), .T1 (1b0), .T2 (1b0), .T3 (1b0), .T4 (1b0), .TCE (1b1), .RST (~rst_n) ); // 差分输出缓冲器 OBUFDS #( .IOSTANDARD (LVDS_25) ) obufds_inst ( .O (lvds_p), .OB (lvds_n), .I (ser_data) ); endmodule这段代码的结构很清晰OSERDESE2负责把7位并行数据按bit顺序打成串行然后OBUFDS把单端信号转成差分信号输出。注意OSERDESE2的数据位映射顺序D1是第一个被送出去的bitD7是最后一个这和并串转换的顺序直接相关。有些参考设计里顺序反了导致收发两端bit顺序不一致数据全错。还需要说明的是上面的代码只展示了数据通道部分。实际工程中LVDS发送常常还需要一个同步的差分时钟输出也就是第4对差分线用来给接收端提供采样时钟。这个简单把并行时钟或串行时钟经过OBUFDS输出即可但要确认接收端需要的是bit时钟还是byte时钟不同接口协议要求不同最好对着接收端的datasheet来。3.2 接收端设计ISERDES加上Bitslip对齐接收端比发送端稍复杂一点。ISERDESE2负责把差分信号转成单端然后把高速串行bit按顺序存到并行寄存器里但这里有个关键问题接收端怎么知道bit的边界在哪里解决这个问题的标准方法是使用训练序列和对齐机制。发送端周期性发送一个特定的pattern比如0x7F或0x80接收端检测到pattern后通过Bitslip信号调整移位直到采样到的数据与预期pattern对齐。下面是接收端的核心代码module lvds_rx_7to1 ( input wire clk_ser, // 串行时钟与发送端同源或恢复时钟 input wire clk_par, // 并行时钟 input wire rst_n, input wire lvds_p, input wire lvds_n, output reg [6:0] data_out, output reg align_done ); wire ser_data; wire [6:0] iserdes_data; reg [6:0] align_data; // 差分输入缓冲器 IBUFDS #( .IOSTANDARD (LVDS_25) ) ibufds_inst ( .I (lvds_p), .IB (lvds_n), .O (ser_data) ); // 例化ISERDESE27:1模式 wire bitslip; reg bitslip_pulse; ISERDESE2 #( .DATA_RATE (DDR), .DATA_WIDTH (7), .SERDES_MODE (MASTER), .INTERFACE_TYPE(NETWORKING) ) iserdes_inst ( .Q1 (iserdes_data[0]), .Q2 (iserdes_data[1]), .Q3 (iserdes_data[2]), .Q4 (iserdes_data[3]), .Q5 (iserdes_data[4]), .Q6 (iserdes_data[5]), .Q7 (iserdes_data[6]), .Q8 (), .CLK (clk_ser), .CLKB (~clk_ser), .CLKDIV (clk_par), .OCE (1b1), .RST (~rst_n), .BITSLIP (bitslip_pulse), .D (ser_data), .DYNCLKSEL (1b0), .DYNCLKDIVSEL (1b0) ); // 对齐控制状态机没对齐时周期性发bitslip脉冲直到采到正确pattern reg [3:0] counter; reg [7:0] state; localparam IDLE 8d0; localparam WAIT_SLIP 8d1; localparam CHECK 8d2; always (posedge clk_par or negedge rst_n) begin if (!rst_n) begin state IDLE; bitslip_pulse 1b0; data_out 7d0; align_done 1b0; align_data 7d0; counter 4d0; end else begin case (state) IDLE: begin if (!align_done) begin state WAIT_SLIP; end end WAIT_SLIP: begin bitslip_pulse 1b1; state CHECK; counter 4d0; end CHECK: begin bitslip_pulse 1b0; if (iserdes_data 7b0111111 || iserdes_data 7b1000000) begin align_done 1b1; data_out iserdes_data; state IDLE; end else if (counter 4d10) begin counter counter 1b1; state WAIT_SLIP; end else begin state IDLE; end end default: state IDLE; endcase end end always (*) begin align_data align_done ? data_out : iserdes_data; end endmodule这个对齐状态机的逻辑是发送端周期发0x7F和0x80交替的pattern。接收端不断检查ISERDES输出的数据如果不是预期值就拉高bitslip脉冲让ISERDES移动一个bit位置再检查直到数据正确为止。Bitslip每触发一次ISERDES的输出就整体移一位所以对齐过程最多试7次就能找到正确边界7bit宽度。实际项目中对齐完成后数据输出还需要做一个延时补偿。因为bitslip操作会影响后续数据的相位所以一旦对齐成功后续数据输出需要从同一寄存器读取避免毛刺。上面代码里用align_done信号控制对齐以后直接锁存data_out就是这个目的。3.3 完整回环测试代码怎么组织调试LVDS接口第一步一定是回环测试。在FPGA内部把发送端的数据直接接到接收端或者用一根短跳线帽把板子上的TX差分对和RX差分对短接先确认逻辑正确再升级到外部回环。这里给一个简单的测试顶层思路module lvds_loopback_test ( input wire clk_50m, input wire rst_n, output wire lvds_tx_p, output wire lvds_tx_n, input wire lvds_rx_p, input wire lvds_rx_n, output wire [6:0] led_data, output wire led_align ); wire clk_ser; wire clk_par; // 用MMCM生成700MHz串行时钟和100MHz并行时钟 clk_gen clk_gen_inst ( .clk_in_50m (clk_50m), .clk_ser (clk_ser), .clk_par (clk_par), .locked () ); // 发送端发送递增数据 reg [6:0] tx_data; always (posedge clk_par or negedge rst_n) begin if (!rst_n) tx_data 7d0; else tx_data tx_data 1b1; end // 发送端同时周期发0x7F训练序列 reg [6:0] tx_pattern; wire [6:0] tx_mux (tx_data[5:4] 2b11) ? 7b0111111 : tx_data; lvds_tx_7to1 tx_inst ( .clk_ser (clk_ser), .clk_par (clk_par), .rst_n (rst_n), .data_in (tx_mux), .lvds_p (lvds_tx_p), .lvds_n (lvds_tx_n) ); // 接收端 wire [6:0] rx_data; wire align_ok; lvds_rx_7to1 rx_inst ( .clk_ser (clk_ser), .clk_par (clk_par), .rst_n (rst_n), .lvds_p (lvds_rx_p), .lvds_n (lvds_rx_n), .data_out (rx_data), .align_done (align_ok) ); assign led_align align_ok; assign led_data rx_data; endmodule这个测试工程的思路是发送端平时发递增数据每4个周期插入一个0x7F的training pattern接收端检测到pattern后完成对齐。对齐成功之后LED指示信号亮起来同时把接收到的数据送到LED显示。这样调试的时候一眼就能看出对齐有没有成功也能通过LED上数据的跳变判断收发是否一致。4. 调试流程与信号完整性分析4.1 回环测试的正确打开方式回环测试分三个层次建议按顺序做。第一层FPGA内部逻辑回环。把发送端的输出信号在FPGA内部直接接到接收端输入不需要经过IO口。这种测试纯粹验证逻辑功能不涉及实际PCB走线和信号完整性仿真波形对了基本就没问题。第二层板上外部回环。把FPGA的TX差分对通过PCB走线或跳线连接到RX差分对这种测试已经包含IO电平转换、PCB走线和终端匹配的影响是最基本的有意义测试。第三层跨板互联。两块板卡通过连接器、排线或同轴线互连这时的信号完整性问题最严重也是真正考验设计水平的层次。调试的时候一定按顺序来。我见过不少人一上来就搞跨板测试结果TX和RX两端都有问题排查起来剪不断理还乱。老老实实做内部回环逻辑确认无误再做外部回环能省很多事。4.2 眼图和误码率怎么判断信号质量好不好LVDS信号好不好示波器上看眼图最直观。把示波器探头接到接收端的差分引脚打开余辉模式把触发设成时钟边沿就能看到一串重叠的波形——像一只只眼睛。眼图张开越大说明信号质量越好。看眼图主要看三个指标。第一眼高判断电压裕量LVDS正常眼高应该有几百mV。第二眼宽判断时间裕量眼宽越宽说明采样时刻越充裕。第三抖动看波形交叉点的阴影阴影越宽抖动越大最容易造成误码。如果眼图闭合或者抖动明显可以从这几个方面找原因终端电阻是否焊接正确差分走线阻抗是否匹配到100欧姆地平面是否连续连接器质量是否可靠。误码率测试则是长时间跑随机数据统计收到的错误bit数量。最简单的实现是发送PRBS伪随机序列接收端也本地产生同样的PRBS序列两边对比不一致就计数。这个逻辑不复杂但很实用我经常把它做成一个上电自动测试的小模块长时间跑一晚上第二天看错误计数器有没有跳数。4.3 调试中的关键监控手段除了示波器和误码仪之外FPGA内部逻辑分析仪ILA/VIO/SignalTap是调试LVDS接口的利器。CHIPSCOPE或Vivado的逻辑分析仪可以直接观测ISERDES输出的并行数据对齐状态机的状态以及误码计数器的值。我用ILA调试的时候一般会抓这几个信号位对齐完成信号、ISERDES输出的并行数据、对比错误计数器的数值。如果对齐完成信号一直拉不起来说明训练序列和实际数据对不上优先检查发送端发的pattern和接收端检测的pattern是否一致如果对齐成功但误码计数持续增加问题多半在信号完整性上重点检查硬件。有位同事曾跟我分享过一个骚操作他用ILA把ISERDES的原始并行数据抓出来然后对照原理图手工算了一遍bit顺序发现收发两端的LSB/MSB定义不一致导致所有数据都倒序了。这个情况用常规检查很难发现但用ILA看波形对照数据手册逐bit核对问题就藏不住。5. 常见问题与排查技巧实录5.1 接收端完全收不到数据对齐永远失败这种情况我见过非常多排查顺序要讲究。先用示波器量接收端的差分引脚确认有没有波形进来。如果是跨板连接先量发送端板卡的连接器处再量接收端板卡的连接器处把故障定位到发送端、线缆、接收端三段中的某一段。最常见的原因有三个。第一发送端压根没输出。检查MMCM/PLL是否锁定如果PLL失锁说明时钟配置有问题多半是串行时钟频率超出MMCM工作范围。第二复位逻辑没处理好。LVDS这种高速电路上电复位时间要比普通逻辑更长有些FPGA的IO配置和MMCM锁定需要几百微妙复位撤除太早就会导致状态机跑飞。第三IBUFDS和OBUFDS的电平标准配置错误。比如PCB Bank电压是1.8V却配了LVDS_25这时候IO可能完全工作不了。5.2 数据对齐了但数据内容错误对齐成功后数据还是错的优先检查bit顺序。OSERDES和ISERDES的D1到D7映射顺序在不同厂商、不同系列里定义不完全一致。比如Xilinx的OSERDESE2数据进入FIFO的顺序是D1到D8但在考虑DDR采样时实际上是CLK上升沿采D1、D3、D5、D7下降沿采D2、D4、D6、D8。如果收发两端用的都是同一系列器件通常没什么问题但如果是FPGA转ASIC或者跨厂商互连bit顺序不匹配的概率相当高。排查方法很简单发送一个已知pattern比如0x01接收端抓到的数据如果变成了0x80说明bit整体反序了如果变成了0x02说明只是bit串行时序上移了一位用bitslip可以修正。这个技巧很实用能快速定位问题类型。5.3 高速跑起来随机出错频率降低就正常这种情况十有八九是时序余量不足。先看时序报告确认OSERDES/ISERDES相关路径的WNS最差负余量是不是负数。如果WNS在0附近甚至为负运行不稳定的概率非常大。解决办法有四种思路。第一优化代码减少组合逻辑级数让并行数据更早稳定下来。第二调整MMCM的相位偏移常常能直接救回来。第三检查PCB走线差分对等长是否真的做到了阻抗是否匹配。第四降低速率把线速率降下来看看是否稳定以此判断到底是时序问题还是信号完整性问题。5.4 上电偶尔正常偶尔不正常这类问题最折磨人通常和浮动引脚、初始化状态有关。我做过的项目里出现过一次上电后有时对齐成功有时失败最后查出来是复位信号在时钟稳定之前就释放了导致MMCM输出时钟还带毛刺时状态机就已经开始跑。解决办法是把复位置为高有效并由MMCM的locked信号参与控制locked为低时保持复位locked为高后再延迟几百个周期释放复位。还有一类上电偶发问题来自复位信号本身的亚稳态。FPGA设计里外部异步复位信号进入时钟域没有做两级同步就直接用很容易出现复位释放时刻刚好处于时钟上升沿附近的情况导致状态随机初始化。对LVDS这种高速接口来说复位同步这个细节在高速运行的严苛环境下会被无限放大我后面专门写了一段两级同步的复位处理建议直接抄走。reg rst_n_sync1, rst_n_sync2; always (posedge clk_par or negedge rst_n) begin if (!rst_n) begin rst_n_sync1 1b0; rst_n_sync2 1b0; end else begin rst_n_sync1 1b1; rst_n_sync2 rst_n_sync1; end end // 使用rst_n_sync2作为内部复位保证复位释放与时钟同步5.5 LVDS调试问题速查表现象优先排查方向解决方案完全无波形配置、时钟、电平标准查MMCM锁定、查复位时序、核对IOSTANDARD波形存在但无法对齐极性、数据顺序检查P/N是否接反检查pattern匹配对齐成功但数据错误bit映射顺序、训练序列发送已知pattern定位错位方向高速随机出错时序余量、信号完整性看时序报告、调MMCM相位、查终端电阻上电偶发失败复位同步、初始化状态加复位同步器、用locked控制复位释放最后再分享一个小技巧聊到这儿我特别想再把一个容易被忽略的细节拎出来强调一下。调试LVDS这种高速接口很多问题并不是逻辑错了而是约束不规范。Xilinx里如果引脚约束或时钟约束写得不完整工具不会报错但布线结果抗干扰能力会很差看起来什么都是对的一跑高速就出问题。我个人习惯是一上来先花一小时把约束文件理清楚把管脚电平标准、差分属性、时钟频率全部定义好再开始写逻辑。这样到调试阶段基本不会出现逻辑正常但硬件跑不过这种让人抓狂的情况。还有在PCB能改的前提下差分对离连接器越近越好终端电阻一定要靠近接收端放这两个点比想象中影响大得多。第一次做LVDS的朋友在这两个硬件细节上提前检查好后面软件调试会顺很多。
返回列表