尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FPGA多路MIPI视频聚合:从协议解析到系统调试全解析

FPGA多路MIPI视频聚合:从协议解析到系统调试全解析 做了这么多年的FPGA图像处理多路视频聚合这类需求几乎每年都会撞上几回。尤其是现在MIPI摄像头遍地都是MIPI接口本身又是一个高速串行协议一提到“多路MIPI聚合”很多第一次接触的朋友第一反应就是拿什么接怎么接接了之后怎么保证每一路都不花屏、不掉帧、不互相干扰这个项目说白了就是把多路MIPI CSI-2接口的摄像头数据全部接入一片FPGA在FPGA内部完成协议解析、数据缓存、带宽仲裁和时序调度最后统一从一路输出接口送出去。目标接口可以是HDMI、LVDS、MIPI DSI也可以是千兆/万兆以太网取决于下游要用什么。这篇文章我用一个典型的4路1080p60 RAW10输入、聚合后单路输出的方案做主线把从协议层、系统架构到具体模块实现、再到调试排障的整个路子梳理一遍。适合正在做或者准备做FPGA视频接入、多目拼接、机器视觉采集这类项目的朋友参考也适合刚入门想搞明白“MIPI在FPGA里到底是怎么跑起来”的开发者。1. 为什么是FPGA来做多路视频聚合1.1 场景痛点多路摄像头接入的现实问题做多路视频聚合这件事第一道坎往往不是算法和逻辑而是物理接口。现在的摄像头模组输出接口就那么几类MIPI CSI-2、USB、DVP、LVDS其中MIPI又是手机/嵌入式平台上最主流的。问题是主控芯片上的MIPI CSI控制器数量有限一般也就2到4路而且很多SoC对分辨率、帧率、lane数还有严格限制。举个例子一台工业检测设备要同时看4个不同角度的画面每个都是1080p60的摄像头。如果用带MIPI输入的SoC去接档次稍微高一点的芯片可能勉强有4路CSI但每一路最多支持1~2 lane、带宽也有限制实际上经常是只能接2路高帧率或者4路低帧率的。再加上摄像头输出格式五花八门RAW8、RAW10、RAW12、YUV422SoC那边还不一定全支持。这时候FPGA的价值就出来了——你要的接口数量、协议格式、时序要求都可以通过可编程逻辑“硬凑”出来。1.2 FPGA方案的核心优势并行、低延迟、可定制FPGA做视频聚合最大的天然优势是并行。多路MIPI数据进来之后每一路走独立的物理通道和独立的解码逻辑天然不会互相阻塞。相比之下CPU/GPU方案要多路解码哪怕只是搬运数据也会被总线带宽和调度延迟卡住。延迟方面FPGA内部从接收像素到写入DDR再到读出聚合全程是硬件流水线典型延迟在几微秒到几十微秒级别而且非常确定。做实时控制类应用比如机械臂视觉引导、高速检测时这个确定性比绝对延迟大小还重要。第三个优势是可裁剪。同一套硬件平台今天做4路拼接明天可能要做1路4K输入、或者是8路720p输入只需要改改逻辑、换一下DDR读写调度策略硬件板卡不用重新画。遇到过项目做到一半需求改成“画面分割OSD叠加”SoC方案大概率要换芯片FPGA这边改一版逻辑就完事。1.3 方案选型不同产品线的取舍选FPGA型号时我一般会先看三个资源有没有高速收发器serdes、有没有硬核MIPI PHY、DDR控制器和带宽够不够。目前主流的几条产品线情况如下平台MIPI RX支持方式适合场景需要注意的点Xilinx 7系列 / Zynq-7000MIPI CSI-2 RX Subsystem IP或者LVDS SerDes改造成D-PHY中等分辨率多路接入7系列没有硬核MIPI用SerDes有一定限制Xilinx Zynq UltraScale硬核MIPI仅部分型号或者综合IP高分辨率、多路配合ARM做上层MIPI硬核数量固定超过数量还是得走逻辑紫光同创 FPGA如PGL/PG2系列自带MIPI硬核或厂家IP国产化项目、本地化支持需要仔细读IP文档部分IP与Xilinx用法差别大Lattice CrossLink / Nexus硬核MIPI RX/TX专门做视频桥接简单桥接、少量通道聚合逻辑资源少复杂调度能力偏弱适合做桥而不是做系统中低端国产FPGA如安路、高云部分型号有MIPI硬核或靠LVDS IP低成本小分辨率高速MIPI支持有限抗干扰能力要靠PCB保障实际项目中如果核心诉求是“性能、灵活、后续好扩展”我会首选Xilinx 7系列/Zynq或者紫光同创的PGL系列如果就是想把4路MIPI转成一路MIPI给SoC用Lattice CrossLink反而最省心它内部甚至自带视频处理单元。2. MIPI协议里那些必须先搞清楚的细节2.1 D-PHY和C-PHY别再傻傻分不清MIPI物理层有两个分支D-PHY和C-PHY。D-PHY最常见摄像头模组上几乎都是它C-PHY在手机上见的更多特点是三线一组、没有独立时钟线理论上同频率下带宽更高但实现复杂度和PCB设计难度都要上一个台阶。D-PHY的通道是“分 lanes”的一组时钟lane加一组数据lane数据lane是1~4条。时钟是DDR模式就是时钟的上升沿和下降沿都能送数据。C-PHY则是每条lane由三根线构成采用三进制编码每个symbol能携带大约2.28bit效率比D-PHY高但信号完整性设计和解码逻辑都更麻烦。FPGA这边绝大多数逻辑方案处理的是D-PHY。C-PHY目前在FPGA上要么用SerDes硬核强行解要么压根不支持你要是拿到一颗输出C-PHY的摄像头传感器先别急着开心先确认FPGA侧的PHY能不能解。2.2 协议层CSI-2包结构、ECC和CRCMIPI CSI-2协议层结构其实不复杂每一帧图像被拆成一串长短不一的包。长包包括包头Packet Header、数据负载、包尾Packet Footer。包头里有数据类型Data TypeDT、字计数Word Count和ECC校验值。DT字段决定了这个包里装的是什么格式的数据比如RAW8是0x2ARAW10是0x2BRAW12是0x2CYUV422-8bit是0x1E。识别错了DT后面即使数据没错图像也是花的。ECC是包头里8bit的校验它能纠正1bit错误、检测2bit错误这个机制在高速链路上很有用。图像数据负载里还有16bit CRC做完整性校验。这些字段看着繁琐但在FPGA里实现就是一个字节流转状态机的事。最关键的反而是“你怎么知道一个包什么时候开始、什么时候结束”。CSI-2的SoTStart of Transmission、EoTEnd of Transmission是物理层的信号到了协议层就变成了包头的固定模式0x00/0x01开头状态机只要按字节流去抓这个模式就行。2.3 链路训练、deskew calibration和初始化时序MIPI链路不是上电就能跑的摄像头要等主控通过I2C配置寄存器然后双方握手进入高速传输模式。这里有个热词经常出现deskew calibration。为什么需要deskew因为D-PHY是DDR传输数据lane和时钟lane是分开走的PCB上走线长度不可能完全等长多条lane之间也会因为过孔、扇出走线长度差异产生偏移。这个偏移如果超过一个UI单位间隔的几分之一接收端采样就会出错。Deskew calibration就是让发送端在进入高速模式前发出一串特殊模式接收端根据这个模式调整采样相位把各lane对齐。FPGA侧通常不需要你手动调相位MIPI IP核会自己完成calibration但你要做的是保证phdrx或者IP的core clock频率正确、复位时序正确以及PCB上尽量保证同组lane等长误差控制在mil级以内。否则deskew会失败IP会报错或者链路时而能通时而不通。2.4 带宽到底怎么算先算清楚再接这是一个特别基础但特别容易出错的步骤。MIPI链路带宽计算公式要同时考虑格式位宽、帧率和协议开销。以1080p60 RAW10为例一帧像素数据量是1920x1080x10bit20.736Mbit帧率60fps就是1.244Gbps。这还不包括行消隐和帧消隐期间的空闲以及包头的开销。如果摄像头给出的实际帧率是60fps行/帧消隐很短按1.35Gbps估算链路速率比较稳妥。然后用2条lane、速率800Mbps的D-PHY链路算2 x 800 1.6Gbps看起来够但你要注意这个速率是指lane比特率实际有效载荷还得去掉协议开销所以算出来刚好够的资料往往在实际跑的时候会掉帧。再比较一下“4路1080p60 RAW10聚合”的需求4路加起来有效数据是4.98Gbps。如果只是简单搬运不做格式转换输出的单一链路必须是5Gbps以上——这基本意味着得用DDR3/4缓存下来再通过千兆以太网最多1G、USB3.0、或者PCIe出去不然单靠一路MIPI是喂不出去的。这正是多路聚合项目绕不开DDR的原因。3. 多路聚合系统架构先从整体格局想清楚3.1 顶层架构RX→缓存→仲裁→调度→输出多路MIPI聚合的FPGA系统骨架逃不开下面这几步多路MIPI RX物理层接收各自完成deskew、字节对齐、协议解析每一路解析后的像素数据先做异步缓冲跨时钟域搬到统一的DDR时钟域多端口DDR控制器完成写操作把各路数据分别存到DDR的不同帧缓存区域输出调度逻辑按目标格式拼接、画中画、轮播去读DDR读出数据再经过输出协议打包成目标接口格式。第一步的MIPI RX侧每一路完全独立从DDR开始多路才真正发生交会。这里的关键设计问题有三个时钟怎么规划、DDR多端口怎么仲裁、输出调度的策略怎么选。3.2 时钟规划跨时钟域的坑必须提前埋好4路MIPI进来严格来说有4个不同的byte clock域。每一路的byte clock是根据它自己的链路速率恢复出来的与其他路没有相位关系。如果这4路数据直接丢进同一个FIFO跨时钟域时序必炸。我的做法是先把它变成两个域第一级是“各自byte clock域→DDR core clock域”用异步FIFO完成第二级是“DDR读时钟域→输出时序生成时钟域”同样用异步FIFO再兜一层。DDR core clock选在150MHz~200MHz之间输出时钟按目标接口计算比如HDMI 1080p60的像素时钟148.5MHz。时钟规划里最容易忽视的点是MIPI IP核的core clock频率必须覆盖最高的链路速率恢复时钟。举个例子1.5Gbps的lane速率DDR模式恢复的byte clock是187.5MHz那么IP核core clock至少要给到200MHz以上不然IP内部FIFO读不出来。3.3 DDR多端口读写仲裁策略怎么定如果DDR控制器只有一个接口喂给它的却是4路写、1路读那就必须做端口仲裁。Xilinx的MIG带AXI接口可以直接开多个AXI HP从端口但底层带宽是共享的紫光同创以及多数国产平台也有类似机制但仲裁逻辑得自己写。我在设计时习惯给每个写端口分配优先级申请权重的组合仲裁。简单场景用round-robin每个端口分配固定时间片实现简单但可能出现某一路帧率波动时的时间片浪费进阶做法是带缓冲的“请求-授权-完成”握手谁的数据在FIFO里积压到阈值就优先响应谁。再补充一个容易踩的坑DDR控制器选择页策略bank/row管理对多端口场景影响很大。多路视频流各自读写不同的地址区间如果访问跳跃太散bank预充电开销会把有效带宽吃掉两三成。所以帧缓存分配尽量按整帧连续区块分配不要按行交叉。3.4 三类聚合策略拼接、轮播、画中画取舍完全不一样“多路聚合”是个宽泛的词具体到需求调度逻辑天差地别整屏拼接多路图像拼成一个完整画面类似视频墙。这种需求对帧同步要求高各路帧率必须一致否则拼接处会出现撕裂调度是按区块读DDR输出时序复杂通常需要一个帧号仲裁各路必须保证同一帧号同时写入缓存再统一读出。单路轮播/切换某一时刻只输出一路由外部信号或内部定时器切换。这种最简单DDR只需1~2帧缓冲调度是分时的。画中画主画面一个窗口、辅画面多个小窗。调度逻辑要处理窗口坐标裁剪、缩放可能要缓存多行做双线性插值最复杂但对帧同步要求反而不高因为小窗丢一两帧无所谓。这个项目里常用的场景是第一种拼接因为它最考验前级帧同步和DDR调度的功底。后续的实现说明也以拼接为主。4. 核心模块实操从接收解析到输出恢复4.1 MIPI RX物理层用IP核还是自己写MIPI RX物理层有两条路。一是用厂商IP比如Xilinx的MIPI CSI-2 RX Subsystem、紫光同创的MIPI IP二是自己用LVDS SerDes搭D-PHY接收器。用IP核的前提是搞清楚IP支持的lane数、速率和协议版本以及占用的引脚资源。Xilinx的CSI-2 RX Subsystem只支持有限的传感器接口格式很多时候还需要一个“像素重建”模块来适配DT。紫光同创的MIPI IP按器件型号严格区分同一个VIP核换一颗更小的FPGA可能就不支持了这个要提前跟FAE确认。自己写PHY这条路只建议在两种情况下选一是IP核不支持你的目标lane速率二是你希望把PHY和协议解析完全自定义。自研PHY的核心是把进来的串行差分信号过serdes变成12bit/16bit并行数据然后做bit对齐和word对齐再交给协议状态机。注意自研方案在物理层跑300Mbps以下还行上到1Gbps以上信号完整性就很难保证非高手不要碰。4.2 协议解析状态机把字节流变成像素流收到并行数据后协议层逻辑的核心是一个有限状态机状态跳转大致是IDLE等待包头起始标志byte 00包头锁存DT、Word Count、ECC同时做ECC校验数据按Word Count计数读取载荷字节如DT是RAW10就把10bit像素按位拼好包尾校验CRC回IDLE。这里我有两个经验第一ECC校验失败不一定就要丢包。CSI-2规定ECC可纠正1bit错误如果是RAW格式的帧数据丢一个包会导致整行错位。实际调试中发现EEP类错误大多是信号完整性问题你更应该去查PCB走线和端接而不是在FPGA里打补丁。第二包头的Word Count通常包括行内的有效像素数乘像素位宽再除以8但不一定和你的行缓冲深度正好对齐设计行FIFO时要留裕量否则行尾会截断数据。4.3 数据打包进DDRRAW10的位对齐最烦RAW10格式每4个像素占5个字节这个非对齐结构是很多初学者栽跟头的地方。如果你直接把接收到的字节流水式写入DDR读出之后像素位序会乱。我的写法是先用一个移位寄存器把RAW10流拼成32bit对齐的数据以每4个像素为一组写入一个双口RAM然后按32bit从RAM读到DDR写数据总线上。仔细检查最后一行末尾的不足4像素填充不然会多出几个无效点导致拼接时出现左右错位。还有一个关键点是写入DDR的起始地址管理。我用一个帧base寄存器每收到一帧的FS帧起始包就锁定当前base然后行地址按行号累加写完一帧把“帧可读”标志置位。这样输出侧只认标志位避免读到写了一半的半帧。那半帧在拼接拼接输出的时候会出现一条明显的“撕裂线”别问我为什么知道。4.4 输出时序恢复让拼接画面稳定输出输出端要根据目标时序生成行场同步和解码信号。这里最容易忽略的是DDR读出的数据流是“突发”的而输出端是像素时钟驱动的匀速消费两者之间必须套一个“读出FIFO低水位预警”的机制否则画面会周期性闪一下。我用的是FIFO高水位/低水位prefetch机制当输出端FIFO低于阈值就启动一次DDR突发读高于阈值就暂停读取。帧缓存里最好保留1~2帧的预读空间这样DDR响应延迟不会造成FIFO空。第一次调这个逻辑时我天真地把FIFO深度设成一行像素结果每隔几行就抽风——后来改成预读半帧才彻底解决。输出侧同时要处理帧号对齐。拼接模式里4路视频的帧号必须一致才会被读出否则画面上下街区会错开。我的做法是在每路帧缓存头位置存一个帧号标记调度器在每帧开始前检查各路的帧号是否都等于期望值不相等就等在那边直到凑齐同时配合超时跳帧处理防止一路异常卡死整个系统。4.5 写Testbench别等上板才排查逻辑问题这个项目里我花了不少时间在仿真上尤其是MIPI解析和DDR调度这两块。写Testbench的核心思路是模拟完整的MIPI burst信号先产生hs_clk再产生data lane的LP→HS转换然后按CSI-2包格式组织字节流再给FPGA逻辑喂入。有一个容易忽略的点MIPI信号在仿真中要用真实速率产生DDR数据而不是直接给byte。用一个task按lane速率循环发送bit流可以帮助提前发现字节对齐逻辑的时序问题。DDR那块仿真最痛苦老板等结果等得急所以建议用DDR控制器的仿真模型配合简单BurSt读写测试把调度逻辑跑通。仿真跑到“能出图”之后上板定位速度快非常多。我见过不少项目上来直接上板调天天示波器戳结果发现是字节解析错位这种仿真里半小时就能发现的问题。5. 调试实录那些让我熬夜的坑和排障清单5.1 上电先看眼图再看波形高速MIPI信号上板调试顺序千万别反。先确认物理层正常再谈协议层最后才看图像。没有示波器的时候至少要用IP核自带的status寄存器看lane对齐状态和CRC错误计数。如果条件允许MIPI D-PHY的信号完整性要关注这几个参数输出差分电压VOD、共模电压VCM、上升下降时间和UI。C-PHY的信号更讲究因为三线之间互相干扰S参数仿真几乎是必修课。PCB设计时同组lane等长误差尽量控制在±5mil以内串行电阻端接要靠近接收端地平面不要被切断。5.2 deskew calibration失败九成是布线问题deskew失败的现象是IP核报link training error或者摄像头偶尔能出图偶尔黑屏。常见的排查路径检查clock lane和数据lane的走线长度差是否在规格内检查复位时序上电后要先给IP核复位等pll lock再启动IP核的enable信号检查I2C上摄像头是否成功输出HS信号传感器如果没有被正确配置会一直停在LP模式检查PCB上是否有过孔把同组lane的参考地切断。有一次调了整整一天最后发现是摄像头模组的软排线太长且没有包地导致高速信号衰减严重缩短排线后问题消失。这种物理层问题在FPGA逻辑里永远找不到答案。5.3 花屏、绿屏、错位的逐类排查花屏的根因五花八门我把遇到过的都列在下表里现象可能原因处理手段整个画面有规律斜纹行缓冲宽度与DT格式不匹配检查行像素数、Word Count和RAW10对齐左右半幅错位4像素打包时丢/多填充检查移位寄存器拼接逻辑特别是行尾某一通道周期性黑块DDR仲裁超时或带宽不足降低输出分辨率或调整仲裁优先级拼接接缝处撕裂帧号不对齐/帧同步失败检查帧号比较逻辑增加超时跳帧画面发绿/偏色DT识别错误RAW10当成YUV422检查CSI-2包头解析的DT映射表偶发花一帧包尾CRC错误但没丢帧确认DDR帧缓冲切换标志是否可靠开机不出图摄像头初始化失败I2C没配上用逻辑分析仪抓I2C看地址和寄存器值5.4 带宽不足的表现和判断方法多路聚合项目里DDR带宽不够的表现很隐蔽——它不会直接告诉你“带宽不够”而是表现为帧率下降、偶发丢帧、延迟抖动变大。我用一个简单的计数器在线观测DDR写请求对手持等待的时钟周期数占写总请求的比例。如果这个等待比例经常超过30%带宽就接近临界了。还有一个经验值实际DDR有效带宽通常只能做到理论值50%~70%控制器刷新、bank冲突、数据总线位宽利用率都会打折。带宽不够的解决方案按性价比排序优先优化仲裁策略和地址分布、其次降低DDR时钟频率但提高数据位宽利用率、最后才是换更宽的DDR或换芯片。6. 资源占用和性能评估别等综合后才发现资源不够6.1 四路1080p60的资源估算以Xilinx Artix-7 200T级别器件为例4路1080p60 RAW10聚合方案的资源占用大致如下资源估算占用占比XC7A200TLUT35k~50k25%~35%FF40k~60k15%~22%BRAM120~200个36Kb30%~50%DSP少量做插值或缩放时5%高速收发器0MIPI用LVDS Bank—这个估算已经包含了两级异步FIFO、行缓存、DDR写调度和输出FIFO。如果画面要做缩放DSP和BRAM会明显增加所以先做好功能裁剪。不同的逻辑设计水平对资源影响很大。同样的功能有人BRAM只用了80个有人用了200个——关键区别在“复用”。行缓存如果可以复用就比每路独立缓存省很多。6.2 延迟与抖动的实测经验在调试基本稳定后我实测过一版方案的端到端延迟从摄像头曝光到像素出现在输出接口大约是2.2ms其中MIPI接收和协议解析占了0.4msDDR写入排队调度读出占1.6ms输出FIFO缓冲占0.2ms。抖动在±0.3ms以内。降低延迟的最直接手段是减少帧缓存深度。如果接受不了1.6ms延迟可以用“行缓存行调度”方案也就是不等整帧写入DDR而是边写边读只留半帧到一帧的余量延迟能压到几百微秒。代价是抗DDR延迟波动的能力变弱帧率不稳时容易撕裂。另外一个关于轻负载的体会并行度越高的情况下DDR排队时间越不可控所以设计时尽可能把“多路写入”分散到不同的bank group而不是全部簇拥在同一个bank。某些DDR控制器支持channel/bank interleave多端口场景下一定要把这个打开。6.3 可维护性设计逻辑分层和参数化最后聊点工程上的体会。多路MIPI聚合这种项目写代码时一定要控制“把逻辑写死”的冲动。我习惯把每一路接收链封装成一个独立的模块外界只暴露帧同步、行同步、数据和帧号几个信号DDR读写仲裁独立成一个模块不绑定具体是哪路数据输出调度也是一个纯逻辑模块不知道摄像头型号只认“帧号有效像素”。这样每一层的替换和扩展都非常容易项目后期加一路8K或者换一颗摄像头传感器时不需要推倒重来。仿真Testbench也建议参数化把lane速率、分辨率、DT格式都定义成常量这样换方案的时候仿真环境可以复用。这个习惯在一次从RAW8换到RAW10的场景里帮我省了两天时间。7. 组合拳中的细节几个我想多说两句的经验7.1 传感器配置I2C初始化别写在FPGA逻辑里很多做FPGA的朋友是把摄像头I2C配置写在逻辑里的用状态机去写传感器寄存器。这在调试时很痛苦改一组寄存器要重新综合一次一个小时就没了。我的做法是FPGA逻辑里只留一个串行配置接口类似于用串口或PCIe把配置数据下发寄存器列表放到ARM端或PC端上位机里。这样摄像头初始化参数可以动态下发、随时调整不用动一次就烧一次板。即便是没有软核的小FPGA也可以外接一颗MCU做配置下发成本不高调试收益很大。7.2 复位和错误处理别让一个小错毁掉整个系统的稳定性多路视频系统任何一路出现异常都可能导致整个链路罢工。我的经验是设计“隔离”机制某一通道连续出错比如ECC错误计数超过阈值、超时无帧就自动把它从DDR调度仲裁里摘出去其他路继续工作同时上报一个状态寄存器的错误位。系统可以做中断告诉上层但绝对不要因为一路的问题而导致整体黑屏。7.3 板卡上电时序先低压再高压FPGA项目里MIPI相关的另一个坑在电源。MIPI lane电压通常是1P2V或0P9V级别而摄像头模组的IO电压可能是1P8V甚至1P5V电源上电顺序不对会导致lane烧毁或者IP核link训练异常。我一般要求硬件工程师把摄像头、FPGA的MIPI bank电压、I2C上拉电压按顺序错开上电后用状态机确认各电源稳定再启动MIPI MAC核。别小看这个多路项目里经常出现“单独测每一路都好一接四路就挂”的诡异现象最后查出来是某一组电源纹波在4路同时工作时过大。8. 写在最后这个项目真正让我成长的地方做多路MIPI聚合这个项目最大的收获其实不是代码量而是建立了整套“从物理到协议到系统”的排查思维。以前遇到问题会本能地往逻辑上猜现在会把链路分成物理层、协议层、缓存层、调度层逐层看状态寄存器很少再做无头苍蝇式的乱试。如果要给后来者一句忠告那就是第一接口规格和带宽计算一定放在最前面一遍算清楚比后面反复调快得多第二尽量把调试观测点做在FPGA内部比如CRC错误计数器、带宽利用率计数器、FIFO水位计这些比示波器还管用第三不要迷信IP核“开箱即用”每个平台、每个型号的IP都有脾性必须先拿小分辨率验证再往上冲。多路视频聚合这个方向还有非常多可以展开的地方加AI推理、叠加异形拼接、做动态ROI裁剪、接PCIe DMA到上位机……每一条路都不缺挑战。希望这篇分享能帮你少踩几个我已经替你踩过的坑。
返回列表