
1. 项目缘起与整体设计思路屏幕直通这个需求最早来自我手上一个工业相机项目。前端是一颗图像传感器输出LVDS信号后端要接一块LCD屏做实时预览。中间原本用专用桥接芯片但芯片交期一拖再拖价格也翻了几倍于是我把目光转向了手边现成的Cyclone V开发板。用FPGA做LVDS收发把图像数据从一端搬到另一端逻辑上就是直通但真正动手才发现这里面涉及SerDes配置、时钟域处理、数据对齐、时序约束一整套东西任何一个环节没弄对屏幕上要么花屏要么干脆黑屏。这篇内容就是把我从零调通这套链路的完整过程整理出来。核心目标很明确在Cyclone V上配置LVDS SerDes IP接收前端LVDS图像数据经过必要的格式整理后再通过LVDS发送出去驱动屏幕显示。适合已经写过一些Verilog、对FPGA开发流程有基本了解但没怎么碰过高速SerDes的工程师参考。如果你正在做图像采集、屏幕驱动、多路LVDS桥接这类项目这套思路可以直接复用。先说清楚为什么选Cyclone V。这颗芯片在工业领域用量极大性价比高而且它的LVDS资源相当扎实——每个I/O bank支持真LVDS差分对最高速率能到800Mbps以上配合内部的SerDes硬核做视频直通绰绰有余。更关键的是Intel原Altera的LVDS SerDes IP成熟稳定配置界面清晰文档也全对新手相对友好。相比之下有些国产FPGA的SerDes IP文档偏薄遇到问题不好查第一次做LVDS的话Cyclone V是更稳妥的起点。整体方案我采用的是接收SerDes 数据整理 发送SerDes三段式结构。接收端把串行LVDS解成并行像素数据发送端再把并行数据串出去。中间的数据整理层负责处理位宽转换、时钟域 crossing、以及必要的像素格式调整。这个分层的好处是每一段职责单一调试时可以分段验证——先确认接收端数据对不对再确认发送端时序稳不稳最后联调。如果一上来就把所有逻辑揉在一起出了问题根本不知道是哪一段的锅。提示LVDS直通项目里最容易被低估的是时钟。接收端恢复出来的时钟和本地系统时钟往往不同源跨时钟域处理没做好数据会间歇性错位表现为屏幕偶尔闪一下或者局部花屏非常难查。这一点后面会专门展开。方案选型上还有几个取舍值得说明。第一用硬核SerDes还是软核逻辑Cyclone V的LVDS SerDes IP底层调用的是专用硬件不占通用逻辑资源速率和稳定性都远好于自己用普通I/O拼出来的收发逻辑所以毫不犹豫选IP。第二数据位宽选几位这取决于前端传感器的输出格式和屏幕的接收要求。我这边前端是4通道LVDS每通道串行化7位解串后每通道得到7bit4通道拼起来是28bit。这个位宽直接决定了后面FIFO和时钟的设计。第三要不要做帧缓存如果收发时钟完全同源理论上可以纯组合逻辑直通但实际项目中两端时钟总有偏差加一级异步FIFO做缓冲几乎是必须的否则一旦有微小频差累积几帧就会溢出或读空。2. LVDS SerDes IP核心配置细节拆解2.1 收发方向与通道数的确定打开Quartus的IP Catalog搜索ALTLVDS会看到两个IPALTLVDS_RX和ALTLVDS_TX。这两个要分别例化接收和发送是独立的。配置第一步是确定通道数Number of channels。我这边前端传感器是4对差分线所以RX通道数填4屏幕端也是4对TX同样填4。这里有个坑通道数不是随便填的它必须和你的硬件差分对数量严格对应填多了IP会占用不存在的引脚编译报错填少了数据不全。每个通道的串行化因子Deserialization factor是另一个关键参数。它表示一个串行位流被解成多少位并行数据。常见取值是7、8、10。为什么会有7这种奇怪的数字因为很多视频接口的位宽就是7bit每通道比如某些LVDS屏的RGB666格式每个颜色6位加1位控制正好7位。我这边前端输出就是7位所以RX的deserialization factor设为7TX的serialization factor也设为7两端保持一致。如果这里填错解出来的数据位序会完全乱掉屏幕上显示的就是彩色雪花。时钟方面IP需要一个高速串行时钟fast clock和一个低速并行时钟slow clock。以我这边为例串行速率是490Mbpsdeserialization factor是7那么并行时钟就是490/770MHz。这个70MHz就是解串后数据的同步时钟。IP配置界面里会让你指定这两个时钟的关系通常fast clock是slow clock的整数倍倍数就是deserialization factor。这个关系必须算准否则IP内部PLL锁不住。2.2 时钟资源与PLL的绑定Cyclone V的LVDS SerDes必须绑定到特定的PLL上不是随便哪个PLL都能驱动。每个I/O bank旁边有专用的LVDS PLL配置IP时Quartus会自动提示可用的PLL。我建议在配置前先看一下引脚规划确认你的差分对落在哪个bank然后用那个bank对应的PLL。如果PLL选错编译能过但下载后SerDes不工作示波器量差分线没有任何波形。具体操作上在ALTLVDS_RX的配置向导里有一个Clock页面会让你选择Use dedicated SERDES circuitry还是Use LVDS PLL。这里必须选dedicated因为只有专用电路才能跑到那么高的速率。然后指定参考时钟频率我这边板子上给LVDS PLL的参考时钟是50MHzIP内部会倍频到490MHz作为串行时钟。这个50MHz必须和实际晶振一致填错了PLL输出频率就偏接收端采样点会漂移。注意Cyclone V不同速度等级C6、C7、C8支持的LVDS最高速率不同。C8是最慢的做高速LVDS要特别留意。我手上这块是C6跑490Mbps很轻松但如果你用的是C8建议先查数据手册确认余量。2.3 数据对齐与字对齐模式LVDS接收最头疼的问题是字对齐word alignment。串行数据流进来FPGA怎么知道哪一位是一个字的开头ALTLVDS_RX提供了几种对齐模式手动对齐、自动对齐、以及基于训练序列的对齐。我这边前端传感器没有专门的训练序列所以用的是手动对齐配合bitslip信号。bitslip是一个很巧妙的设计。当发现解出来的数据错位时拉高bitslip一个周期IP会把解串窗口滑动一位相当于把数据流整体挪一格。反复拉bitslip直到数据对齐。实际操作中我会在接收端加一个简单的对齐检测逻辑比如前端数据里有个固定的同步头我这边是每行开头的几个固定字节检测到这个同步头就认为对齐了否则继续发bitslip。这个逻辑用状态机实现几十行Verilog就够。如果前端有训练序列那用自动对齐更省事IP内部会自动搜索训练模式并对齐。但要注意自动对齐需要配置训练序列的长度和内容配错了会一直对齐失败。我个人的经验是能用训练序列就用省去手写对齐逻辑没有的话手动bitslip也不复杂关键是同步头的选择要足够独特避免在正常数据里误触发。2.4 发送端的预加重与驱动强度TX端的配置相对简单但有两个参数影响信号质量预加重pre-emphasis和驱动强度VOD。预加重是在信号跳变时额外加一点过冲补偿传输线的高频衰减。如果屏幕离FPGA比较远超过10厘米或者走的是普通排线而非阻抗匹配的差分线建议把预加重打开档位选中等。驱动强度则决定差分摆幅标准LVDS是350mV如果接收端灵敏度低可以适当调高。这两个参数在IP配置界面里是下拉选项不用自己算。但我的建议是先用默认值板子跑起来后用示波器看眼图如果眼图闭合严重再调。盲目调高预加重反而会引入过冲导致接收端误判。我踩过一次坑为了保险把预加重拉满结果屏幕上出现规律性的竖条纹后来调回中等就正常了。3. 数据通路与Verilog实现要点3.1 接收端解串与数据拼接ALTLVDS_RX解出来的数据是按通道分开的。假设4个通道每个通道输出7bit那么IP会给出4组7bit数据外加一个并行时钟。我的处理方式是把这4组数据拼成一个28bit的宽数据方便后续处理。拼接顺序要和前端传感器的输出顺序对应这个顺序在传感器的数据手册里会写清楚通常是通道0对应最低位还是最高位一定要核对。// 4通道7bit拼接成28bit wire [6:0] rx_ch0, rx_ch1, rx_ch2, rx_ch3; wire [27:0] rx_data; assign rx_data {rx_ch3, rx_ch2, rx_ch1, rx_ch0};拼接本身是一行代码的事但顺序错了就是灾难。我建议在调试阶段先把每个通道单独接到LED或者SignalTap上确认每个通道的数据都在跳变再拼接。如果某个通道一直是0或者一直是1说明那对差分线没接好或者IP配置里通道数不对。3.2 跨时钟域FIFO的设计接收端的并行时钟我这边70MHz和发送端的并行时钟屏幕要求的时钟可能是65MHz或72MHz通常不同源。直接跨时钟域传数据亚稳态是必然的。所以中间必须加异步FIFO。我用的是Quartus自带的FIFO IP配置成异步时钟模式写时钟用RX并行时钟读时钟用TX并行时钟。FIFO深度怎么定这取决于两端时钟的频差和帧大小。假设频差是1%一帧有1024个像素那么一帧时间内累积的偏差大约是10个像素。为了留余量FIFO深度至少要是帧像素数的2%到5%。我这边一帧2048像素FIFO深度设了128实测很稳。如果频差更大或者帧更长深度要相应增加。深度不够的表现是屏幕周期性撕裂因为FIFO写满后数据被丢弃了。提示异步FIFO的读写使能要配合好。写端在RX数据有效时拉高wr_en读端在TX需要数据时拉高rd_en。如果读端读得太快FIFO会读空输出旧数据读得太慢会写满。可以用FIFO的empty和full标志做流控但视频流通常不能停所以更稳妥的做法是让读端略慢于写端保证FIFO始终有数据。3.3 发送端并串转换与时钟生成TX端的ALTLVDS_TX接收28bit并行数据内部自动串化成4路7bit的LVDS输出。这里需要注意的是TX的并行时钟必须由TX的LVDS PLL生成不能直接用RX的时钟。因为发送和接收的速率可能不同即使相同用同一个PLL驱动两个方向的SerDes也可能有扇出问题。我这边TX单独用一个PLL参考时钟同样是50MHz倍频到屏幕要求的串行速率。发送端的数据来源是FIFO的读端口。读出来的28bit直接接到TX IP的data端口。但要注意TX IP对数据的时序有要求通常需要在并行时钟的上升沿稳定。如果FIFO输出有组合逻辑延迟可能需要在中间加一级寄存器打拍。我一开始没加SignalTap看数据是对的但屏幕上偶尔有噪点后来加了一级寄存器就干净了。3.4 复位与锁定信号的正确处理LVDS SerDes IP有多个复位和锁定信号处理不好会导致IP不工作。RX端有rx_reset和rx_lockedTX端有tx_reset和tx_locked。正确的上电顺序是先给PLL复位等PLL锁定再给SerDes复位等SerDes锁定最后才让数据通路开始工作。如果复位顺序乱了SerDes可能锁不住或者锁定后数据错位。我的做法是用一个简单的状态机控制复位序列。上电后先等PLL的locked信号拉高延时一段时间比如1ms让时钟稳定然后释放SerDes复位再等SerDes的locked拉高最后释放数据通路的复位。这个延时不是必须的但加了之后稳定性明显提升。另外rx_locked和tx_locked要接到SignalTap或者LED上调试时一眼就能看出SerDes有没有工作。4. 时序约束与引脚分配的实战经验4.1 差分对的引脚约束Cyclone V的LVDS引脚不是随便分配的必须成对且落在支持LVDS的bank上。在Quartus的Pin Planner里差分对的正端和负端要分别指定负端通常用_n后缀。比如rx_ch0_p和rx_ch0_n。如果只指定了正端Quartus会自动分配负端但可能分到不支持的引脚上导致编译错误。更稳妥的做法是在QSF文件里手动写引脚约束。格式是set_location_assignment PIN_XX -to rx_ch0_p负端同理。写之前要查板子的原理图确认每对差分线连到FPGA的哪个引脚。我见过有人把差分对分到了普通I/O上结果编译能过但信号质量极差因为普通I/O没有LVDS的差分驱动能力。4.2 时序约束的编写LVDS SerDes的时序约束主要是对并行时钟和数据的约束。Quartus的TimeQuest里需要对RX的并行时钟创建时钟约束周期就是1/70MHz≈14.286ns。然后对RX数据到FIFO的路径做setup和hold约束。如果约束没写TimeQuest可能报时序违例但实际跑起来又是对的这种假违例会干扰判断。我的经验是先让Quartus自动生成SDC约束然后在此基础上补充。ALTLVDS IP在生成时会附带一个.sdc文件里面有时钟和输入输出延迟的约束模板直接拿来用就行。需要自己加的是跨时钟域路径的false path或者set_max_delay。异步FIFO的读写时钟之间要设false path因为本来就不需要满足时序关系设了反而会让工具做无用功。4.3 板级调试的信号观测板子回来之后第一件事不是下载程序看屏幕而是用示波器量差分线。先量TX端确认有差分波形摆幅在350mV左右共模电压在1.2V左右。如果TX没波形检查PLL有没有锁SerDes复位有没有释放。TX正常后再量RX端RX的波形来自前端传感器如果传感器没输出FPGA这边什么都收不到。SignalTap是调试LVDS的利器。把RX解串后的并行数据、FIFO的读写指针、locked信号都抓进去。触发条件可以设成locked下降沿这样一旦SerDes失锁就能抓到现场。我调的时候发现屏幕偶尔黑一下用SignalTap抓到rx_locked短暂拉低后来查出来是电源纹波太大导致PLL失锁加了个滤波电容就好了。5. 常见问题排查与避坑速查5.1 屏幕花屏或显示错位花屏是最常见的现象原因通常有三个字对齐没做好、数据拼接顺序错、或者FIFO读写时钟不匹配。排查顺序是先用SignalTap看RX解串后的数据如果数据本身是乱的那就是对齐问题手动发bitslip直到数据稳定。如果RX数据是对的但屏幕花那就是拼接顺序或者FIFO的问题。拼接顺序可以对照传感器手册逐位核对FIFO则检查读写时钟频率是否和预期一致。还有一种花屏是规律性的斜条纹这通常是时钟频差导致的。RX和TX的时钟如果有微小偏差FIFO会周期性溢出或读空表现为斜条纹缓慢移动。解决方法是让两端时钟同源或者加大FIFO深度并加入流控。我这边最终是把TX的PLL参考时钟和RX的参考时钟接到同一个晶振上频差消除条纹就没了。5.2 SerDes无法锁定rx_locked一直不拉高说明SerDes没收到有效的串行数据或者PLL没锁。先确认前端传感器有没有输出用示波器量差分线。如果有波形但locked不拉高检查IP配置里的串行速率和实际速率是否一致。速率填错PLL输出频率不对自然锁不住。另外参考时钟的频率也要核对50MHz的晶振不能填成25MHz。如果PLL本身就没锁检查参考时钟有没有送到PLL引脚。有些板子的晶振是给普通I/O的不是给专用时钟引脚的这种情况需要手动在QSF里把时钟引脚分配到CLKIN。Cyclone V的专用时钟引脚有特定编号查引脚手册就能找到。5.3 数据间歇性错误数据大部分时候对偶尔错几个像素这种问题最难查。常见原因是跨时钟域的亚稳态或者时序余量不足。先检查异步FIFO的读写指针有没有做格雷码转换如果没有指针在跨时钟域时可能采到中间值导致FIFO误判空满。Quartus的FIFO IP默认会做格雷码转换但如果是自己写的FIFO一定要手动加。另一个原因是时序约束没写全某些路径的建立时间不够。用TimeQuest跑一遍看有没有红色的违例路径。如果有先看是不是跨时钟域路径是的话设false path不是的话就要优化逻辑或者加流水线。我遇到过一次TX数据到IP的路径差0.2ns加了级寄存器就好了。5.4 常见问题速查表现象可能原因排查方法解决措施屏幕全黑TX无输出或PLL未锁示波器量TX差分线检查PLL参考时钟和复位序列彩色雪花字对齐失败SignalTap看RX数据手动发bitslip或配置训练序列规律斜条纹收发时钟频差对比两端时钟频率同源时钟或加大FIFO深度间歇性噪点跨时钟域亚稳态检查FIFO指针格雷码加格雷码转换或打拍局部花屏数据拼接顺序错对照手册核对位序调整拼接顺序SerDes失锁电源纹波或速率配置错量电源纹波和串行速率加滤波电容或修正速率注意调试LVDS时不要一上来就怀疑代码。先量硬件信号确认差分线有波形、电源干净、时钟稳定再去查逻辑。我见过太多人代码改了半天最后发现是排线接触不良。6. 实操心得与后续扩展方向这套LVDS直通链路我前后调了大概两周大部分时间花在字对齐和时钟域处理上。最大的体会是LVDS本身不难难的是细节。IP配置界面里的每一个参数都有它的道理填之前一定要想清楚为什么。比如deserialization factor它不是随便选的而是由前端数据格式决定的PLL的参考时钟频率必须和板子上的晶振一致。这些细节如果靠猜调试时间会成倍增加。另一个心得是善用SignalTap。LVDS是高速信号示波器只能看波形质量看不到数据内容。SignalTap能把解串后的并行数据抓出来对齐没对齐、数据对不对一目了然。我建议在设计的每个关键节点都留一个SignalTap探针调试阶段全打开稳定后再逐个关掉。后续如果想扩展有几个方向。一是加入图像处理比如在直通的基础上做缩放、裁剪或者色彩空间转换这需要在数据通路里插入处理模块注意处理好流水线延迟。二是支持多分辨率通过检测输入时序自动切换TX的配置这需要动态重配置PLLCyclone V支持PLL的动态重配但逻辑会复杂不少。三是把LVDS换成更高速的接口比如MIPI或者DisplayPort思路类似但SerDes的配置和协议层要重新做。最后分享一个小技巧如果手头没有LVDS信号源可以用FPGA自己产生一个测试图案通过TX发出去再用一根线把TX和RX短接做自环测试。这样可以在没有前端传感器的情况下验证整个收发链路。我一开始就是这么干的先确认TX和RX都能工作再接真实传感器省去了很多来回排查的麻烦。