☰
FPGA实现多路MIPI视频聚合:架构设计与DDR带宽优化实战
2026/9/30 23:39:09 网站建设 项目流程

1. 项目缘起与整体设计思路

1.1 为什么需要多路MIPI视频聚合

做过嵌入式视觉项目的朋友大概率都遇到过这样的场景:手头有好几路MIPI摄像头或者MIPI视频源,每一路都是独立的CSI-2输出,但后端主控的MIPI CSI接口数量有限,通常只有一到两路。这时候要么换主控,要么加桥接芯片,要么就得上FPGA做聚合。

我最早接触这个需求是在一个多目视觉项目里,四路MIPI摄像头需要同步采集,后端只有一路CSI输入。市面上能买到的MIPI聚合芯片选择不多,而且灵活性差,分辨率、帧率、路数稍微一变就得重新选型。用FPGA来做这件事,虽然前期开发投入大一些,但胜在完全可控,后期扩展也方便。

所谓多路视频聚合,本质上就是把多路独立的MIPI视频流,经过缓冲、同步、复用之后,合并成一路高带宽的视频流输出。这个“合并”可以是空间上的拼接(比如四宫格画面),也可以是时间上的轮询(分时复用),还可以是通道上的复用(把多路视频打包进一个更大的数据流)。具体选哪种,取决于后端怎么解析。

1.2 方案选型的几个关键考量

在动手之前,有几个问题必须先想清楚,否则后面会反复返工。

第一,MIPI输入路数和速率。MIPI D-PHY的每条lane速率决定了你能接多少路。比如常见的1080p60视频,每路大概需要1.5Gbps左右的带宽(取决于像素格式和blanking)。如果FPGA的MIPI硬核支持4条lane、每lane 2.5Gbps,那总输入带宽就是10Gbps,理论上能接6路左右。但实际要考虑协议开销和突发,一般留30%余量。

第二,聚合后的输出形式。如果后端是标准CSI接收器,那输出也得是标准MIPI CSI-2格式,只是把多路虚拟通道(Virtual Channel)映射到同一个物理链路上。MIPI CSI-2协议本身支持VC(Virtual Channel)概念,最多16个虚拟通道,这给聚合提供了天然便利。如果后端是自定义协议或者走其他接口(比如LVDS、以太网),那输出形式可以更灵活。

第三,帧同步问题。多路视频如果要做拼接或者融合,帧同步是绕不开的。不同摄像头的曝光时刻可能差几毫秒甚至几十毫秒,直接拼接会出现运动物体错位。解决方案要么是硬件同步信号(比如外部触发),要么是在FPGA里做帧对齐缓冲。我一般推荐前者,软件对齐的延迟和复杂度都更高。

第四,DDR带宽和延迟。多路视频聚合必然需要帧缓冲,DDR的带宽和访问延迟直接影响系统能跑多高的分辨率。以四路1080p60为例,每路每秒约124MB的原始数据(RGB888),四路就是500MB/s的写入带宽,加上读出,DDR至少需要1GB/s以上的有效带宽。DDR3-1600理论带宽12.8GB/s,实际有效带宽大概60%-70%,也就是7-8GB/s,看起来够用,但要注意多端口竞争和刷新开销。

1.3 整体架构设计

基于上面的考量,我最终确定的架构是这样的:

  • 输入侧:FPGA的MIPI D-PHY硬核接收多路MIPI信号,每路独立解包CSI-2协议,提取出像素数据和时序信息。
  • 缓冲侧:每路视频写入DDR中独立的帧缓冲区,采用乒乓缓冲或者多帧缓冲策略。
  • 聚合侧:从DDR中按需读出各路视频数据,根据聚合模式(拼接/轮询/复用)进行重组。
  • 输出侧:将聚合后的视频流重新打包成MIPI CSI-2格式,通过D-PHY发送出去。

这个架构的核心在于DDR控制器的多端口仲裁和MIPI协议的精确解析。前者决定了系统能不能跑满带宽,后者决定了数据能不能正确提取。

提示:如果FPGA没有MIPI硬核,也可以用LVDS或者普通IO配合外部电阻网络来接收MIPI信号,但速率和稳定性会打折扣。建议优先选带MIPI D-PHY硬核的FPGA,比如Xilinx的Zynq UltraScale+系列或者紫光同创的Logos系列。

2. MIPI协议解析与FPGA实现细节

2.1 MIPI CSI-2协议栈的拆解

MIPI CSI-2的协议栈从下到上大致分三层:物理层(D-PHY)、协议层(CSI-2)、应用层(像素数据)。FPGA要做的,主要是物理层的接收和协议层的解析。

