ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ISERDESE3在UltraScale下的LVDS接收:时钟、位序与调试要点

ISERDESE3在UltraScale下的LVDS接收:时钟、位序与调试要点 做FPGA的LVDS接收做到一定项目量后你会发现同一套接口逻辑在不同的Xilinx芯片系列里写法和用法差距比想象中大得多。老项目从7系列迁移到UltraScale上时我最先碰壁的不是LUT资源也不是DDR控制器而是SelectIO这一层原来调通的ISERDESE2接收代码直接搬到UltraScale工程里连例化都过不了。查了一圈UG572发现这个系列给你换成了ISERDESE3管脚、参数、时钟要求全都不一样。这篇就把ISERDESE3的接收侧用法讲透重点围绕LVDS数据接入、DDR模式下的串并转换、CLK与CLKDIV怎么给、以及为什么很多人第一次跑起来数据是错的适合正在调LVDS ADC采集、视频接口或者FPGA间高速串行传输的兄弟参考。1. 从7系列迁到UltraScaleISERDESE3为什么要另起炉灶1.1 新架构下接收原语的定位变化7系列里的ISERDESE2身兼数职SDR/DDR串并转换、BITSLIP位滑动、SHIFTIN/SHIFTOUT级联、可配置的并行宽度、内部时钟分频。到了UltraScaleXilinx把ISERDESE3砍成了一个专注“把串行位流按固定宽度展开”的硬模块很多过去依赖原语内部帮你做的事情现在都需要自己在逻辑里实现。第一次打开Vivado的Language Templates你会明显感觉到ISERDESE3的例化模板短了一大截。这不是Xilinx在偷懒而是UltraScale的IO架构朝ASIC风格靠拢的结果。IO逻辑内部的关键路径被缩短原语行为更固定布局布线后的时序也更可预测。代价就是灵活性下降你要的是更多控制权原语不再替你包办但换来的是更硬、更稳的时序表现。1.2 ISERDESE2与ISERDESE3能力对照把两个原语拉一张表差异一目了然。功能点ISERDESE27系列/Spartan-6ISERDESE3UltraScale/UltraScale最大并行宽度8可通过级联扩展更大宽度8DDR模式仅支持4/6/8SDR模式支持2~8BITSLIP支持不支持SHIFTIN/SHIFTOUT级联支持不支持内部时钟分频支持可自动产生CLKDIV不支持CLKDIV必须外部显式提供数据输入只有DD、DDLY、OFB三条通路动态CLKDIV反相无有DYN_CLKDIV_INVCLKB互补时钟输入有无DDR双沿采样在内部完成1.3 迁移时最容易忽略的“减法”很多老工程师从7系列迁到UltraScale第一反应是找BITSLIP找不到然后想用两个ISERDESE3拼成1:16的宽度发现没有SHIFT端口直接傻眼。这意味着过去为字节对齐写的bitslip逻辑、为扩展宽度做的级联全部得自己重建。另一个容易被忽略的点是时钟分频。ISERDESE2内部带分频逻辑CLKDIV可以由原语自己产生你只需要关心CLK速率。ISERDESE3把这个功能删了CLKDIV必须由外部时钟资源显式生成。听起来只是多接一根线的事实际上分频比怎么算、相位怎么对齐都是坑。很多人例化完不报错但跑起来数据对不上多半就出在这个环节。所以在做项目选型或者老代码移植前先对一下这个表能省掉后面一大半调试时间。2. ISERDESE3的管脚手册没那么长但每个脚都有讲究2.1 参数先定乾坤DATA_RATE与WIDTHISERDESE3的顶层参数一共有两个DATA_RATE和WIDTH。参数少不代表不重要这两个直接决定了外部时钟频率和内部采样行为。DATA_RATE可选SDR或DDR。SDR模式下每个CLK周期只在上升沿采一个bitDDR模式下CLK上升沿和下降沿都采样一个时钟周期采两个bit这也是高速LVDS最常用的模式。WIDTH是并行输出宽度。DDR模式支持4、6、8SDR模式支持2到8。WIDTH小于8时多余的Q输出管脚可以不连接。这里有个算时钟分频比的关键公式很多人容易算错并行字更新频率 串行bit速率 / WIDTHDDR模式下串行bit速率 CLK频率 × 2因此并行字更新频率 CLK × 2 / WIDTH。而CLKDIV频率应该等于并行字更新频率。举个例子DDR模式WIDTH8CLKDIV就是CLK的四分之一DDR模式WIDTH4CLKDIV就是CLK的二分之一DDR模式WIDTH6CLKDIV是CLK的三分之一。这个三分频比较难受用BUFGCE_DIV做不了只能靠MMCM或者PLL也是WIDTH6在工程里不常见的原因。2.2 三条数据通路D、DDLY、OFB怎么接ISERDESE3的数据输入有三个D、DDLY和OFB。D是直接数据输入最常用。从IBUFDS出来的单端信号可以直接接这里表示数据不经过任何额外延迟直接进串并转换核心。DDLY是延迟数据输入通常接IDELAYE3的DATAOUT。当系统需要精细调整每个通道的延迟来补偿PCB走线差、温度漂移或者线缆长度差异时数据先过IDELAYE3再从DDLY进入ISERDESE3。D和DDLY一般只用一个同时接两个没有实际意义。如果使用DDLY通路官方文档要求D接地。OFB是输出反馈路径用于把IO输出逻辑的数据反馈回接收端普通LVDS接收根本用不到固定接0即可。我第一次用这个原语时因为不清楚这三个脚的分工把IBUFDS的输出同时接在D和DDLY上仿真没问题上板后数据时不时错一位查了半天才发现是这里连接不规范导致的。2.3 时钟输入CLK与CLKDIV以及DYN_CLKDIV_INV的补偿逻辑ISERDESE3没有CLKBDDR模式的双沿采样在内部完成外部只需要提供CLK和CLKDIV。CLK是高速采样时钟频率等于串行数据速率除以2。CLKDIV是并行字时钟频率按上一节公式计算。CLK和CLKDIV必须同源并且CLKDIV的上升沿要和CLK的上升沿对齐这是整个原语能正确工作的前提。实际工程中如果CLKDIV相位没有对齐采出来的数据会呈现一种很典型的错乱并行字的某几位来自前一个采样字某几位来自当前采样字用ILA抓出来看就是“隔位错乱”。这个问题在7系列里不太明显因为ISERDESE2内部自动处理相位关系到了ISERDESE3分频时钟的相位完全由外部资源决定就变成了高频发问题。DYN_CLKDIV_INV就是为这个问题准备的。运行时把它拉高CLKDIV会在内部被反相相当于把整个并行字采样窗口移动半个CLK周期。当你发现数据有错位、又不想改动时钟树时先试一下把DYN_CLKDIV_INV从0改成1经常能救急。2.4 Q1~Q8位序问题的源头Q1到Q8是并行输出。官方时序图里有一句话很关键Q1对应最近一次被CLK边沿采到的bitQ8对应更早进入的bit。也就是说在一个完整的并行字更新窗口内最早进来的数据停留在Q8最新进来的数据在Q1。这句话决定了你拼接字节的方向。举个例子如果发送端按LSB-first发送bit0最先发出那么一个8bit窗口结束后bit0会落在Q8而不是Q1。如果你希望把恢复出来的字节按bit0放在最低位存就需要让Q8落到寄存器的bit0位置也就是写rx_byte {Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8};而不是反过来。位序问题是最容易让第一次用ISERDESE3的人抓狂的因为功能仿真大概率是对的上板之后才发现高位低位对不上。建议做板级调试时先发一个固定pattern比如递增斜坡0x00、0x01、0x02抓回来如果变成0x00、0x80、0x40说明位序反了调一下拼接顺序就好。3. 一个可复现的DDR LVDS接收示例八位ADC数据3.1 连接关系IBUFDS、IDELAYE3、ISERDESE3各管哪一段以最常见的八位DDR LVDS ADC接收为例。外部差分时钟经过IBUFDS转成单端时钟进入BUFG后分配为两路一路直接给ISERDESE3的CLK另一路经过BUFGCE_DIV四分频后给CLKDIV。差分数据同样先过IBUFDS转成单端后直接接ISERDESE3的D管脚。如果项目有眼图调整或者走线长度补偿需求数据路径中间要插入IDELAYE3IBUFDS输出接IDELAYE3的IDATAINIDELAYE3的DATAOUT接ISERDESE3的DDLY同时ISERDESE3的D接地。IDELAYE3的内容量比较大官方叫VTC窗口扫描后面单独开篇展开这里先给一张连接关系图式的描述方便你对照。3.2 完整Verilog例化代码下面给一个最小可运行的接收模块输入是一对差分时钟和一对差分数据输出一个8位并行字节时钟频率按200MHz DDR、WIDTH8为例。module lvds_rx_8bit_ddr ( input wire clk_p, input wire clk_n, input wire data_p, input wire data_n, input wire rst_n, output reg [7:0] rx_byte ); wire clk_int; wire clk_bufg; wire clk_div; wire data_from_pad; // 差分时钟接收 IBUFDS #( .DIFF_TERM(TRUE), .IOSTANDARD(LVDS) ) u_clk_ibufds ( .I (clk_p), .IB(clk_n), .O (clk_int) ); BUFG u_clk_bufg ( .I(clk_int), .O(clk_bufg) ); // DDR WIDTH8 时CLKDIV CLK / 4 BUFGCE_DIV #( .BUFGCE_DIVIDE(4), .CE_INVERT(FALSE) ) u_div ( .I(clk_bufg), .CE(1b1), .O(clk_div) ); // 差分数据接收 IBUFDS #( .DIFF_TERM(TRUE), .IOSTANDARD(LVDS) ) u_data_ibufds ( .I (data_p), .IB(data_n), .O (data_from_pad) ); wire q1, q2, q3, q4, q5, q6, q7, q8; reg rst_div; // 复位同步释放避免复位沿和clk_div沿竞争 always (posedge clk_div or negedge rst_n) begin if (!rst_n) rst_div 1b1; else rst_div 1b0; end ISERDESE3 #( .DATA_RATE(DDR), .WIDTH(8), .IS_CLK_INVERTED(1b0), .IS_CLKDIV_INVERTED(1b0), .IS_D_INVERTED(1b0), .IS_RST_INVERTED(1b0) ) u_iserdes ( .Q1(q1), .Q2(q2), .Q3(q3), .Q4(q4), .Q5(q5), .Q6(q6), .Q7(q7), .Q8(q8), .D(data_from_pad), .DDLY(1b0), .DYN_CLKDIV_INV(1b0), .OFB(1b0), .CLK(clk_bufg), .CLKDIV(clk_div), .RST(rst_div) ); // Q1是最近采到的bitQ8是最早采到的bit。 // 若发送端按LSB-firstbit0先发则让Q8落到rx_byte[0]。 always (posedge clk_div) begin if (rst_div) rx_byte 8d0; else rx_byte {Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8}; end endmodule3.3 时钟怎么生成BUFGCE_DIV / BUFIO的选择上面代码里CLK用的是BUFGCLKDIV用的是BUFGCE_DIV的4分频。这在200MHz左右、数据率400Mbps量级没有问题时序收敛相对容易。如果数据率跑到了1Gbps以上官方推荐CLK走BUFIO路径因为BUFIO是专门面向IO逻辑的时钟缓冲到ISERDESE3的延迟更短、更可控。BUFGCE_DIV则继续输出CLKDIV给并行逻辑。此时的时钟结构是IBUFDS输出后分两路一路进BUFIO接ISERDESE3的CLK另一路进BUFGCE_DIV分频后接CLKDIV。BUFIO的输出不能驱动普通逻辑所以CLKDIV必须由BUFGCE_DIV或MMCM提供两者从同一个源点出发架构上保证相位关系。3.4 位序组装与跨时钟域rx_byte到底该不该反过来代码里rx_byte {Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8}意思是Q1当高字节、Q8当低字节。这个拼接方向不是随手写的而是基于“发送端LSB-first”的假设。如果你的ADC芯片手册写明串行输出是MSB-first那拼接方向要反过来写成{Q8, Q7, Q6, Q5, Q4, Q3, Q2, Q1}。千万不要觉得“反正都叫Q1直接赋值就行”。LVDS接口没有固定标准谁先发谁后发完全由发送端芯片决定。我建议所有人在写代码前先查清楚发送端芯片手册里的“Output Data Mapping”或者“Serial Bit Order”章节再决定拼接方向。跨时钟域方面ISERDESE3的Q输出更新频率等于CLKDIV频率。如果你的后续逻辑跑在另一个系统时钟域不能直接用Q输出打一拍就完事。稳妥做法是加一个小异步FIFO深度8到16就够CLKDIV侧写入系统时钟侧读出。4. XDC约束与CLKDIV相位相关坑位4.1 input delay的计算和写法LVDS是源同步接口时钟和数据一起从发送端送过来时序约束的核心是告诉工具数据相对于时钟的窗口位置。端口约束时只约束差分对的正端。create_clock -name clk_rx -period 5.000 [get_ports clk_p] set_input_delay -clock clk_rx -max 1.200 [get_ports {data_p}] set_input_delay -clock clk_rx -min 0.100 [get_ports {data_p}] set_input_delay -clock clk_rx -max 1.200 -clock_fall -add_delay [get_ports {data_p}] set_input_delay -clock clk_rx -min 0.100 -clock_fall -add_delay [get_ports {data_p}]input delay的值不是拍脑袋写的它来自发送端芯片的Tco和PCB走线延迟差。公式是input_delay_max Tco_max Tdata_max - Tclk_mininput_delay_min Tco_min Tdata_min - Tclk_max比如发送端Tco范围0.4到1.0ns时钟线比数据线长导致时钟到达晚0.2ns数据线之间最大最小差0.1ns那么max 1.0 0.2 - 0 1.2nsmin 0.4 - 0.2 - 0.1 0.1ns写出来就是上面那组值。注意我不是让你直接抄这组数每个板子的PCB走线都不一样必须根据实际情况算。4.2 CLKDIV相位不对时现象与DYN_CLKDIV_INV补救CLKDIV和CLK相位没对齐最典型的现象是串行输入的是一个连续递增斜坡但抓回来的并行字节周期性出现重复或丢失。比如期望0x00、0x01、0x02、0x03实际看到0x00、0x00、0x01、0x03。排查步骤先确认分频比对不对比如DDR WIDTH8BUFGCE_DIV的DIVIDE设成几。很多人设成2觉得DDR就是2分频结果错了一半。分频比没问题再看相位这时候把s_iserdes的DYN_CLKDIV_INV从0改成1看看错乱是否跟着翻转。如果翻转后数据对了说明分频后的CLKDIV上升沿离采样窗口边界太近DYN_CLKDIV_INV相当于把窗口挪了半个CLK周期。这个方法在调试现场很有用因为你不可能为每次试验重新布线DYN_CLKDIV_INV是运行时动态可调的。4.3 没有BITSLIP字对齐自己做要几个步骤ISERDESE3没有BITSLIP意味着协议类接口的字对齐逻辑要自己写。思路不复杂把ISERDESE3输出的8位字不断流入一个16位移位寄存器。在移位寄存器中搜索同步字比如K28.5或固定训练pattern。找到后记录当前对齐位置后续并行字以这个位置为基准截取8位而不是直接取Q1到Q8。如果链路出现连续误码导致失锁重新进入搜索状态。reg [15:0] shifter; always (posedge clk_div) begin shifter {shifter[7:0], q_rx}; end always (posedge clk_div) begin if (shifter[15:8] 8hBC) // 检测到同步字 aligned_byte shifter[14:7]; // 以同步字为基准滑窗取数 end这里q_rx是ISERDESE3的Q输出拼成的并行字节shifter每拍移入一个字节滑动搜索。注意这和BITSLIP效果不完全一样BITSLIP是在串行位流上逐bit滑动而在CLKDIV域做滑动搜索只能按字节滑动粒度不同。真正的逐bit对齐需要在更高层次配合发送端的训练序列来做这个话题后面单独写一篇也不一定收得住。5. 调试复盘我在实测中踩过的几个典型坑5.1 复位没跑干净Q输出一片乱第一次上板时我图省事把复位信号直接接到ISERDESE3的RST上没有做同步处理。结果抓ILA发现每次上电后前几个并行字完全对不上过一阵子才稳定。问题出在复位释放时刻落在CLKDIV上升沿附近内部寄存器进入亚稳态。后来改成CLKDIV域同步释放具体代码就是示例里的rst_div那段。这不算什么高深技巧但代价是实实在在的调试时间。另外注意ISERDESE3的RST是高有效模型里写rst_div为1时复位千万别搞反。5.2 同样的代码换板卡后数据隔位错乱代码在A板卡上跑得好好的换到B板卡开始隔位错乱。查了半天最后定位到B板卡的PCB Layout里CLK差分对比数据差分对长了一大截导致CLK到达ISERDESE3的时间晚于数据采样点逼近数据窗口边缘。这种问题靠改RTL解决不了要么改PCB要么在数据路径上插入IDELAYE3做延迟补偿。IDELAYE3的延迟值可以精细调节调到采样点位于数据眼图的中间位置。这也是为什么很多高速接口设计直接把IDELAYE3当成标配而不是可选项。5.3 ILA抓出来的数据不要直接信先看采样点调试LVDS时ILA的采样时钟必须和CLKDIV同频同相否则抓出来的数据本身就可能错位。有一次我图方便用系统时钟去采CLKDIV域的信号ILA显示出来的波形毛毛躁躁看起来像毛刺其实是采样时钟和数据更新沿不同步。ILA的采样时钟最好直接用CLKDIV或者用一个和CLKDIV相位关系确定的时钟。另外Vivado的ILA在UltraScale上采样深度足够大时组合逻辑的亚稳态会被工具自动处理但前提是约束正确。先看Timing Report里有没有关于CLKDIV路径的违规再决定要不要抓波形。5.4 想要眼图IDELAYE3是后话ISERDESE3本身不带眼图扫描功能眼图扫描要配合IDELAYE3的延迟扫描和误码统计来做。具体做法是把IDELAYE3的延迟值从最小扫到最大每个延迟值统计一段固定时间内的误码数误码率最低的那个延迟区间就是最佳采样点。这个流程在工程里叫VTC窗口扫描官方也有对应的参考设计。这也是为什么我说ISERDESE3本身不复杂复杂的是它和IDELAYE3、时钟树、位序拼接配合起来的整个接收链路。这篇先把这个原语的管脚、参数和基本用法踩实下一篇我会把IDELAYE3的动态延迟校准和VTC扫描展开讲那才是高速LVDS项目里真正拉开差距的地方。
返回列表