拓冰建站拓冰建站
首页 / 资讯中心 / 正文

FPGA实战:7:1 LVDS源同步SerDes设计要点与踩坑记录

1. 从XAPP585开始为什么7:1 LVDS是绕不开的硬骨头前阵子做一块工业相机的采集板卡接口正好是7:1 LVDS分辨率1080p60。第一反应是“LVDS嘛差分对接收就行”结果真正调起来才发现事情完全不是“接两根线、打几个IO”那么简单。数据率一上来所有平时不在意的细节全部变成坑时钟怎么倍频、数据怎么解串、字边界怎么对齐、通道和通道之间怎么对齐、PCB上那几对差分线的等长误差到底该控多少——每个问题都能卡你两三天。后来翻到Xilinx的老牌应用笔记XAPP585标题就是“7:1 LVDS SerDes”这才算把整个链路学明白。这篇笔记写得早用的是Spartan-6时代的ISERDES/OSERDES原语但后面7系列、UltrasScale的底层思路几乎一点没变。直到今天你在ISE和Vivado里搜“LVDS 7:1”或者视频接口相关IP还是能追踪到XAPP585这套方法。可以说搞懂XAPP585就搞懂了FPGA上所有源同步串行接口的收法。这篇博客我不打算把XAPP585原文翻译一遍而是结合自己实际调板子的经历把7:1 LVDS源同步SerDes里最核心的设计方法和容易踩的坑梳理出来。适合谁看两类人一是刚接触LVDS接口、要在FPGA上做视频采集或显示驱动的硬件/FPGA工程师二是被某些带LVDS接口的工业主板、相机、屏幕折腾过想搞清楚“信号进来之后到底发生了什么”的嵌入式开发者。读完你至少能明白7:1到底是哪来的7、时钟倍频怎么设计、解串为什么要个“训练过程”、以及信号不稳定时第一步该查什么。关于XAPP585里给的结构我提炼成一个核心思想用一对随路时钟把低速并行数据在发送端并转串在接收端串转并整个链路靠同一个时钟源维持同步不需要专门的CDR时钟数据恢复电路。这个思想和PCIE、SATA那种嵌入式时钟SerDes完全不同它属于“源同步”机制代价是PCB布线必须保证数据和时钟严格等长好在代价对应的是FPGA内部实现非常简单普通IO资源就能跑。2. SerDes与源同步先把“7:1”这张皮揭掉2.1 7:1那个数字到底是哪来的很多做软件出身的同学第一次听到“7:1 LVDS”会很懵以为是一种“7倍速LVDS协议”其实根本不是。7:1指的是每个像素时钟周期里一条LVDS差分对上串行传输7个bit。为什么会是7因为在传统的RGB视频信号里一个像素包含R、G、B三个通道每个通道6bit或8bit加上HSYNC、VSYNC、DE这些控制信号。以24bit RGB为例RGB各8bit一共24个数据位加上3个控制位就是27bit。如果用3对LVDS来传27除以3等于9所以会有9:1的LVDS如果用4对LVDS数据线传24bit数据加3个控制位刚好每对传7bit这就是7:1的来历。你可以把7:1理解成“每通道位宽为7”的并行转串行比率而不是某种编码协议。实际用FPGA做1080p30这种分辨率时像素时钟约74.25MHz7:1串行之后每条差分线的数据率就是74.25×7519.75Mbps如果跑1080p60像素时钟148.5MHz数据率就是1.0395Gbps。这个速率对LVDS来说已经是比较极限的区间了对PCB走线和连接器质量也比较敏感。2.2 源同步和CDR SerDes的本质区别理解源同步可以对比一下嵌入式时钟方案。PCIE的发送端不会把时钟单独送出去数据编码里就隐含了时钟信息接收端要用CDR锁相环把时钟从数据流里“挤”出来。好处是少一对时钟线、没有时钟和数据之间的偏斜问题代价是实现非常复杂必须用专用的高速收发器没有普通IO什么事。LVDS的源同步则完全相反发送端单独发一对时钟LVDS数据和时钟是平行着一起走的。接收端拿这对时钟去采样数据即可不需要恢复时钟。这个做法的前提是时钟和数据必须同源同相在PCB上必须保证等长让时钟边沿到达接收端时数据仍然稳定。通俗点说CDR像是一个人听鼓点找节奏源同步则是鼓手把节拍器信号也一起递给你你照着节拍直接弹就行但前提是谱子和节拍器不能对不上。这个区别决定了7:1 LVDS在FPGA上的实现路径发送端负责把7bit并行数据串行发送出去接收端靠外部时钟把串行数据流按时间顺序切成7bit一组再通过字对齐把正确的位边界锁定。整个过程不需要PLL从数据里恢复时钟只需要一个PLL做倍频或者相移难度一下子低了一截。2.3 XAPP585里的双时钟结构XAPP585的接收端结构有一个关键细节它不直接用外部LVDS时钟去采数据而是先把外部时钟经过MMCM/PLL处理生成两个内部时钟一个是慢时钟像素时钟频率比如7倍分频也即74.25MHz用来给并行逻辑用另一个是快时钟7倍频比如519.75MHz用来驱动移位寄存器把DDR取回来的数据进一步并转串。为什么不能直接用外部时钟采样因为LVDS接收进来的时钟只是普通IO时钟无法直接作为FPGA内部全局时钟网络使用直接使用会带来很大的扇出和布线延迟不确定性。正确做法是把外部时钟送入专用的时钟输入引脚MRCC/SRCCMapped到全局时钟缓冲区域再经由MMCM的PLL重定时产生稳定的内部时钟树。XAPP585里还利用了MMCM的相位调整能力可以微调采样时钟相对数据的相位找到眼睛窗口最中间的位置。我自己调试时发现PLL倍频的选择要格外谨慎。比如像素时钟不是绝对的74.25MHz可能因为摄像头驱动芯片晶振精度原因有几十ppm偏差这个偏差问题不大因为发送端和接收端用的是同一对随路时钟像素时钟进来多快MMCM就能跟多快PLL的输入频率在Fpga允许范围内就能锁住。真正麻烦的是输入时钟的抖动如果送进来的LVDS时钟本身抖动太大MMCM输出的高速时钟会额外引入随机误码这种问题不加眼图仪几乎看不出来。2.4 位滑动BitSlip为什么必备解串之后FPGA拿到的是8bit或7bit的并行数据。但串行数据流里从哪一位开始算第一个bit在没有训练的情况下是随机的。比如物理上第1个bit可能是R0但解串器恰好是从R3开始截断的那么一整行图像都会错位表现就是满屏雪花、颜色错乱、或者画面完全不可解析。BitSlip就是解决这个问题的机制。它本质上是一种循环移位可以在并行域把解串后的数据左右移动1位重新选择字边界。这在XAPP585里对应的是“训练序列”过程发送端在消隐区间送出特定码型比如一串0101或特定字节接收端不断尝试BitSlip每次滑动后检查收到的码型是否匹配预期值如果不匹配就继续滑动直到完全对齐。这个训练逻辑如果自己从零写坑非常多。最常见的坑是接收端初始化顺序错误在链路还没有稳定的情况下就开始滑动导致怎么滑都对不上其次是训练码型选择不当用了周期性很短的重复码型比如0xAA和0x55这种只有两位重复的会产生多个“假匹配”位置有些时候滑到错误位置也看起来像对齐了。实践下来最好选那种具有足够唯一性的码型例如无符号的固定图像数据或者伪随机序列对齐成功后再加上持续监测机制运行中如果发现同步丢失要能自动重新进入训练流程。3. 时钟倍频与内部IO结构XAPP585是怎么把LVDS跑起来的3.1 时钟倍频链路设计现在仔细看一下时钟倍频设计。7:1模式下外部进来的像素时钟频率Fpix内部需要一个7倍频时钟Fser7×Fpix用于并转串和串转并。用户会看到Xilinx的DCM/PLL使用指南上通常建议一种做法在MMCM中输入Fpix输出FserBUFIO驱动IO逻辑再把Fser分频得到并行时钟Fpar建议用BUFR而不是全局BUFG。XAPP585里的方案不一样它建议同时使用两个时钟一个快时钟用于串行移位一个慢时钟用于并行数据这里有个关键取舍问题。为什么不用一个7倍频时钟打天下因为在IOB中的ISERDESE2/OSERDESE2原语串行端快时钟域和并行端慢时钟域天然就是两个时钟域。并行端的数据总线必须用慢时钟来同步如果强行用7倍频时钟去做并行数据的流水第一是Fmax不满足第二是综合工具会因为慢速逻辑挂在高速时钟网络上产生严重的时序违例。正确的思路永远是尽量让2个时钟域分工明确快时钟只管移动bit慢时钟管并行逻辑。表7:1模式下推荐时钟分配时钟频率作用驱动资源外部像素时钟Fpix源同步参考时钟IBUFDS→BUFG→MMCMMMCM输出快时钟7×Fpix驱动ISERDESE2的CLK引脚BUFIO高扇出IO时钟MMCM输出慢时钟Fpix并行数据流、状态机BUFR或BUFG相位调节可调保证采样点居中MMCM的CLKOUT相位这里我特别想提醒不要省略BUFIO/BUFR直接全走BUFG。早期的设计图省事快时钟全部走全局时钟网络BUFG结果跑到800Mbps以上就会出现数据采样错误。原因很简单BUFG的传输延迟大而且整个FPGA所有逻辑都挂在上面电源噪声和串扰会把快时钟边沿搞得乱七八糟。IO时钟专用网络BUFIO的延迟是专门针对IOB优化的只能驱动IO相关逻辑但延迟小、确定性好很适合这个任务。BUFR可以把快时钟分频出慢时钟并且分频后的时钟和原始时钟保持在同一个区域时延可控。3.2 ISERDESE2/OSERDESE2原语怎么接如果只记XAPP585里最核心的代码那一定是ISERDESE2和OSERDESE2的例化。7系列FPGA上ISERDESE2支持DDR模式下的8:1、10:1、14:1等比率可实现1:7解串。不过注意不是说原语支持7:1就可以直接接7根线出来。ISERDESE2在DDR模式下通常是1:8输出要得到7:1需要把并行输出宽度设为8bit然后只取其中7bit有效数据或者通过级联master/slave实现。更常见的做法是直接把ISERDESE2配成8:1让训练电路使用第8位作为边界标记这样逻辑更简单。一个很关键的细节是ISERDESE2的DATA_RATE参数必须设成“DDR”意味着时钟的上升沿和下降沿都采样。这很好理解7:1串行数据在外部的1个像素时钟周期里送7个bit外部时钟只有一对边沿不采用双沿采样就无法在1个时钟周期内取回7bit。DDR模式下ISERDESE2内部通过两个半速率支路I支路和Q支路先并行取出两组数据再合路成完整的并行总线。OSERDESE2发送端则正好相反并行总线送入慢时钟域OSERDESE2在快时钟域里按bit依次移出。实操中发送端的时钟分配和接收端完全对称外部像素时钟进MMCM快时钟通过BUFIO驱动OSERDESE2的CLK慢时钟作为并行数据侧时钟。我曾见过有人直接用通配符连接ISERDESE2的Q1~Q8把顺序搞错导致图像整体错位。这个顺序不是随便接的DDR输出时Q1对应的是第一个被采样的bitQ8是最后一个而且Q7、Q8在7:1场景下通常是无效位或者是边界位。因此并行数据总线的bit0应来自Q1还是Q8要看仿真不能想当然。最靠谱的验证方法是写个简单的仿真testbench输入一串已知pattern观察解串输出bit顺序对不对。3.3 训练模式为的是什么写软件的人总爱把“初始化序列”想得很简单发几个配置字就行。但LVDS链路的字对齐不是软件能直接操作的必须硬件逻辑配合。XAPP585里的做法是在训练阶段利用HSYNC/VSYNC/DE这些控制信号作为对齐参照。由于这些信号在消隐区间具有固定的电平特征训练逻辑可以尝试20种不同的BitSlip偏移每一种偏移下都连续检查若干行内的控制信号是否符合预期符合则锁定完成对齐。注意训练阶段的匹配判断不能只依赖单个像素时钟周期的瞬态值因为噪声很可能造成偶发错误。正确的做法是连续统计N个像素周期比如256个或1024个统计正确率超过99%才判定对齐成功。我在实际项目中曾经为了省资源把窗口设成32个周期结果在干扰大的工业环境里频繁失锁后来改成1024个周期稳定性好非常多。发送端在训练期间应持续发送训练码型。如果发送端是另外一个厂家的ASIC或者相机它可能在初始化时还没有输出有效视频数据甚至链路还没建立。此时需要在FPGA侧做“超时重训练”机制比如训练不成功就自动重置发送端配置、重新初始化相机。联调时这种异步握手最容易出问题我习惯在代码里预留足够多的寄存器给上位机方便查看当前训练状态、已尝试的BitSlip次数、以及当前错误率计数器排查起来一目了然。4. 实操过程从引脚约束到上板调试要点4.1 引脚分配与电平标准确认LVDS在FPGA里通常由工具自动识别差分对物理上需要一对相邻引脚P和N。先确认bank电压7系列HP bank支持1.8V的LVDS标准HR bank支持2.5V/3.3V的LVDS_25和LVPECL等标准。如果你的电路板上是3.3V供电的LVDS电平最好显式使用“LVDS_25”还是“LVDS”要严格查手册因为这两个标准的共模电压不同接错会导致接收端无法正确判定高低电平出现随机误码。工业相机常见的是LVDS_252.5V笔记本内屏常见的是LVDS1.8V或者更复杂的eDP必要时加电平转换芯片。另外现代FPGA支持内部差分端接。Xilinx的IBUFDS原语上有个DIFF_TERM属性设置为TRUE时内部自动接上100Ω差分端接电阻。对于高速LVDS信号100Ω差分端接必须存在否则信号在接收端会发生反射眼图恶化表现为数据出错率随线长、温度变化而波动。如果使用外部端接记得放在接收端靠近FPGA引脚的位置一般建议小于5mm。4.2 xdc约束中容易被忽视的细节一个常见的困惑是LVDS引脚约束到底要不要指定PACKAGE_PIN还是直接让工具自动分配我的建议是对于PCB已经定死的设计必须手动指定引脚对于自由度大的原型板可以自动分配但要用IO Planning视图核对差分对关系。xdc里简单的写法set_property PACKAGE_PIN AC11 [get_ports lvds_clk_p] set_property IOSTANDARD LVDS_25 [get_ports lvds_clk_p] set_property PACKAGE_PIN AD11 [get_ports lvds_clk_n] set_property IOSTANDARD LVDS_25 [get_ports lvds_clk_n] set_property PACKAGE_PIN AC12 [get_ports lvds_d0_p] set_property IOSTANDARD LVDS_25 [get_ports lvds_d0_p] set_property PACKAGE_PIN AD12 [get_ports lvds_d0_n] set_property IOSTANDARD LVDS_25 [get_ports lvds_d0_n]不要忘记在顶层使用差分缓冲器这样工具才会认为这两个管脚是差分对。Verilog里可以直接用原语IBUFDS/OBUFDS也可以依赖引脚约束自动推断。头一次做的时候很容易犯一个错只约束了P端、忘了N端结果工具报错“没有匹配的差分引脚”排查半天。如果你需要做时序约束尤其是跨时钟域和相位对齐可以考虑用set_clock_groups把PLL的两个输出时钟设成异步防止工具对两个时钟域做不合理的时序收敛。同时对输入延迟set_input_delay需要根据PCB走线等长和器件数据手册参数来填虽然很少有人认真填但不填在高速接口上综合结果往往更差。4.3 RTL实现要点这里写一个简化版的接收端状态机核心流程初始状态WAIT_PLL_LOCK等待MMCM的LOCKED信号拉高同时等待外部输入时钟稳定。然后进入TRAIN状态启动训练计数器每个时钟周期做一次BitSlip尝试然后进入CHECK状态。CHECK状态连续采样若干数据比对预期码型。如果全部符合则进入RUN状态如果出现不匹配则回退到TRAINbitslip一次再次检查。RUN状态正常传递并行数据同时用滑动窗口监控码型错误率。如果错误率超过阈值自动回到TRAIN重新对齐。实际的BitSlip信号时延和时序要求值得注意ISERDESE2原语的BITSLIP引脚是异步脉冲触发的但这个信号从慢时钟域到快时钟域属于跨时钟域需要做两级同步再打给BITSLIP。如果不做同步偶尔会出现BitSlip脉冲采样到亚稳态导致一次性滑动两三位训练结果飘忽不定。发送端则要比接收端简单很多。如果数据源来自DDR3或者异步FIFO记得在发送端加一个FIFO做跨时钟域缓冲防止上游数据突发时打乱OSERDESE2的发送节奏。更多的细节是如果7:1的7bit里包含DE、HSYNC、VSYNC等控制信号在消隐期间也要保持输出有效否则接收端训练状态机可能因为“缺失控制信号”而误判。4.4 多通道扩展与对齐如果你处理的是多对LVDS数据线比如4对数据1对时钟每对数据流独立完成BitSlip的情况下还需要做通道间对齐。XAPP585的标准做法是训练阶段发送端在每一组数据通道中插入相同的对齐码型接收端完成每通道字对齐后再评估通道间偏斜。偏斜来源主要是PCB上几对差分线长度差异、连接器接触、以及驱动器内部的微弱差异典型数值在几百ps到几个ns之间。如果偏斜在1个像素时钟以内可以通过FIFO调整如果更大需要检查PCB等长设计是否有问题。我调过一块偏斜比较恶劣的板子4通道中某条线在连接器处走了额外的分支导致比其他通道晚了近5ns几乎是半个像素周期。这种问题靠逻辑很难完全补救只能加FIFO做弹性缓冲并把允许的偏斜范围放大。但FIFO深度增大会引入帧延迟对于视频链路还能接受如果做工业控制类的同步采集就得认真控制线长差。一般7:1 LVDS的线长差建议控制在±5mm以内对应约±25ps基本无碍。5. 实测中的常见问题与排查方法5.1 丢同步和误码的排查顺序遇到LVDS画面出现雪花、闪屏或者偶发花块按下面这个顺序排查每步都确认之后再往下确认物理层连接差分P/N有没有接反示波器探一下两脚之间的摆幅是否正常一般LVDS为350mV差动摆幅共模电压是否在合理范围。检查端接电阻内部端接是否开启或者外部100Ω是否焊好。关掉端接观测信号波形可能会有明显振铃。检查时钟链路MMCM是否LOCKED外部像素时钟是否接入了正确的MRCC引脚快时钟频率是否正好是像素时钟的7倍可以用频率计在测试引脚上量。确认BitSlip训练逻辑是否工作看训练状态机的状态寄存器和错误计数器如果一直在重训练说明字对齐没有成功。如果对齐成功但运行中偶发失锁大概率是信号质量问题或者通道串扰用示波器看眼图、调MMCM相位而不是继续改逻辑。我用文字总结成一个速查表现象可能原因快速定位手段完全无数据没有时钟/端接错/引脚错示波器查LVDS时钟有无、PLL是否锁定画面整体雪花字对齐失败/训练没完成查看训练状态机、Bitslip计数画面错位/颜色乱数据位序错/字节映射错发送固定pattern对比仿真偶发花块信号质量差/通道偏斜大示波器测眼图、检查等长、调相位长时间后失锁温度/噪声导致相位漂移增加持续监测、周期性重训练5.2 调相位到底怎么调源同步接口的采样窗口来自MMCM的输出相位调整。Vivado里可以通过动态相位偏移Dynamic Phase Shift在运行中微调也可以在xdc里固定初始相位。核心思路很简单采样时钟的边沿必须落在数据稳定区间而不是数据跳变沿。怎么找最佳点当链路训练成功后连续改变相位值同时统计错误率。某个相位范围内错误率始终为0取这个区间的中间值作为最佳相位。这个动态调整逻辑我在项目里是通过AXI接口读写一个寄存器组、从软件触发多次扫描实现的。第一次扫的时候很痛苦因为每调一次相位需要复位BitSlip、重新训练整体耗时较长。后来优化成只在链路初始化时做扫描后续正常运行就锁定这个相位值。环境温度变化大的工业现场可以考虑设计定期重新扫描牺牲一小段时间的切换换长时间工作的可靠。5.3 内嵌训练逻辑要克制见过不少同事喜欢在训练模块里堆功能加了一堆调试探针、软复位、状态上报结果本身就成了不稳定因素。我的体会是训练逻辑越简单越可靠。核心逻辑只保留状态机、计数器和错误统计其余的寄存器读写、debug抓波全部用独立模块挂在总线后面互不干扰。另外有一个实操技巧上板调试时在发送端预先写死一个递增的数据流比如每7bit组里依次放0x01、0x02、0x03…接收端按字节对数据一眼就能看出是不是出现了位错。如果递增序列都能正确恢复说明链路正常如果数字是乱序的优先查BitSlip逻辑而不是查上层图像算法。这个技巧在排除“链路问题”和“业务逻辑问题”非常好用。5.4 强烈建议有条件就用集成IP如果你用的是相对较新的Vivado版本可以搜一下“LVDS Video”相关的IP或者参考设计它们内部已经封装好了完整的7:1解串、训练、对齐逻辑。和XAPP585相比IP方式是图形化配置支持更多的通道数和更丰富的调试点但缺点也很明显——IP屏蔽了底层细节出了问题黑盒难排查。我的建议是第一次做LVDS接口一定要手动用原语搭一遍把XAPP585吃透之后再用IP提高效率。跳过原理直接上IP调试时会非常被动。6. 与其他方案的横向对比反正都得懂底层FPGA软解串与专用LVDS解串器芯片市面上有专用的LVDS解串器/解串芯片比如常见的DS90CR288A28bit LVDS转并行、SN65LVDS93并行RGB转LVDS等等。它们的好处是简易输入输出全都是现成的并行总线不需要自己写SerDes逻辑。尤其适合应用逻辑非常简单的嵌入式设备一颗芯片解决接口转换问题。代价则是功能固定通道数、位宽、甚至训练序列都固化在芯片内部几乎不可配置。如果你的屏幕分辨率或信号格式稍有变化就得换芯片。而FPGA方案最大的灵活性在于无限可重构同一块板卡可以通过加载不同bitstream支持单通道7:1、多通道7:1、甚至其它比例。工业相机、医疗设备这类产品经常要面对多种传感器格式FPGA方案几乎成了唯一选择。与嵌入式时钟SerDes的边界还有一类容易混淆的方案是使用FPGA内部的高速收发器GTP/GTX/GTY这些收发器支持PCIE、SATA、10G以太网等串行协议是嵌入式时钟CDR方式。很多人想当然认为收发器性能更好想用它来跑LVDS。这里要明确一个底线LVDS是电气标准高速收发器是物理层收发通道两者不是一回事。如果信号源本身输出的就是LVDS电平的7:1串行数据用普通IO加XAPP585的方式才是正确的。硬要接进GTX还需要额外的电平转换和协议适配事倍功半。最后再说一个领域外但很常见的坑笔记本或主板说明书里提到的“LVDS内屏接口”很多是直接接屏的排线接口电平并不是标准LVDS有时是eDP转换后的信号有的直接用HSCL串行时钟配LVDS数据线。遇到这类接口先看屏端控制器的datasheet分清物理层信号类型再动手否则摩拳擦掌实现了半天接口根本对不上。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门