物理层(D-PHY)负责把差分信号转换成数字比特流。D-PHY有HS(高速)和LP(低功耗)两种模式,视频传输时工作在HS模式。HS模式下,时钟lane提供DDR时钟,数据lane在时钟的上下沿都采样数据。FPGA的MIPI硬核会自动处理时钟恢复和串并转换,输出的是并行数据加同步信号。

协议层(CSI-2)的数据包结构是这样的:每个数据包以SoT(Start of Transmission)开始,然后是包头(包含虚拟通道号、数据类型、数据长度等),接着是有效载荷(像素数据),最后是CRC校验和EoT(End of Transmission)。FPGA需要解析包头,提取出虚拟通道和数据类型,然后把有效载荷写入对应的缓冲区。

这里有个容易踩坑的地方:CSI-2的包头长度和字节序。包头通常是4个字节,但不同厂商的IP核可能对字节序的处理不一样。我遇到过有的IP核输出的是大端序,有的是小端序,如果不注意,解析出来的虚拟通道号会完全错乱。建议在调试阶段先把包头抓出来,用逻辑分析仪或者ILA核对一下。

2.2 多路MIPI接收的同步与去偏斜

多路MIPI接收时,各路之间的偏斜(skew)是个大问题。虽然每路内部有D-PHY的deskew校准,但路与路之间的到达时间可能差很多。如果后端要做像素级拼接,这个偏斜必须处理。

我的做法是在每路的CSI-2解析模块后面加一个帧同步FIFO,用统一的帧起始信号(比如第一路的SoF)作为基准,其他路的数据先缓存,等到基准帧到来后再一起释放。FIFO的深度取决于最大偏斜量,一般几行像素的缓冲就够了。

注意:MIPI D-PHY的deskew校准是在物理层做的,主要解决lane与lane之间的偏斜。路与路之间的偏斜属于系统级问题,需要在协议层或者应用层解决。

2.3 虚拟通道映射与数据提取

CSI-2协议支持虚拟通道(VC),这给多路聚合提供了便利。每路摄像头可以配置成不同的VC号,FPGA在解析时根据VC号把数据分发到不同的缓冲区。这样即使多路视频复用在同一个物理链路上,也能正确分离。

但实际项目中,很多摄像头不支持VC配置,或者VC号是固定的。这时候就需要FPGA主动做VC重映射。具体做法是:在CSI-2解析模块里,忽略包头中的VC号,而是根据接收端口号来分配VC。比如端口0的数据统一映射到VC0,端口1映射到VC1,以此类推。

数据提取时要注意像素格式的转换。MIPI CSI-2支持多种像素格式,RAW8/10/12、RGB565/888、YUV422等。FPGA内部一般统一成RGB888或者RAW16来处理,输出时再转回目标格式。转换过程中要注意位对齐和饱和处理,否则会出现颜色偏差。

2.4 输出侧的CSI-2打包

输出侧的CSI-2打包是接收侧的逆过程。需要根据聚合后的视频流,重新生成包头、计算CRC、插入SoT/EoT。这里的关键是时序控制:MIPI D-PHY对时序要求很严,SoT和EoT之间的间隔、包头和载荷之间的间隔都有明确规定,违反了会导致接收端解析失败。

我一般会用状态机来实现打包过程:IDLE状态等待帧起始,然后依次发送包头、载荷、CRC,最后发EoT。每个状态的持续时间根据D-PHY的时序参数来计算。比如包头通常是4个字节,在HS模式下每个字节需要的时间是固定的,状态机只需要按节拍推进就行。

3. DDR多端口读写与带宽优化

3.1 多端口DDR控制器的设计

多路视频聚合对DDR控制器的要求很高:多个写入端口(每路视频一个)、多个读出端口(聚合模块一个或多个)、还要保证实时性。常见的做法是用Xilinx的MIG或者紫光同创的DDR控制器IP,然后在外面包一层仲裁逻辑。

仲裁策略我试过几种:轮询仲裁最简单,但实时性差;优先级仲裁能保证关键端口,但低优先级端口可能饿死;基于信用额的仲裁比较复杂,但效果最好。最终我选的是加权轮询,给每个端口分配一个权重,权重根据视频分辨率和帧率来定。比如1080p的端口权重是720p的两倍。

DDR的访问粒度也很重要。MIPI视频数据是流式的,但DDR是突发访问的。如果每次只写几个字节,DDR的效率会极低。我的做法是在写入侧加一个异步FIFO,积累到一定长度(比如64字节或者128字节)再触发一次DDR突发写。这样能把DDR的效率从30%提升到70%以上。

3.2 帧缓冲区的地址规划

帧缓冲区的地址规划直接影响DDR的访问效率。我的经验是:

  • 每路视频分配独立的地址区间,避免交叉访问导致的bank冲突。
  • 地址区间按DDR的bank和row对齐,尽量让连续访问落在同一个row里。
  • 乒乓缓冲的两块地址要错开bank,避免同时访问同一bank。

