ARTICLE DETAIL

资讯详情

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

FPGA开发者必看:LVDS接口Camera Sensor数据采集全流程

FPGA开发者必看:LVDS接口Camera Sensor数据采集全流程 FPGA开发者必看手把手实现LVDS接口的Camera Sensor数据采集1. 翻开Sensor数据手册前先搞清楚LVDS到底在传什么做FPGA图像采集的兄弟应该都有同感拿到一块Camera Sensor板子发现接口是LVDS而不是MIPI或者DVP时心情基本是半喜半忧。喜的是LVDS在全世界的FPGA开发板上几乎是标配原生支持好不用像MIPI那样费劲巴拉地找原语或者IP忧的是LVDS在Camera Sensor场景里往往不是标准的一路差分走线传一串高速数据那么简单里面还有sub-LVDS、HiSPi这种变种协议加上多通道、多lane的时序关系第一次接触很容易被数据手册里的波形图绕晕。我最早接触LVDS Camera数据采集是在一个工业检测项目上Sensor输出4通道sub-LVDS每通道720Mbps左右的有效数据率FPGA端要同步接收、拼接、做行场对齐再送进ISP做后续处理。当时踩了一堆坑从原理图端接电阻选型到FPGA内部IO约束再到解串后的bit对齐前前后后折腾了两周。这篇文章把我那段时间的完整实现路径、代码思路、调试方法全部整理出来给正准备动手做LVDS Camera采集的FPGA开发者一条可以直接参考的路线。先说清楚一个核心概念LVDS在Camera传输里物理层是标准差分信号但协议层五花八门。最常见的三类标准LVDS接口Sensor把并行数据经过内部Serializer变成串行差分对一般带一个像素时钟同步信号接收端用时钟沿采样数据即可。常见于VGA、720p分辨率的工业Sensor。sub-LVDS接口信号摆幅比标准LVDS小通常只有400mV左右甚至更低省电、抗干扰依然不错常见于手机CIS、车载摄像头。数据没有随路时钟接收端需要从数据流里恢复时钟或者用PLL对参考时钟倍频作为采样时钟。HiSPi接口Aptina现在On Semi推的一种高速串行协议在sub-LVDS物理层之上定义了8B/10B编码和包结构逻辑上像串行数据包。不管哪种类型接收端FPGA要干的活有三件把差分信号变成单端信号、把串行bit流恢复出来、把bit重新拼成像素数据并还原帧结构。这篇文章以最常见的标准LVDS和sub-LVDS为主线按完整项目流程来讲。2. 硬件底子原理图上的端接、供电与IO标准选择直接决定能不能收到数据很多人在FPGA里调LVDS收不到数据第一反应是代码写得有问题。但我遇到的案例里至少有三成问题出在硬件设计上。这不是危言耸听LVDS是模拟特性很强的接口差分阻抗、终端电阻、共模电压匹配随便哪一项不对波形都会劣化到采样失败。2.1 终端电阻不是随便焊一个100欧姆就完事LVDS规范要求接收端在差分线之间跨接100欧姆终端电阻用于匹配传输线阻抗、吸收反射。但实际画板子时要考虑这个电阻放哪里。Sensor到FPGA距离短比如同板走线5cm以内放在靠近FPGA引脚处如果是板间FPC连接终端电阻放在接收端连接器附近更合理能减少FPC带来的反射。还有一点很多人忽略FPGA的HP/HRL bank内部自带了可编程差分终端只要软件里使能DIFF_TERM就不用外部焊电阻。我就碰到过一次外部已经焊了100欧代码里又开了内部终端等于差分线上并联了50欧信号幅度直接压低一半导致误码。这个在设计时要提前确定外部端接和内部端接二选一。2.2 供电与共模电压的范围核对LVDS要求接收端能容忍一定范围的共模电压标准LVDS是1.2V左右sub-LVDS通常0.9V到1.8V不等。FPGA的IO bank如果没有独立的VCCO差分参考或者VCCO电平不匹配Sensor的输出共模接收端静态工作点会偏移。具体表现就是数据在温度变化或者走线长度差异下时好时坏极难排查。我建议在选型阶段就核对三张表Sensor数据手册里的差分输出电压幅值、共模电压范围FPGA HR bank的差分输入共模范围Xilinx 7系通常0.3V到1.5V左右具体见DS182两者的交叉区间是否覆盖全温度范围如果Sensor共模电压和FPGA容限刚好卡边界尽量选共模范围宽的FPGA或者加AC耦合电容做电平适配。AC耦合虽然简单但会引入直流偏移需要保证信号速率不至于使耦合电容形成低通截止一般0.1uF在几Mbps到1Gbps范围内问题不大。2.3 差分走线的PCB检查点LVDS在PCB上的要求不算苛刻但有几个点容易翻车差分对内等长等长差控制在走线总长的1%以内理想是50mil以内。不等长会导致接收端采样时建立/保持时间余量不对称。差分对间等长多通道同时传数据时lane与lane之间也要匹配长度否则通道间skew过大拼接数据时需要对偏。尽量避免过孔一个过孔会引入约0.5~1pF寄生电容破坏差分阻抗连续性。实在躲不开就成对打孔并在附近加回流地孔。参考平面完整LVDS走线下方的GND平面不能有长条开槽否则回流路径增大EMI和误码同时恶化。这几点看着基础但好多项目就是栽在好像没问题上。板子回来第一次上电前先拿万用表量一下差分对之间有没有短路再查一遍端接电阻位置比反复改FPGA代码有效得多。3. FPGA内部实现第一步从差分引脚到串行比特流的正确姿势硬件没问题之后才进入逻辑设计。LVDS接收在FPGA内部拆开看无非是差分缓冲 时钟管理 解串器这套组合。但不同厂商的FPGA原语名称和用法差别很大这里以Xilinx 7系和Intel Cyclone V两个主流平台为例把实现思路讲透。3.1 单端-差分转换原语IBUFDS是起点无论是标准LVDS还是sub-LVDS第一步一定是从物理引脚进IO bufferIBUFDS #( .DIFF_TERM(TRUE), // 使用内部差分终端 .IBUF_LOW_PWR(FALSE) // 高速应用建议关闭低功耗模式 ) u_ibufds_lane0 ( .I (sensor_lvds_p[0]), .IB(sensor_lvds_n[0]), .O (sensor_lvds_single[0]) );这里有个细节IBUFDS的输出是单端、3.3V/2.5V域内部信号在7系FPGA里它可以直接连到后面的ISERDESE2或者BUFIO。但如果你要用这个信号做普通逻辑采样比如低速调试模式就必须经过BUFG绕到全局时钟网络否则时序完全跑不起来。Diff信号进来之后根据Sensor数据手册的接口速率来决定下一步结构低速 100Mbps直接用BUFG 上升沿采样不一定非要解串器中高速100Mbps ~ 800Mbps用IDELAYE2 ISERDESE2走DDR解串时钟用BUFIO超高速 800Mbps考虑ISERDESE2的8:1模式或者更高速的收发器如果是标准LVDS对一般到1Gbps以内3.2 时钟恢复问题有随路时钟和没有随路时钟是两套做法这是LVDS Camera接收最核心的分水岭也是新手最容易卡住的地方。情况一Sensor提供随路时钟如标准LVDS数据接口这种最简单。Sensor输出一路像素时钟比如25MHz/50MHz/74.25MHz和数据lane数据经过解串后输出并行像素。FPGA端用随路时钟做采样时钟但是有一个麻烦随路时钟是单端信号还是差分信号接口上经常是Tclk/-差分对。所以接收路径上除了数据lane需要IBUFDS时钟lane也要IBUFDS然后如果解串比是1:7比如7:1需要把像素时钟倍频成串行比特时钟Xilinx推荐用MMCM/PLL从随路时钟生成CLK_DIV和高速采样时钟高速采样时钟走BUFIO驱动ISERDESE2的CLK分频时钟走BUFG驱动并行数据侧逻辑情况二没有随路时钟如大多数sub-LVDS Sensor这种就要靠参考时钟PLL/MMCM自己恢复采样时钟。比如Sensor输入Master Clock是24MHzPLL内部90度相移后倍频到数据率的7倍或14倍作为采样时钟。因为没有随路时钟同步所以时钟相位和数据相位之间的关系不是绝对的需要动态调节。动态调节的手段就是IDELAYE2。Xilinx的LVDS接收普遍做法是IDELAYCTRL给IDELAYE2提供参考时钟IDELAYE2的tap值可以通过端口动态调整每个tap约78ps具体取决于参考时钟频率用ILA观察对齐状态、或者用对齐状态机自动扫描tap值找到最佳采样窗口我之前项目里就是用了一个简单的眼图扫描状态机上电后从tap0开始每步调整一次IDELAY同时检查接收到的同步码是否正确把正确的tap区间记下来最终取区间中间位置。这个过程在工业级产品上很实用既能对抗温度漂移也能容纳PCB制造误差。3.3 ISERDESE2解串器7:1还是8:1取决于你的数据格式标准LVDS Camera接口最常见的数据格式是一个时钟周期传7个bit7:1串行化也有些Sensor是10:1或者8:1。Xilinx ISERDESE2原生支持DDR模式下的4、6、7、8、10位解串不同系列支持范围略有差异。7:1的经典配置代码如下ISERDESE2 #( .DATA_RATE(DDR), // DDR 模式上下沿都采样 .DATA_WIDTH(7), // 7:1解串 .INTERFACE_TYPE(NETWORKING), // 使用IDELAY或外部时钟 .DYN_CLKDIV_INV_EN(FALSE), .NUM_CE(1), .OFB_USED(FALSE) ) u_iserdes_lane0 ( .D (sensor_lvds_single[0]), .DDLY (sensor_lvds_delayed[0]), // 来自IDELAYE2 .CLK (serdes_clk), // 高速采样时钟BUFIO驱动 .CLKB (serdes_clkb), // 反相时钟 .CLKDIV (serdes_clkdiv),// 分频后的并行时钟 .RST (rst_n), .Q1 (q1), .Q2(q2), .Q3(q3), .Q4(q4), .Q5 (q5), .Q6(q6), .Q7(q7), .Q8(q8) );这里的坑在于Q1~Q8的bit顺序。ISERDESE2在DDR模式下Q1是最先采到的串行bit还是最后采到的跟实际硬件走线、采样沿选择直接相关。不要指望规格书把bit次序写清楚必须在仿真或者ILA实测里验证。我的习惯是先在FPGA里发一个固定的8bit训练序列出来比如8h57 0101_0111看解串后Q输出的映射关系再把这个映射关系在逻辑里固定下来。3.4 Intel Cyclone V平台的对应实现如果用Intel系FPGAALTDDIO_IN和ALTLVDS_RX是更顺手的方案。ALTLVDS_RXIP可以直接配置成1.5Gbps以内的LVDS接收解串器内部自动处理DDR采样和bit滑动对齐比Xilinx纯原语组合简单不少。但代价是灵活性降低如果Sensor有特殊训练序列或者动态对齐需求IP内部的对齐状态机不好定制。更底层的做法是用ALTIOBUF配DIFFIN_RX接到ALTDQ_DQS2或者ALTPLL产生采样时钟。Cyclone V的IOE里也有类似IDELAY的引脚延迟单元通过DDIO_IN实现DDR采样。逻辑原理和Xilinx一致高速时钟沿采样、分频时钟并行输出、bit/word对齐另做。所以这篇讲的思路在两个平台上都能落地只是原语细节不同。4. 数据链路恢复串行比特到像素再到完整的帧拿到了每一位bit工作才完成三分之一。真正的复杂度在怎么把bit拼成第n行第m列的像素值。数据手册里的data mapping图是起点但实际还有一种叫做共享/独立同步信号的变体。4.1 一组典型的Sensor时序长什么样以一款工业CMOS Sensor为例它的输出是4 lane sub-LVDS每个lane传输8bit灰度数据的序列化结果。数据lane上除了像素值还周期性插入同步码SOF、EOF等短码。比如SONY家的sub-LVDS Sensor普遍走行同步码 像素数据 行同步结束码的格式而Aptina的HiSPi则走包结构。首先要做的是从4个lane里找到共同的起始基准。大部分Sensor上电后会出一段确定的同步模式比如sensor_start 0xAB四个lane的这个码出现在同一帧内的同一时刻最小差一个lane的启动延迟。逻辑实现流程解串后的并行数据每拍都与期望同步码比较任何一个lane命中同步码就开始记录该lane数据等待所有lane的同步码都出现过之后以最晚那个为基准对各lane做FIFO延迟补偿补偿完成后按Datasheet的lane-to-pixel映射关系重组像素4.2 数据对齐状态机核心代码思路这是全项目里最值得写注释的一段代码。下面给出数据对齐状态机的参考框架localparam IDLE 3d0; localparam SYNC_LANE0 3d1; localparam SYNC_LANE1 3d2; localparam SYNC_LANE2 3d3; localparam SYNC_LANE3 3d4; localparam ALIGNED 3d5; localparam ERROR 3d6; reg [2:0] align_state; reg [1:0] lane_sync_done; always (posedge lane_clk or negedge rst_n) begin if (!rst_n) begin align_state IDLE; lane_sync_done 2d0; end else begin case (align_state) IDLE: begin if (data_lane0_rx SYNC_CODE) begin lane_sync_done[0] 1b1; align_state SYNC_LANE1; end end SYNC_LANE1: begin if (data_lane1_rx SYNC_CODE) begin lane_sync_done[1] 1b1; align_state SYNC_LANE2; end end // ... 类似扩展 ALIGNED: begin // 持续检查同步码保持对齐状态 if (sync_loss_counter THRESHOLD) align_state ERROR; end ERROR: begin // 重建同步重新扫描 align_state IDLE; end endcase end end工程上有两个变量需要重点调一是同步码宽度一般1个并行时钟周期就够有的Sensor需要连续两个周期二是丢失同步的判定门限我用过连续16次未命中即判失步防止瞬时噪声触发重建。4.3 lane间延迟补偿的FIFO深度选择四个lane经过PCB走线长度差异、Sensor内部驱动差异到达FPGA的时间不完全一致。解串之后并行数据之间的相位差表现为若干个并行时钟周期的偏差。一般用FIFO做数据缓存再把所有lane对齐到同一个读指针上。最常见的做法是// 每个lane一个异步FIFO写时钟是各自的lane_clk读时钟是基准lane_clk // 以最慢到达的lane为基准读侧滞后FIFO深度 最大滞后周期数FIFO深度至少要大于最大lane间延迟的2倍避免读写指针碰撞。经验值是128拍足够因为并行时钟一般10MHz~50MHz对应一个时钟周期20~100nslane间延迟通常在几十ns量级。保守起见我一般取256深度。一个常见误区是把PLLl到各BUFIO的时钟skew也当成lane间延迟。其实BUFIO保证同bank内skew极小几十ps级别真正的延迟来源是引脚间的IDELAY设置不同和数据路径不同。所以把IDELAY scan和FIFO延迟补偿分开处理会更清晰前者管亚稳态和采样窗口后者管多lane对齐。5. 上板实测从ILA一片黑到看到第一帧图像这中间有哪些大坑写到这里如果你的板子和代码已经能综合下载恭喜真正有意思的部分才刚开始。上板实测会暴露一堆仿真看不出来的问题。我把这轮调试里最典型的坑列一遍按我踩的频率排序。5.1 ILA采样窗口设置错误导致明明信号有抓出来是乱的ILA是LVDS接收调试最重要的工具但很多人忘了ILA也受时钟约束。如果你把ILA的采样时钟挂到解串后的并行时钟那采样窗口天然是并行数据率没问题。可如果你挂到更快的系统时钟需要检查ILA的建立保持时间余量否则抓到的是亚稳态边沿。一个更隐蔽的坑部分Sensor上电初期数据链路还没稳定会输出一段全0或全1或者乱码直接触发ILA的capture条件抓到的都是垃圾。我的做法是给ILA加一个启动延迟条件——在同步完成后延时100us再触发capture排除上电链路建立时间。5.2 bit顺序错了导致图像呈雪花噪点图像出现大量随机噪点特别是亮暗像雪花一样分布十有八九是解串后的bit次序反了或者硬件反相了。排查手段很简单让Sensor输出纯色画面比如全黑、全白、或者特定灰度条然后看FPGA收到的像素值码型。若是纯色画面下收到的像素值全部是重复的错位码基本可以判断是bit次序问题。调法有两个方向在ISERDESE2输出上手动重排Q1~Q8到Q8~Q1或者把IBUFDS的I和IB互换物理上反相让采样沿落在正确的bit滑窗上两种都试一下哪个配合同步码正确用哪个。注意一旦定了bit次序会影响后面的所有像素映射改起来要全局统一。5.3 高低温下信号时好时坏IDELAY动态校准的必要性工业项目最怕这种问题。常温调好了现场高温或者低温就丢同步。原因就是PCB走线传输延迟会随温度漂移Sensor输出时序也在漂移固定tap值总有脱离采样窗口的时候。解决思路是上电时做一次IDELAY扫描记住前后都开始出错的tap上下边界取中间值运行。如果环境温度变化范围大最好再加周期性重校准在每帧的消隐区做一次重新扫描把新的最佳tap值写入IDELAYE2。这样虽然会牺牲一点逻辑资源但换来的是可靠性。5.4 误以为LVDS只能点对点忽略多Camera复用的可能性部分FPGA的LVDS bank同时接多个Camera Sensor时会产生同一bank内IO切换噪声耦合导致相邻lane误码率升高。这不算接口本身的问题而是同时翻转的ddr数据线太多ground bounce叠加到共模电压上。缓解办法同一bank内不要同时放太多高速LVDS lane留出空闲IO做隔离给每个sensor的电源加独立LDO滤波避免Sensor驱动电流突变反馈到信号地在PCB上Sensor电源地单独走高感抗区域再回接到FPGA地平面形成单点接地处理完这些误码率能下降一个数量级尤其是帧率较高的720p/1080p输出场景。6. 时序约束怎么写三组约束文件把vivado和quartus都伺候好FPGA在跑高速LVDS时如果时序约束不写工具全凭猜测布局布线出来的结果往往可用但偶尔在个别批次板子上就挂。正确做法是主动告诉工具哪些是高速串行引脚、哪些是时钟、哪些是数据到时钟的关系。这样才能让工具优选资源防止关键路径放到慢速区。6.1 Xilinx的XDC约束要点最基本的三条# 1. 给所有LVDS输入端口定义电平标准 set_property -name IOSTANDARD -value LVDS_25 -objects [get_ports {sensor_lvds_p[*]}] set_property -name IOSTANDARD -value LVDS_25 -objects [get_ports {sensor_lvds_n[*]}] set_property -name DIFF_TERM -value TRUE -objects [get_ports {sensor_lvds_p[*]}] # 2. 创建输入时钟约束随路时钟或恢复时钟 create_clock -name sensor_clk -period 40.0 [get_ports clk_in_p] # 3. 数据和时钟的set_input_delay约束 # LVDS 7:1 DDR 模式的输入延迟通常参考IDELAY设置 set_input_delay -clock sensor_pix_clk -max 2.5 [all_inputs] set_input_delay -clock sensor_pix_clk -min 0.5 [all_inputs]这里的数值不能照抄要根据Sensor手册的tSU/tHD时序参数换算。公式是tdco_max tCO_max PCB走线延迟data_available_window tclk_period - tdco_max输入延迟的max/min对应数据有效窗口的左右界如果用的是sub-LVDS无随路时钟那create_clock约束的是PLL参考时钟数据延迟通过IDELAY动态调整XDC里只给一个大致的过约束范围给工具留灵活性。6.2 Intel Quartus的SDC约束要点Quartus里对应的设置更图形化一些但核心SDC语句也很明确set_input_delay -clock [get_clocks {pll_sensor_clk}] -max 3.0 [get_ports {rx_d[*]}] set_input_delay -clock [get_clocks {pll_sensor_clk}] -min 1.0 [get_ports {rx_d[*]}] set_false_path -from [get_ports {rst_n}]需要注意Quartus对LVDS IP的时序约束大多由IP内部的altera_mf自动处理只在用户逻辑边界手动约束。如果你直接用ALTIOBUF裸原语约束就要写全。6.3 跨时钟域处理的约束策略解串后并行数据从serdes_clkdiv转到用户逻辑时钟比如pixel_clk时属于典型的单bit或窄脉冲跨时钟域。最稳妥方案是控制信号打两拍同步数据总线用FIFO处理同步码检测必须在serdes_clkdiv域完成不轻易跨到pixel_clk域约束上给同步状态机所在的时钟域设置set_clock_groups -async防止工具在两个异步域间做无意义的时序分析。7. 从收到一帧到连续输出稳定性调优和性能验证图像终于出来了不代表项目结束。连续工作10分钟、30分钟、一小时是否会出现帧丢失、行错位这是现场交付更关心的。把最后这部分稳定性调优思路整理一下。7.1 误码率统计怎么做在工程里加一个计数器记录自同步建立以来同步码出错的次数同时记录最初发生错误的帧ID。然后通过I2C/UART回传这个寄存器值。我建议在产品初始化流程里把误码率统计作为常驻后台运行一旦超过阈值自动触发重同步。// 统计同步码错误次数 always (posedge pixel_clk or negedge rst_n) begin if (!rst_n) begin sync_err_cnt 32d0; end else if (is_in_sync rx_data ! SYNC_CODE) begin sync_err_cnt sync_err_cnt 1b1; end end这个计数器只要别清零就能判断整机长时间运行的稳定性。我之前遇到一台设备现场每天固定时间丢一帧后来通过错误次数统计发现是Sensor PLL在特定温度点抖动导致而不是FPGA逻辑问题。7.2 眼图扫描的实践技巧如果平台支持动态IDELAY用ILAILA读取tap值的组合可以做简易眼图扫描。做法把IDELAYTAP值由外部寄存器UART/I2C控制tap值每次加1记录对应误码率画出tap值 - 误码率曲线正常应该有一段平坦低误码区取平坦区间中心作为最终tap值这段扫面如果能自动化放进校准程序会比人工逐个tap试爽太多了。7.3 帧同步/异常恢复策略最后一步建立异常恢复状态机保证一旦检测到失步能在下一帧重新建立同步。常见策略失步计数器连续错误超过N次我常用32进入失败状态失败状态下拉高Sensor的reset或restart引脚重新初始化sensor输出重置所有FIFO和IDELAY扫描状态机回到初始IDLE等待下一帧同步码再进入正常状态这个恢复时间一般需要几帧到几十帧对大多数实时应用完全够用。关键是保证恢复过程中不能把错误的像素数据送入下游图像处理否则ISP会被脏数据污染。8. 我在实际调试中断言最值钱的一句话写到最后如果只保留一句话给所有准备动手做LVDS Camera采集的人我会说先看波形再谈代码。LVDS接收的问题90%以上可以通过示波器差分探头和逻辑分析仪在硬件层面定位。把差分探针夹在Sensor输出端观察有没有正常的差分摆幅、有没有过冲/振铃再把探针夹到FPGA引脚端确认信号到这里还正常。两段都正常再打开ILA看FPGA内部。很多人一上来就抓ILA结果两个时钟都没绑定对白忙半天。还有一个实用技巧仿真时一定要建一个带IBUFDS ISERDESE2的模型不要让Testbench直接把并行数据怼到解串输出上。这样综合后的仿真结果才贴近上板行为也方便提前发现bit次序反转这类问题。LVDS Camera sensor采集这个活做到了最后其实就是耐心活。硬件、约束、逻辑、调试一层一层来数据手册每一条时序都要找到对应的工程落地。希望这套从零到一的路径能帮你少走几周弯路做出稳定、可靠的产品。
返回列表