ARTICLE DETAIL

资讯详情

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

FPGA与高速ADC的LVDS接口设计与调试全解析

FPGA与高速ADC的LVDS接口设计与调试全解析 做数据采集板卡的FPGA逻辑最容易被卡住的地方往往不是算法而是ADC和FPGA之间的那个LVDS接口。尤其是用Xilinx 7系列FPGA接一片250MSPS以上的高速ADCDDR模式下DCO跑在250MHz、每根线上500Mbps稍微一个细节没处理好ILA里抓到的就是一团乱码。这篇文章我把整个设计和调试过程掰开揉碎讲一遍覆盖接口选型、原理图阶段必须拍板的耦合方式、DCO/FCO时钟链路、ISERDESE2例化、Bitslip对齐、时序约束和随机误码排查给你一条能直接落地的路子。我拿一个典型项目举例14bit、250MSPS的ADCDDR LVDS输出7对数据线1对DCO位时钟1对FCO帧时钟。这个配置在无线通信、雷达中频采集、高精度示波器前端里非常常见你把它吃透了其他参数的ADC也基本是同样的套路。1. 为什么LVDS在这个ADC项目里比CMOS和JESD204B都合适先说个很多新手会忽略的计算。一片14bit、250MSPS的ADC采样数据率是14 × 250M 3.5Gbps。这个数据量摆在那里接口方式的选择空间其实没有想象中大。CMOS并行接口在这个速率下基本出局。14根数据线加1根时钟每根线要在250MHz下稳定翻转引脚数量倒是能接受问题在于同步开关噪声。单端信号在边缘速率很陡的时候地弹和串扰会让你在时序收敛上做大量无用功而且很多ADC的CMOS模式最高只能跑到200MSPS左右。就算ADC支持PCB Layout的代价也远超想象14根线要等长、要包地、要控制串扰得不偿失。JESD204B是另一条路但它有自己的门槛。JESD204B把多路并行数据变成高速串行lane一根线就能跑几Gbps在更高采样率、更多通道的场景下几乎是必选。但代价是链路层复杂需要8b/10b编码、需要多lane对齐、需要SYSREF和确定性延迟支持ADC和FPGA两侧都得支持这一整套协议。如果你只是做一块单通道250MSPS的采集板为了这个需求引入JESD204B设计和调试周期会明显变长。LVDS刚好卡在中间。差分信号天生抗共模噪声500Mbps的速率对7系列FPGA来说是轻负载引脚数量合理7对数据线加1对DCO、1对FCO总共9对差分对就搞定。调试手段也成熟ILA直接抓并行数据不需要额外的协议分析仪。对很多实际项目来说这个性价比是最高的。从FPGA侧来看Xilinx 7系列对LVDS的支持非常完善。HP Bank高性能Bank支持LVDS和LVDS_25电平标准配合ISERDESE2原语做DDR串并转换500Mbps这种速率从电气和逻辑上都留了充足裕量。你只要引脚规划时把LVDS接口全部放在HP Bank上后续逻辑设计基本不会遇到性能瓶颈。接口类型引脚开销14bit250MSPS单线速率逻辑复杂度调试难度适用场景CMOS并行14数据1时钟250Mbps低低低速、低分辨率LVDS7差分对2差分时钟500Mbps中中中高速采集JESD204B1~2高速lane数Gbps高高高速多通道2. 原理图阶段就必须定死的耦合、共模和端接方案很多人拿到ADC评估板原理图直接照着连从没想过为什么这里要放电容、那里为什么要加100Ω电阻。真到自己画板子的时候这些细节全变成坑。2.1 直流耦合还是交流耦合高速ADC的LVDS输出接口通常建议直流耦合。原因很简单ADC和FPGA之间是源同步关系数据信号和DCO时钟信号的直流路径应当一致。如果数据线做了交流耦合而DCO没做两者的共模电压在工作点可能不一致接收端容易出问题如果全部做交流耦合那DCO的低频分量也会被电容吃掉虽然LVDS信号是高频翻转但你不希望在直流恢复电路上增加额外的复杂度。我见过一些设计师为了让FPGA输入共模更“干净”在数据线上串了隔直电容结果低频测试模式比如ADC输出固定Pattern时数据全错。因为LVDS是电流驱动隔直电容在低频时阻抗很大差分电压大部分落在电容上到FPGA接收端的电压摆幅就不够了。所以我的经验是除非ADC手册明确说需要交流耦合否则一律直流耦合省事且可靠。2.2 共模电压不匹配怎么处理LVDS信号由电流源驱动经过100Ω终端电阻产生差分摆幅典型摆幅是350mV左右但不同ADC的共模输出电压差异不小有的标0.9V有的标1.2V。FPGA的LVDS接收器有一个共模输入范围7系列HP Bank的LVDS_25标准输入共模范围大致在0.3V到1.8V之间只要ADC输出共模落在这个区间内就没问题。关键点在于Bank电压。HP Bank用2.5V供电时选择LVDS_25用1.8V供电时选择LVDS标准这两种标准对共模范围的要求略有区别。如果ADC输出共模是1.2V两种都能兼容如果ADC手册里写共模只有0.9V建议优先选LVDS_25共模裕量更大。还有一种偷懒但有效的检查方法直接在原理图上把ADC的共模输出参数和FPGA所选电平标准的VCM范围列成一张表对照一遍再画板。2.3 100Ω终端电阻该放哪LVDS接收端必须端接100Ω差分电阻这个电阻要靠近FPGA引脚放置。很多7系列FPGA的HP Bank支持片内差分端接在约束里加一句DIFF_TERM。但我个人强烈建议外部再放一个100Ω电阻。原因有两点第一片内端接的实际阻值受工艺、温度影响精度不如外部电阻第二外部电阻靠近引脚放置能给信号回流提供更明确的路径减小端接不连续带来的反射。尤其在500Mbps这个速率下反射裕量本来就有限能多一分确定性就多一分。外部端接电阻的摆放距离建议控制在FPGA引脚200mil以内过孔不要太多。如果做阻抗仿真可以算一下不匹配的后果但实际上在PCB设计中做到差分阻抗100Ω±10%就够用了终端电阻用1%精度。整个链路里最容易出问题的是走线阻抗离散而不是端接电阻本身。注意不要把端接电阻放在FPGA的另一面、打过孔再连到引脚这样会造成stub高速下反射明显。端接电阻和FPGA引脚之间的连接线越短越好。3. 时钟链路DCO、FCO和采样时钟各自的去处高速ADC的LVDS接口是源同步设计——ADC把数据连同时间基准DCO一起送给FPGA。所以FPGA侧处理时钟的思路和处理普通系统时钟完全不同。你不能把DCO随便丢到PLL里绕一圈再用那样引入的抖动和延迟会毁掉整个采样。3.1 DCO从IBUFDS到BUFIO再到ISERDESDCO是位时钟频率等于采样率DDR模式下每个DCO周期有两个数据沿。在我的例子里DCO250MHz。这个时钟从引脚进来经过IBUFDS差分转单端后必须走BUFIO进入ISERDES的CLK和CLKB端口。BUFIO是专用IO时钟网络延迟小、抖动低专门用来驱动IO逻辑。这里最容易犯的错误是拿DCO去驱动普通逻辑或者用BUFG把DCO全局分发后再接ISERDES。这么做有几个问题一是延迟变大和数据的相对相位更难控制二是时钟到达ISERDES的时间和PCB走线决定的到达时间可能不匹配造成采样错误。正确做法是DCO进来后只走BUFIOISERDES的并行输出时钟CLKDIV则由BUFR产生。BUFR可以和BUFIO同源对BUFIO输出的时钟做分频。500Mbps的串行数据率配合1:4模式CLKDIV就是125MHz这个频率正好驱动后续的并行逻辑。3.2 CLKDIV到底由谁产生BUFR是对BUFIO输出分频的专用电路可以选择÷2、÷3、÷4等分频比。在ISERDESE2配置成DDR模式、DATA_WIDTH4时CLKDIV频率 DCO频率 / 2 125MHz所以BUFR设成÷2即可。这里有个细节BUFR的输出要接到所有ISERDES的CLKDIV端口。如果ISERDES比较分散跨了多个时钟区域你需要确认BUFR的驱动范围。一个BUFR最多驱动一个时钟区域内的IO逻辑如果LVDS接口跨了两个时钟区域就要用两个BUFR或者把IO约束在同一个时钟区域内。引脚规划阶段最好就把7对数据线加DCO、FCO尽量安排在同一个Bank、同一个时钟区域内这样BUFR分发最干净。3.3 FCO是干什么的怎么接FCOFrame Clock的作用是标识数据字的帧边界。在连续传输的LVDS数据流里FPGA需要知道8bit或14bit的分界在哪里。FCO通常和DCO有固定的相位关系频率可能是采样率分之一也可能等于DCO频率具体看ADC手册。很多人在FCO上翻车把它当成普通信号处理接到一个普通IO引脚再用寄存器采样。这种方法在低速下能用高速下容易因为相位裕量问题导致帧边界判断错误。我的建议是用和普通数据线一样的通道处理FCOIBUFDS后进ISERDES让FCO和数据一样被串并转换然后在并行域里检测FCO的上升沿或高电平位置作为数据对齐状态的参考。这样FCO和数据经过完全相同的延迟路径相对相位关系最稳定。3.4 采样时钟如果由FPGA提供要单独处理如果ADC的采样时钟由FPGA输出这个时钟不能从普通IO引脚随便给。ADC采样时钟的抖动会直接影响信噪比250MHz采样率下抖动哪怕只有几百飞秒SNR也会掉好几个dB。推荐用MMCM的专用时钟输出引脚或者是板级单独的低抖动时钟源。即使是FPGA输出也要考虑走线等长到ADC时钟输入引脚并且和DCO之间保持确定的延迟关系。大多数评估板会建议采样时钟由外部信号源提供调试时可以先用信号源排除FPGA时钟引入的抖动嫌疑。4. ISERDESE2接收逻辑原语例化、拼接与Bitslip硬件链路理清楚之后FPGA内部逻辑就是整个设计的核心了。Xilinx 7系列接收LVDS数据的关键原语是ISERDESE2它把串行数据转成并行数据。很多新手直接照抄网上的例程但对自己设计里的数据拼接关系并不清楚最后调不出来。4.1 IBUFDS ISERDESE2 原语组合每个LVDS数据lane需要IBUFDS把差分信号转成单端再送进ISERDESE2。一个典型的1:4 DDR模式例化如下// clk_dco: DCO经过IBUFDS后的单端时钟 // clk_dco_bufio: clk_dco经过BUFIO后的时钟 // clk_div: 经过BUFR分频后的并行时钟DCO/2 // data_p, data_n: 某一条LVDS差分数据线对 wire ddr_data; wire q1, q2, q3, q4; IBUFDS #( .DIFF_TERM(FALSE), // 外部有100Ω端接这里关闭片内端接 .IOSTANDARD(LVDS_25) ) ibufds_data_inst ( .I (data_p), .IB(data_n), .O (ddr_data) ); ISERDESE2 #( .DATA_RATE (DDR), .DATA_WIDTH (4), // 1:4模式 .INTERFACE_TYPE(NETWORKING), .NUM_CE (1), .INIT_Q1 (1b0), .INIT_Q2 (1b0), .INIT_Q3 (1b0), .INIT_Q4 (1b0), .SRVAL_Q1 (1b0), .SRVAL_Q2 (1b0), .SRVAL_Q3 (1b0), .SRVAL_Q4 (1b0) ) iserdes_data_inst ( .CLK (clk_dco_bufio), .CLKB (~clk_dco_bufio), .CLKDIV (clk_div), .RST (serdes_rst), .CE (1b1), .D (ddr_data), .Q1 (q1), .Q2 (q2), .Q3 (q3), .Q4 (q4), .BITSLIP (bitslip), .SHIFTOUT1 (), .SHIFTOUT2 (), .SHIFTIN1 (1b0), .SHIFTIN2 (1b0), .OCLK (1b0), .OCLKB (1b0), .CLKDIVP (), .O (), .DYNCLKSEL (1b0), .DYNCLKDIVSEL(1b0) );需要注意CLK和CLKB是互补时钟在DDR模式下ISERDESE2在CLK上升沿和CLKB上升沿也就是CLK下降沿分别采样。如果你的设计对延迟要求极其苛刻可以用OCLK配合专用逻辑做更精细的控制但对多数数据采集板CLK/CLKB标准接法就够用。4.2 1:4模式的并行数据拼接规则这是最容易懵的地方。ISERDESE2在1:4 DDR模式下每两个DCO周期从串行数据中提取4个bit输出到Q1到Q4。数据顺序是Q1最先对应第一个采样点Q2其次Q3再次Q4最后。这里的“先后”指的是时间顺序Q1是第一个DCO上升沿采到的bitQ2是紧接着下降沿的bitQ3是第二个DCO上升沿Q4是第二个DCO下降沿。对于14bit、7 lane的ADC假设lane0传输bit0和bit1lane1传输bit2和bit3依此类推。那么一个CLKDIV周期内每个lane的Q1、Q2合起来是第N个采样点的14bit数据Q1对应当前采样点的偶数bitQ2对应奇数bit具体哪个奇哪个偶要看ADC手册和相移设置但Q1、Q2属于同一个采样点这个关系是确定的每个lane的Q3、Q4合起来是第N1个采样点的14bit数据代码里拼接时7个lane的Q1拼成高位还是低位不需要死记因为最终对齐后bit的顺序可以由测试Pattern验证出来。你只需要约定一个顺序把7个lane的Q1按lane0到lane6的顺序拼成一个7bit向量Q2拼成另一个7bit向量然后 {Q1_lane0, Q1_lane1, ..., Q1_lane6, Q2_lane0, ...} 组合成14bit。如果发现高低位反了在代码里调换一下拼接顺序即可。小技巧为了减少后续调试的麻烦建议在代码里把每个lane的Q1~Q4和lane编号对应关系写成注释用表格贴出来这样回头查问题不用重新看手册算位序。4.3 Bitslip滑的是数据不是时钟Bitslip的作用是调整并行数据的边界。原因是DCO和数据的相位虽然由ADC保证但FPGA的ISERDES开始采样时无法预知采到的第一个bit落在数据字的哪一位上。可能采到bit2而不是bit0这时并行输出整体移位了。Bitslip就是用来纠正这个移位的——每给一个高脉冲ISERDESE2会把它内部并行输出循环右移或左移具体看配置相当于把Q1~Q4的数据边界滑动一个bit。Bitslip只能在CLKDIV域给并且脉冲至少维持一个CLKDIV周期。对齐状态机的控制逻辑比较简单// 简化版对齐状态机 localparam IDLE 2d0; localparam CHECK 2d1; localparam SLIP 2d2; reg [1:0] state; reg [5:0] slip_cnt; always (posedge clk_div or posedge rst) begin if (rst) begin state IDLE; slip_cnt 6d0; bitslip 1b0; end else begin case (state) IDLE: begin state CHECK; end CHECK: begin if (data_aligned) begin state CHECK; // 对齐成功保持 end else if (slip_cnt 6d10) begin slip_cnt slip_cnt 1b1; bitslip 1b1; state SLIP; end else begin state CHECK; // 尝试多次仍未对齐报错 end end SLIP: begin bitslip 1b0; state CHECK; end endcase end end几个经验点Bitslip一次只能移1bit所以需要循环尝试多次。对于1:4模式理论上最多移3次就能从一个完整的4bit序列的任意位置回到正确边界但实际因为多个lane的边界各自独立可能要单独给每个lane发不同次数的Bitslip。所以状态机里最好维护一个逐lane的slip计数不要所有lane共用一个计数器。对齐成功后要连续多帧检测不能只检测一帧就认为稳定。建议连续检测16帧或32帧都正确才进入锁定状态。如果Bitslip后数据反而更乱了不要慌先恢复复位再从头来。很多情况下是因为DCO的采样相位本身就不对Bitslip不能代替相位调整。5. 对齐验证与ILA实测判读逻辑写完综合布局布线跑完上板调试是真正的验证环节。这里的关键是先让ADC输出一个确定性的训练序列而不是直接上真实信号否则你根本分不清是数据错位还是信号有问题。5.1 训练序列怎么选ADC通常有测试模式常见的是输出交替的0x55/0xAA或者递增计数。递增计数比固定Pattern好用得多因为它不仅能判断是否对齐还能判断bit顺序是否颠倒。推荐优先用递增计数模式。具体操作是把ADC设置为输出从0开始递增的14bit计数FPGA接收端用ILA抓取并行数据。对齐时看到的应该是0, 1, 2, 3, ...连续递增如果看到2, 3, 0, 1这种说明边界偏移了2个bit如果看到0, 1, 4, 5这种可能是某个lane接反或者拼位顺序错误。5.2 ILA抓到的几种典型错误特征实测定式。我总结过几类常见波形现象可能原因处理方向数据整体错位比如看到递减或跳变Bitslip未做或次数不够执行Bitslip对齐只有某几个bit位置不对对应lane接反或拼接顺序错误检查PCB差分对和代码拼接数据大体正确但周期性跳变FCO对齐有误帧边界偏了半个字用FCO重做帧同步随机偶发错误时序裕量不足或信号完整性问题检查时序约束、端接和PCB走线数据全0或全1IBUFDS极性接反检查P/N是否接反这些特征都快成条件反射了看到ILA波形基本能判断出问题方向。其中“数据整体错位”是最常见的也是最容易处理的Bitslip跑一遍就好。5.3 逐lane对齐还是整体对齐实际项目里因为PCB走线长度差异、连接器引脚分配等原因不同lane的对齐状态可能不一样。如果所有lane统一发Bitslip可能出现某些lane已经对齐了某些还没对齐。更稳的做法是独立维护每个lane的对齐状态。每个lane的ISERDESE2都有自己的Bitslip端口FPGA逻辑可以分别检测每个lane输出的Pattern是否匹配只对没对齐的lane发Bitslip。这样即使在PCB走线长度差异比较大的情况下也能保证所有lane最终对齐。ILA调试时把状态机的对齐锁定信号、slip_cnt、fco_pos全部拉到ILA窗口里看。对齐过程是发生在复位之后的短暂状态ILA触发条件可以设为状态机进入CHECK状态或者trigger on未对齐信号这样每次复位后自动抓取对齐过程。6. 时序约束、PCB等长和随机误码的实战复盘逻辑功能调通之后最耗时间的其实是两类问题一个是时序约束写得不完整导致布局布线结果不理想另一个是板子上随机误码的排查。这两个问题往往是关联的。6.1 输入延迟约束的计算方法LVDS接口的时序约束核心就是set_input_delay。开发一个源同步输入接口你必须知道ADC的TcoDCO到数据的时钟到输出延迟和PCB走线延迟。ADC手册里会给Tco的min和maxPCB走线延迟用长度除以信号速度估算6mil/ps左右FR4大概每英寸170ps。约束写法示例create_clock -name adc_dco -period 4.000 [get_ports {dco_p}] # 假设Tco: min 0.5ns, max 1.5ns # PCB data走线比DCO走线长0.3ns设数据整体比时钟晚到 set_input_delay -clock adc_dco -max [expr 1.5 0.3] [get_ports {data_p[*]}] set_input_delay -clock adc_dco -min [expr 0.5 0.3] [get_ports {data_p[*]}]注意这里I useget_ports {data_p[*]}是因为set_input_delay定义的是FPGA引脚处的数据相对时钟的相位DCO引脚进来的时钟会自动被工具追踪到BUFIO/ISERDES时钟端。如果DCO是差分信号create_clock需要在差分端口的主端P端上创建。DDR模式下两个沿都要约束。用-clock_fall再加一组set_input_delay同时加-add_delay让两组约束叠加。这个细节漏掉工具对下降沿采样的时序分析就是乱猜。6.2 等长控制到底要控到多少PCB工程师经常问等长到底要控到多少我的实际建议是分三个层次差分对内等长±5mil以内这是基础要求直接决定差分信号质量必须满足。数据线与DCO之间数据线相对DCO的等长差建议控制在±50mil以内。500Mbps下50mil走线延迟大约8ps相比数据有效窗口大约800ps到1ns是很小的比例。控到±50mil能让FPGA接收端有充足的建立时间裕量。lane与lane之间的等长±100mil以内足够。不同lane之间即使有些许偏差最后靠每个lane独立的Bitslip和对齐都能处理掉。为了追求所有lane绝对等长把板子布线复杂度拉满纯属自讨苦吃。注意等长约束的前提是保证参考层完整差分对跨越分割参考平面带来的阻抗不连续比几mil的等长偏差严重得多。画板时要求FPGA下方的高速差分走线区域不要有镂空。6.3 随机误码的排查顺序随机误码是最难调的因为用ILA抓半天可能才抓到一次。我的排查顺序和经验如下。先看电源。ADC和FPGA的Bank供电如果共用一颗LDO瞬间动态电流可能把电压拉低导致LVDS输出摆幅变小或抖动增大。建议在靠近FPGA Bank电源引脚处加足够的高频去耦电容同时确认ADC的数字电源和模拟电源有没有按手册加磁珠隔离。如果随机误码集中在某个温度区间优先怀疑电源。再看时钟。把示波器探头放在DCO引脚上看上升沿有没有明显抖动。如果DCO是FPGA内部PLL提供的采样时钟分频而来检查PLL配置的抖动滤波带宽是否合适。很多时候随机误码就是DCO抖动超标数据和时钟的相位关系在边缘处摇摆时好时坏。然后是信号完整性。检查差分对参考层是否完整终端电阻是否靠近引脚有没有stub过孔。如果示波器能看到FPGA引脚处的眼图打开眼图看一下幅度和交叉点。LVDS眼图交叉点应该在50%左右如果明显偏移说明共模或差分摆幅有问题。最后是时序裕量。布局布线后在Vivado里看ISE/RDES路径的setup和hold slack。如果setup接近0说明PCB和约束条件已经紧贴设计极限。可以考虑在代码里加入IODELAY对数据做相位微调。动态训练方案的做法是在初始化阶段用MMCM的phase shift扫描最佳延迟值但这属于进阶玩法多数项目把FPGA引脚处的时序裕量通过等长和约束分析控好之后并不需要这层复杂度。我的经验是随机误码90%以上是电源或PCB布局问题而不是逻辑问题。真到逻辑层往往发生在约束写错导致时序分析结果不可信但功能上又能跑通的情况。所以遇到随机误码先别急着怀疑自己的Verilog代码先回头审查板子上的电源和解耦。复盘整个LVDS接口设计过程最大的体会是LVDS接口调试能否顺利很大程度上取决于原理图和PCB阶段有没有把共模、端接、等长这些基础工作做扎实。FPGA侧的逻辑和约束只要接口选型正确代码层是相对模板化的。反过来如果原理图阶段对耦合方式和共模范围拍板太随意后面所有调试都会变成在错误的地基上修补。希望这篇内容能帮你把这条链路一次理顺省下几个月的调试周期。
返回列表