以DDR3为例,假设每路1080p视频需要4MB的帧缓冲(RGB888,1920x1080x3约6MB,实际按8MB算),四路就是32MB。加上乒乓,64MB。DDR3的容量一般够用,但地址映射要仔细规划。

3.3 带宽计算与余量评估

带宽计算是方案设计阶段必须做的。以四路1080p60、RGB888为例:

  • 每路像素时钟:1920x1080x60 ≈ 124.4M像素/秒
  • 每像素3字节,每路数据率:124.4M x 3 ≈ 373MB/s
  • 四路总写入带宽:373 x 4 ≈ 1.5GB/s
  • 读出带宽(假设聚合后输出1080p60):373MB/s
  • 总带宽需求:约1.9GB/s

DDR3-1600的有效带宽大概7-8GB/s,看起来余量很大。但实际要考虑:

  • 刷新开销:约5%-10%
  • 多端口竞争:效率下降20%-30%
  • 读写切换开销:约10%

所以实际可用带宽大概4-5GB/s,1.9GB/s的需求是安全的。但如果分辨率提到4K,或者路数增加到8路,就要重新评估了。

提示:带宽计算时一定要留余量,我一般按理论值的50%来规划。宁可DDR跑不满,也不要让视频出现丢帧。

3.4 读写冲突的规避技巧

DDR的读写切换是有开销的,频繁切换会严重拉低效率。我的做法是批量读写:写入侧积累到一定量再写,读出侧也按行或者按块读。这样读写切换的频率大大降低。

另外,读写分离也是个好办法。如果DDR控制器支持多个bank,可以把写入和读出分配到不同的bank,减少冲突。具体做法是在地址映射时,把写入地址和读出地址映射到不同的bank组。

4. 实操过程与关键环节实现

4.1 硬件选型与连接

我用的FPGA是Xilinx Zynq UltraScale+ ZCU104,带MIPI D-PHY硬核,支持4条lane、每lane 2.5Gbps。摄像头是四路IMX219,通过MIPI CSI-2输出1080p30。DDR是板载的DDR4-2400,容量2GB。

连接上,四路摄像头的MIPI信号分别接到FPGA的四个MIPI接收bank。注意MIPI的差分对要走等长,阻抗控制100欧姆。如果走线太长或者阻抗不匹配,眼图会闭合,导致接收失败。

4.2 MIPI接收模块的配置

Xilinx的MIPI D-PHY IP核配置如下:

  • Lane数:2条(IMX219是2-lane)
  • 数据速率:每lane 1.5Gbps(1080p30的典型值)
  • 参考时钟:200MHz
  • 连续时钟模式:使能

配置完成后,IP核会输出并行数据和同步信号。接下来是CSI-2解析模块,我用的是自己写的状态机,解析包头、提取VC和数据类型、计算CRC。

// CSI-2包头解析状态机(简化版) always @(posedge clk) begin case (state) IDLE: if (sot) state <= HEADER; HEADER: begin // 解析4字节包头 if (byte_cnt == 3) state <= PAYLOAD; end PAYLOAD: begin // 提取像素数据 if (byte_cnt == data_len) state <= CRC; end CRC: state <= IDLE; endcase end

4.3 DDR控制器的多端口仲裁实现

DDR控制器我用的是MIG IP,配置成4个AXI端口。仲裁逻辑用加权轮询:

// 加权轮询仲裁(简化版) always @(posedge clk) begin if (req[0] && weight[0] > 0) begin grant <= 0; weight[0] <= weight[0] - 1; end else if (req[1] && weight[1] > 0) begin grant <= 1; weight[1] <= weight[1] - 1; end // ... 其他端口 if (weight[0] == 0 && weight[1] == 0) begin // 重新加载权重 weight[0] <= 4; weight[1] <= 2; end end

权重根据视频分辨率来定:1080p的端口权重4,720p的权重2,480p的权重1。

4.4 聚合模块的实现

聚合模块从DDR中读出各路视频数据,按拼接模式重组。以四宫格为例,输出分辨率是3840x2160(四路1080p拼成2x2)。读出时按行扫描,每行从四路中各取一段,拼成一行输出。

// 四宫格拼接(简化版) always @(posedge clk) begin if (out_x < 1920) begin // 左上角 data_out <= buf0[out_y][out_x]; end else if (out_x < 3840) begin // 右上角 data_out <= buf1[out_y][out_x - 1920]; end // ... 下半部分类似 end

4.5 输出侧MIPI发送的配置

输出侧用同样的MIPI D-PHY IP核,配置成发送模式。CSI-2打包模块生成包头和CRC,然后通过D-PHY发送。注意输出速率要和接收端匹配,否则接收端会解析失败。

5. 常见问题与排查技巧实录

5.1 MIPI接收无信号或信号不稳定

