ARTICLE DETAIL

资讯详情

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

FPGA实现多路MIPI视频聚合:架构、DDR调度与调试实战

FPGA实现多路MIPI视频聚合:架构、DDR调度与调试实战 1. 项目缘起与整体架构拆解多路MIPI视频聚合这件事我在两年前第一次接触时踩了不少坑。当时的需求很直接四路MIPI摄像头同时接入每路1080p30帧需要合成一路画面送到后端做AI推理。市面上能买到的多路采集卡要么贵得离谱要么延迟高得没法用最后决定自己用FPGA搭一套。这个方案的核心思路其实不复杂——把多路MIPI信号收进来做格式统一和时序对齐通过DDR做帧缓存再按目标协议输出。但真正做起来从MIPI物理层调试到DDR带宽分配每一步都有讲究。先说说为什么选FPGA而不是专用桥接芯片。专用芯片比如龙迅的LT系列单路转HDMI或者MIPI转LVDS确实方便但多路聚合场景下灵活性太差。你没法自定义帧率匹配策略没法做画面拼接更没法在流水线里插入图像预处理。FPGA的好处在于整条数据通路完全可控从MIPI D-PHY的字节对齐到DDR的读写调度再到输出时序的生成全部可以按需裁剪。代价就是开发周期长尤其是MIPI D-PHY的校准和DDR控制器的调优没有现成IP的话工作量很大。这个方案适合谁呢如果你在做多目视觉、工业检测、医疗内窥镜或者车载环视这类需要多路视频同步采集的项目而且对延迟和灵活性有要求那这套思路可以直接参考。如果你只是单路MIPI转个接口用桥接芯片更省事。另外这个方案对FPGA资源有一定要求建议至少用中端器件比如Xilinx的Artix-7系列或者紫光同创的Logos-2系列逻辑单元在50K以上的型号会比较从容。整体架构我分成了四个层次物理层接入、协议层解析、缓存调度层和输出层。物理层负责MIPI D-PHY的差分信号接收和电平转换协议层做CSI-2的包解析和像素重组缓存调度层用DDR做多帧缓存和带宽仲裁输出层根据后端需求生成MIPI CSI-2或者LVDS时序。每一层之间用AXI-Stream或者自定义的并行总线连接保证数据流不阻塞。下面我逐层拆开讲把关键参数和踩过的坑都摆出来。1.1 为什么选择MIPI CSI-2作为输入协议MIPI CSI-2是目前嵌入式视觉领域最主流的摄像头接口没有之一。手机、平板、车载摄像头、工业相机几乎清一色用这个协议。它的优势在于引脚少、带宽高、功耗低。以四路1080p30为例每路用2-lane D-PHY每lane速率800Mbps总带宽就是4×2×800Mbps6.4Gbps。这个带宽用LVDS或者DVP根本做不到DVP并口光是引脚数就受不了LVDS虽然能跑高速但线缆成本高而且没有标准化的协议层。CSI-2协议本身是分层的物理层是D-PHY或者C-PHY协议层是CSI-2的包格式应用层就是具体的像素数据。D-PHY是差分信号一对时钟lane加一到四对数据lane源同步传输。C-PHY则是三线一组用三相位编码同样的引脚数下带宽更高但调试复杂度也更高。我建议新手先从D-PHY入手资料多、工具链成熟C-PHY的S参数和眼图调试没有高端示波器根本玩不转。注意MIPI D-PHY的差分对走线必须严格等长误差控制在5mil以内否则deskew校准会非常痛苦。我见过太多因为走线不等长导致图像随机花屏的案例。1.2 多路聚合的核心挑战在哪里单路MIPI采集其实不难Xilinx和Lattice都有现成的参考设计。但多路聚合就完全是另一回事了。第一个挑战是时钟域隔离。每路摄像头的像素时钟是独立的虽然标称都是27MHz或者74.25MHz但实际频率有偏差而且不同摄像头的行场时序也不完全一致。你不能简单地把四路数据拼在一起必须做跨时钟域处理。第二个挑战是带宽。四路1080p30的原始数据率是4×1920×1080×30×16bit≈4Gbps如果做图像预处理比如去马赛克或者缩放数据量还会翻倍。DDR的带宽必须算清楚否则就会出现丢帧。我一般会留30%的余量比如实际需要4Gbps那DDR的有效带宽至少要5.2Gbps以上。第三个挑战是同步。多路视频聚合最怕的就是帧不同步比如四路画面拼在一起结果有一路慢了一帧看起来就是错位的。硬件上可以用同一个外部触发信号让所有摄像头同时曝光软件上需要在FPGA里做帧起始对齐把每路的帧头对齐到同一个内部时基上。2. MIPI D-PHY接收与校准实操MIPI D-PHY的接收是整个方案的第一道关卡也是最容易出问题的地方。很多新手以为接上差分线就能出图像结果发现要么没信号要么花屏要么偶尔丢帧。这一章我把D-PHY的初始化流程、deskew校准和常见问题排查讲透。2.1 D-PHY的初始化状态机D-PHY的物理层有一个严格的状态机从上电到进入高速传输模式中间要经过LP-11、LP-01、LP-00等多个低功耗状态。FPGA端的D-PHY硬核或者软核必须按照这个状态机来检测摄像头的输出。具体流程是先检测时钟lane的差分信号确认进入HS模式后再逐条数据lane做同步。我用的方案是Xilinx的MIPI D-PHY IP核配置成2-lane接收模式。IP核的初始化需要提供一个200MHz的参考时钟然后通过DRP接口配置寄存器。关键寄存器包括HS_SETTLE、HS_TERM_EN、LP_CD等。HS_SETTLE这个参数特别重要它决定了高速传输开始后多久采样数据。设得太小会采到建立时间不足的数据设得太大又会错过有效窗口。一般建议根据lane速率来算800Mbps的时候HS_SETTLE设成0x1E左右比较稳。// MIPI D-PHY IP核初始化示例 mipi_dphy_rx u_dphy ( .clk_ref (200MHz), .rst_n (sys_rst_n), .hs_settle (8h1E), .hs_term_en (1b1), .lp_cd (4h3), .clk_lane_hs (clk_lane_hs), .data_lane0_hs (data_lane0_hs), .data_lane1_hs (data_lane1_hs) );2.2 Deskew校准的实操细节Deskew校准是D-PHY接收里最玄学的部分。理论上所有lane的走线等长skew应该为零。但实际上PCB加工有误差连接器也有差异所以必须做动态校准。校准的原理是FPGA发送一个已知的测试码型摄像头回环或者FPGA自己比较各lane的延迟然后调整每个lane的采样点。我用的方法是利用CSI-2协议里的短包做同步。每帧开始前摄像头会发送一个帧起始短包这个包在所有lane上是同时发出的。FPGA检测每个lane上短包的到达时间计算出相对延迟然后调整IDELAY或者ISERDES的采样相位。Xilinx的SelectIO里有IDELAYE2原语可以以78ps的步进调整延迟足够覆盖大部分走线误差。实操心得Deskew校准不要只做一次就完事。温度变化会导致走线延迟漂移建议每隔几分钟重新校准一次或者在帧消隐期做动态校准。我试过连续跑8小时不做动态校准的话最后图像会偏色。2.3 常见MIPI接收问题速查问题现象可能原因排查方法完全无信号差分对极性接反交换P/N线序图像花屏Deskew未校准检查IDELAY配置随机丢帧HS_SETTLE太小增大HS_SETTLE值图像偏色Lane映射错误核对lane顺序偶尔黑屏电源纹波大示波器测1.2V电源这张表是我实际调试中总结出来的基本上覆盖了80%的MIPI接收问题。特别说一下差分对极性很多原理图设计的时候P/N标反了结果就是完全没信号。这时候不用改板直接在FPGA里把P/N交换就行D-PHY IP核一般都有这个配置选项。3. 多路视频缓存与DDR带宽调度四路视频进来之后不能直接拼必须先缓存。原因很简单每路摄像头的帧率和相位都不一样直接拼会出现撕裂。缓存的作用是把每路视频先写入DDR然后按照统一的时基读出来这样就能保证输出画面的完整性。3.1 DDR控制器的选型与配置DDR控制器我建议用FPGA厂商提供的硬核或者经过验证的软核。Xilinx的MIG、紫光同创的DDR控制器都是不错的选择。关键参数是数据位宽和突发长度。四路1080p30的写入带宽需求是4Gbps左右如果DDR3跑800MHz位宽32bit理论带宽是800M×2×3251.2Gbps看起来绰绰有余。但实际有效带宽只有60%左右因为还有刷新、激活、预充电等开销。我的配置是DDR3-160032bit位宽突发长度8。这样单次突发传输256bit也就是8个像素。四路视频轮流写入每路分配一个独立的写通道用轮询仲裁。读通道也是四路独立但优先级可以调整比如主画面优先读出。// DDR写通道仲裁示例 always (posedge clk) begin case (wr_arb_state) 2b00: if (wr_req[0]) begin wr_grant 2b00; wr_arb_state 2b01; end 2b01: if (wr_req[1]) begin wr_grant 2b01; wr_arb_state 2b10; end // ... 轮询四路 endcase end3.2 帧缓存地址分配策略四路视频的帧缓存地址必须分开否则会互相覆盖。我的做法是给每路分配一个独立的地址区间比如路0用0x00000000到0x00FFFFFF路1用0x01000000到0x01FFFFFF以此类推。每路内部再用乒乓缓冲写一帧读一帧避免读写冲突。地址计算很简单一帧1080p的YUV422数据量是1920×1080×24,147,200字节约4MB。四路就是16MB加上乒乓缓冲就是32MB。DDR3的容量一般512MB起步完全够用。但要注意地址对齐DDR的突发传输要求地址按突发长度对齐否则效率会下降。注意乒乓缓冲的切换时机很关键。必须在帧消隐期切换否则会出现半帧旧数据半帧新数据的情况。我一般用帧起始信号作为切换触发延迟几个时钟周期确保安全。3.3 带宽计算与余量分配带宽计算是DDR调度的核心。我以四路1080p30 YUV422为例算一遍单路像素率1920×1080×30 62,208,000像素/秒单路数据率62,208,000×16bit 995Mbps四路总数据率995Mbps×4 3.98Gbps写入加读出3.98Gbps×2 7.96Gbps加30%余量7.96Gbps×1.3 10.35GbpsDDR3-1600 32bit的理论带宽是1600M×2×32102.4Gbps有效带宽按60%算是61.4Gbps远大于10.35Gbps。所以带宽不是瓶颈瓶颈在仲裁效率和延迟。如果仲裁器设计得不好比如某一路长时间占用总线其他路就会丢帧。我的经验是给每路设置一个最大突发长度比如每次最多写256个像素然后强制切换保证公平性。4. 视频聚合与输出时序生成缓存之后就是聚合和输出。聚合的方式取决于后端需求如果是做画面拼接那就把四路画面按2×2排列如果是做多路切换那就按时间片轮询输出如果是做AI推理那就把四路画面打包成一个多通道张量。我这次做的是2×2拼接输出分辨率3840×2160帧率30。4.1 2×2拼接的坐标映射拼接的坐标映射其实就是一个地址转换。输出画面的坐标(x,y)映射到输入画面的坐标(x_in,y_in)和路号ch。对于2×2拼接每路占输出画面的四分之一所以ch (y 1080) ? ((x 1920) ? 3 : 2) : ((x 1920) ? 1 : 0)x_in x % 1920y_in y % 1080这个映射在FPGA里用组合逻辑就能实现延迟很小。但要注意边界处理比如x1920的时候x_in应该是0ch要切换到右边那路。我一般用移位和掩码来实现比除法快得多。4.2 输出时序生成与MIPI发送输出如果是MIPI CSI-2那就需要FPGA作为发送端生成D-PHY的HS时序。Xilinx有MIPI D-PHY TX的IP核配置成4-lane发送每lane速率1Gbps总带宽4Gbps刚好够3840×2160×30×16bit3.98Gbps。发送端的初始化比接收端简单因为时序是自己控制的不需要deskew校准。但有一个坑MIPI CSI-2的包格式必须符合规范否则后端接收芯片不认。帧起始短包、帧结束短包、行起始短包、行结束短包一个都不能少。而且短包的ECC校验必须正确否则接收端会丢弃整个帧。我建议用现成的CSI-2 TX IP核自己写的话ECC和CRC很容易出错。// CSI-2短包生成示例 // 帧起始短包DI0x00, WC0x0000, ECC自动计算 csi2_short_packet u_fs_pkt ( .data_type (8h00), // Frame Start .virtual_ch (2b00), .word_count (16h0000), .ecc (ecc_out) );4.3 输出帧率匹配与丢帧策略如果输入帧率和输出帧率不一致比如输入是30帧输出是60帧那就需要做帧率转换。简单的方法是重复帧但这样会有运动模糊。复杂一点的方法是做帧插值但FPGA资源消耗大。我的做法是如果输出帧率高于输入就重复最近一帧如果输出帧率低于输入就丢弃多余的帧。丢帧的时候要注意整帧丢弃不能丢半帧。实操心得帧率转换最好在DDR缓存之后做这样丢帧和重复帧都不会影响输入端的采集。我试过在输入端做丢帧结果导致MIPI接收状态机异常花了很久才定位到问题。5. 常见问题排查与调试技巧这套方案我前后调试了三个月遇到的问题五花八门。这里挑几个典型的分享出来希望能帮你少走弯路。5.1 图像横向花屏的根因分析横向花屏是最常见的问题表现为画面上出现横向的彩色条纹。根因通常是DDR读写地址错位或者MIPI lane的字节对齐错误。排查方法是先用一个固定的测试图案代替摄像头输入比如彩条然后观察花屏的位置是否固定。如果固定那就是地址映射的问题如果随机那就是MIPI接收的问题。我遇到过一次花屏只在画面右侧出现左侧正常。后来发现是DDR的突发长度设成了8但地址计算的时候按4对齐了导致每行末尾多读了4个像素。改成按8对齐就解决了。5.2 MIPI屏调试没信号的排查路径如果你用的是MIPI屏做输出调试没信号的时候先查背光和复位。很多MIPI屏需要先给背光供电再给复位信号最后才开始传输数据。顺序错了屏就不亮。然后查MIPI的时钟lane用示波器看有没有差分信号。如果没有那就是FPGA的D-PHY TX没配置好。如果有信号但屏不亮那就是初始化命令序列不对需要对照屏的datasheet逐条核对。5.3 DDR读写冲突导致丢帧的解决DDR读写冲突是多路视频聚合的经典问题。表现是偶尔丢一帧或者画面撕裂。解决方法是增加读写缓冲深度或者调整仲裁优先级。我的做法是给每路视频分配独立的读写FIFOFIFO深度至少能存两行像素。这样即使DDR暂时忙FIFO也能撑住不会丢数据。问题现象解决方法DDR读写冲突偶尔丢帧增加FIFO深度仲裁不公平某路卡顿轮询仲裁加最大突发限制地址错位横向花屏检查突发长度对齐时钟域冲突随机错误加两级同步器5.4 跨时钟域处理的注意事项多路视频聚合涉及多个时钟域每路摄像头的像素时钟、DDR控制器时钟、输出时钟。跨时钟域处理不当会导致亚稳态和随机错误。我的原则是所有跨时钟域的信号都必须经过至少两级触发器同步数据总线用异步FIFO隔离。千万不要直接用两级触发器同步多位数据那样会出现位偏移。注意异步FIFO的读写指针必须用格雷码否则在跨时钟域的时候会出现多位同时变化导致空满判断错误。这个坑我踩过调试了一周才发现。6. 资源优化与工程化建议方案跑通之后下一步就是优化资源和工程化。FPGA资源永远是紧张的尤其是多路视频聚合这种数据密集型应用。6.1 逻辑资源优化技巧首先是MIPI接收部分如果四路都用硬核D-PHY逻辑资源消耗不大。但如果用软核那LUT和FF的消耗就很可观了。我的建议是能用硬核就用硬核Xilinx的Artix-7和Zynq-7000都有MIPI D-PHY硬核。其次是DDR控制器用厂商的硬核控制器比自己写软核省很多资源。图像处理部分比如去马赛克和缩放如果资源紧张可以用时分复用的方式让一个处理单元轮流处理四路数据。代价是处理延迟增加但资源消耗降到四分之一。我试过用200MHz的时钟跑时分复用四路1080p30完全能实时处理。6.2 时序收敛的实战经验多路视频聚合的时序收敛是个挑战尤其是DDR控制器和MIPI高速接口。我的经验是第一给高速接口分配独立的时钟区域不要和逻辑混在一起第二用时序约束把跨时钟域路径设为false path让工具不要浪费精力去优化第三关键路径手动做流水线比如DDR的读写仲裁器加一级寄存器就能提高频率。# 时序约束示例 set_false_path -from [get_clocks mipi_clk] -to [get_clocks ddr_clk] set_max_delay -from [get_pins ddr_arb/*] -to [get_pins ddr_ctrl/*] 2.06.3 工程化部署的几点建议如果这套方案要产品化有几个点必须注意。第一MIPI连接器要选带屏蔽的否则EMC测试过不了。第二DDR的电源要独立纹波控制在50mV以内。第三FPGA的配置方式建议用SelectMAP或者SPI Flash方便现场升级。第四加一个看门狗防止FPGA跑飞。我在实际部署的时候还遇到过一个坑四路摄像头的电源如果共用一个LDO某一路启动的时候会导致其他路电压跌落结果就是随机有一路不工作。后来改成每路独立LDO就解决了。这个问题的隐蔽性很强因为单独测试每一路都正常只有四路同时跑才出问题。最后分享一个小技巧调试多路视频聚合的时候先让每一路单独跑通确认MIPI接收和DDR缓存都没问题再逐步增加路数。不要一上来就四路全开那样出了问题根本不知道是哪一路的毛病。我一开始就是四路全开结果花屏、丢帧、黑屏各种问题混在一起排查了整整两周才理清楚。后来改成单路调试一天就搞定了。
返回列表