这是最常见的问题。排查思路:

  1. 检查物理连接:差分对是否接反、阻抗是否匹配、走线是否等长。
  2. 检查时钟:MIPI的参考时钟是否正常,频率是否正确。
  3. 检查D-PHY配置:lane数、数据速率是否和摄像头匹配。
  4. 检查LP模式:有些摄像头需要先进入LP模式再切HS,如果FPGA没有正确处理LP状态,会一直收不到数据。

我遇到过最坑的一次是差分对接反了,眼图完全闭合,查了两天才发现。所以硬件调试时一定要先确认物理层没问题。

5.2 DDR带宽不足导致丢帧

表现是视频偶尔卡顿或者花屏。排查方法:

  • 用ILA抓DDR的读写信号,看是否有长时间等待。
  • 计算实际带宽需求,对比DDR的有效带宽。
  • 检查仲裁权重是否合理,低优先级端口是否被饿死。

优化手段:

  • 增大FIFO深度,减少DDR访问频率。
  • 调整仲裁权重,给高带宽端口更多机会。
  • 优化地址映射,减少bank冲突。

5.3 聚合后画面错位或撕裂

这通常是帧同步没做好。排查:

  • 检查各路视频的帧起始信号是否对齐。
  • 检查FIFO的深度是否足够容纳最大偏斜。
  • 检查读出逻辑是否按正确的顺序读取。

我的经验是,帧同步FIFO的深度至少要能容纳两行像素的偏斜。如果偏斜更大,就要考虑用外部同步信号了。

5.4 输出MIPI接收端解析失败

表现是后端完全收不到画面,或者画面全是雪花。排查:

  • 检查输出D-PHY的时序参数是否满足MIPI规范。
  • 检查CSI-2包头是否正确,CRC是否计算正确。
  • 检查输出速率是否和接收端匹配。

我遇到过CRC计算错误导致接收端丢弃所有包的情况。后来发现是CRC的多项式用错了,改成标准的多项式后正常。

5.5 常见问题速查表

问题现象可能原因排查方法解决方案
MIPI无信号物理连接问题查差分对、阻抗、走线重新焊接或调整走线
信号不稳定时钟或配置问题查参考时钟、D-PHY配置调整配置参数
DDR丢帧带宽不足计算带宽、抓ILA优化仲裁、增大FIFO
画面错位帧同步问题查帧起始信号、FIFO深度增加同步逻辑
输出解析失败时序或CRC问题查时序参数、CRC计算调整时序、修正CRC

提示:调试MIPI时,逻辑分析仪和ILA是必备工具。没有它们,基本靠猜,效率极低。

6. 几个容易被忽略的细节

6.1 MIPI同层挖空的处理

PCB设计时,MIPI差分对下面的参考平面要完整,不能有挖空或者跨分割。如果参考平面不完整,阻抗会突变,导致信号反射。我见过一个项目因为MIPI走线跨了电源分割,眼图完全闭合,后来加了缝合电容才解决。

6.2 摄像头配置的坑

不同摄像头的MIPI配置寄存器不一样,有的需要配置lane数、数据速率、VC号等。IMX219的配置就比较典型,需要先写一堆寄存器才能输出正确的MIPI信号。建议先用官方的配置脚本,再根据实际需求微调。

6.3 温度对DDR的影响

DDR的时序参数会随温度变化,高温下可能需要降频或者调整刷新率。工业级应用要特别注意这一点。我一般会在DDR控制器里使能温度补偿功能,或者留出足够的时序余量。

6.4 电源噪声对MIPI的影响

MIPI的差分信号幅度很小,电源噪声很容易耦合进去。建议MIPI的电源单独用LDO供电,并且加足够的去耦电容。我试过用DC-DC直接供电,结果误码率很高,换成LDO后立刻改善。

7. 方案扩展与个人体会

这套方案目前跑四路1080p30很稳,DDR带宽占用大概40%,还有很大余量。如果要扩展到八路,或者提升到4K,需要做几件事:换更大容量的DDR、增加MIPI lane数、优化仲裁算法。

我个人在实际操作中的体会是,FPGA做MIPI聚合,难点不在写代码,而在调试。物理层的信号完整性、协议层的时序匹配、系统层的带宽分配,任何一个环节出问题都会导致整个系统跑不起来。所以前期规划一定要做足,尤其是带宽计算和地址映射,后面改起来很麻烦。

另外,MIPI的调试工具很重要。一个能抓协议包的分析仪,能省掉大量猜测时间。我用的是一款支持CSI-2解码的分析仪,虽然贵,但值得。

最后再分享一个小技巧:如果DDR带宽实在不够,可以考虑在FPGA内部做行缓存而不是帧缓存。行缓存只需要几KB的BRAM,带宽压力小很多。但行缓存只能做行级的拼接,不能做帧级的同步。具体用哪种,看你的应用需求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询