ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MIPI CSI-2协议实战:从D-PHY物理层到FPGA调试全解析

MIPI CSI-2协议实战:从D-PHY物理层到FPGA调试全解析 前几天帮客户调试一块RAW10的Sensor板子又把MIPI CSI-2从头到尾捋了一遍。这两年做图像采集和FPGA视频处理MIPI CSI-2基本上是我打交道最多的接口协议手机摄像头、车载环视、工业检测、内窥镜几乎每个项目都绕不开它。很多刚接触这块的工程师一上来就被D-PHY、Lane、ECC、CRC这些术语搞懵其实这套协议远没有想象中那么玄乎核心链路就三件事物理层把差分信号传好、协议层把字节包拆对、应用层把像素数据拼出来。这篇就结合我自己的项目经验把MIPI CSI-2从协议结构到实际调试完整过一遍适合正在做Sensor驱动、FPGA图像采集或者硬件设计的工程师参考哪怕你是刚入门看完也能对整条链路有个清晰的骨架。1. MIPI CSI-2是什么从并口到串口的演进1.1 为什么不是DVP早年的摄像头接口基本都是DVPDigital Video Port一组并行数据线加上PCLK、VSYNC、HSYNC把像素数据直接并行搬出去。我最早做OV5640就是这种接口8位并口一接逻辑分析仪一挂数据清清楚楚。但DVP的瓶颈非常明显频率一高并行数据线之间的skew就压不住PCB走线长度稍有偏差采回来的数据就错位而且十几根线占面积、费引脚、EMI还大。到了500万像素、1080p60这种带宽需求DVP基本就到头了。MIPI CSI-2本质上就是把并行的图像数据变成高速串行差分信号来传输。差分信号天生抗共模干扰串行之后线对数量骤减一根时钟lane加一到四根数据lane就能扛住几Gbps的吞吐。同样的1080p60 RAW10数据DVP可能要把PCLK干到一百多兆而且布板痛苦CSI-2分成四对差分线每对跑几百兆从容得多。1.2 协议栈的三层结构MIPI CSI-2协议从上往下分三层应用层Application Layer、协议层Protocol Layer、物理层Physical Layer。应用层决定数据怎么被解释比如RAW10像素排列、YUV格式怎么映射协议层负责把像素数据装进标准格式的包里加上包头、ECC、CRC物理层则干最底层的脏活累活用D-PHY规范把0和1变成差分线上的电平翻转。打个比方协议层是快递公司给货物贴面单、写地址物理层是货车和公路。只要面单格式统一货用什么车运都行。这也是为什么CSI-2可以先不换协议层只把物理层从D-PHY升级到C-PHY、M-PHY带宽容错能力就扩展了。做调试时最常用到的其实是协议层的包结构以及物理层的HS/LP状态切换这两块理解了后端的FPGA解析和SoC驱动调试就顺了。2. 协议核心包结构、通道与数据格式2.1 包结构与ECC/CRCCSI-2的数据链路里所有信息都通过两种包传输短包Short Packet和长包Long Packet。短包只有4字节前3字节是包头Packet Header第4字节是包尾Packet Footer作用主要是同步和控制比如帧起始Frame Start、帧结束Frame End、行起始Line Start、行结束Line End。长包包头同样是3字节后面跟着可变长度的Payload数据最后再跟2字节CRC校验。包头3字节的布局非常紧凑第1字节低6位是Data TypeDT标识这个包装的是什么数据第1字节高2位是Virtual Channel虚拟通道用于在同一条物理链路上复用多路数据流第2、3字节是Word CountWC表示长包Payload有多少个16位字。短包没有Payload和CRC但它多了ECC纠错码。ECC是很多新手忽略的细节。它在包头第3字节采用汉明码对前两个字节做纠错保护能纠正单比特错误、检测双比特错误。实际调试中如果包头ECC报错频繁说明链路信号质量有问题多半是走线等长没做好或者阻抗不连续。长包的Payload尾部跟着2字节CRC16用来校验每一个字节的数据完整性CRC不过就直接丢包。所以协议层本身是带完整差错防护的遇到图像偶尔花一下先查是不是CRC在丢包而不是急着改Sensor寄存器。2.2 Lane、虚拟通道和帧时序CSI-2的数据lane可以是1、2或4根加上固定的一根时钟lane。多根lane传输时字节是轮询分发的第0字节走lane0第1字节走lane1第2字节走lane2第3字节走lane3第4字节又回到lane0。因此lane数越多每一根线跑的速率越低对PCB要求也相应宽松但接收端要做lane对齐处理各lane之间的skew。虚拟通道VC是在同一个CSI-2链路上承载多路摄像头或者多路数据流的关键机制。比如双摄方案里两颗Sensor可以共用一个MIPI接口一个配VC0一个配VC1软件端根据VC号把数据拆分到不同通路。FPGA接收端在做包解析时也要把VC字段解出来作为AXI-Stream的sideband信号往下传否则上层没法区分数据来源。帧时序上一帧图像的传输顺序是Frame Start短包 - Line Start短包 - 行长包 - Line End短包 - ... - Frame End短包。有些Sensor会把Line Start/End省略只保留FS/FE和行长包。解析FSM只要抓短包和长包的交替就能还原出图像的行列结构。需要注意的是每两个包之间都有一个EoTEnd of Transmission状态物理层回到LP模式然后下一个包再发起新的HS传输。2.3 RAW数据的打包与带宽估算图像格式里最常碰到的就是RAW8、RAW10、RAW12。RAW8最简单一个像素一个字节直接装进长包Payload即可。RAW10则是一个像素占10bit四个像素凑成5个字节传输顺序是P0的高8位、P1的8位、P2的8位、P3的高8位最后再补一个字节里面藏着P0/P1/P2/P3各自剩下的2个低位bit。这个打包逻辑在FPGA端拼图时特别容易写错我见过有同事按字节流硬解RAW10解出来的图像全是错位噪点后来查出来就是位序没对齐。带宽估算也是前端设计必做的功课。以1080p30 RAW10为例1920 x 1080 x 30 x 10bit 622Mbps这是纯有效像素。加上行消隐和帧消隐实际链路速率还要除以大概是70%~80%的利用率算下来约800~900Mbps。如果用2 lane传输每lane大概跑450MbpsD-PHY v1.2的1.5Gbps单lane上限绰绰有余如果上4 lane每lane只要跑200多Mbps非常稳。反过来如果是4K60 RAW12那带宽就到 3840 x 2160 x 60 x 12 x 1.25消隐开销 7.5Gbps左右这就必须用4 lane同时还得把每lane速率推到2Gbps以上普通D-PHY v1.2已经勉强得上v2.0或者考虑压缩方案了。3. 硬件实现引脚、波形与布线3.1 引脚定义与HS/LP两种状态MIPI CSI-2物理层最核心的引脚就是时钟lane和数据lane每个lane都是一对差分线分别叫CLK_P/CLK_N、DATA0_P/DATA0_N直到DATA3_P/DATA3_N。差分对里没有绝对地信号传输靠的是正负两根线之间的电压差。除了数据lane通常还有一组I2CSCCB用来配置Sensor寄存器这就是CCI接口它不属于CSI-2数据通道但任何CSI-2摄像头都离不开它因为Sensor的分辨率、曝光、增益全要靠I2C来设置。D-PHY的lane有两种工作状态LPLow Power和HSHigh Speed。LP状态下差分对的两根线分别独立拉高或拉低电压摆幅是0~1.2V用于传输控制信号比如SoTStart of Transmission、EoTEnd of Transmission的建立以及从HS退出后的复位。HS状态下两根线以差分方式驱动电压摆幅只有200~300mV用来高速搬运数据。LP和HS之间的切换有一整套时序参数比如T_LPX、T_HS_PREPARE、T_HS_ZERO、T_HS_TRAIL这些参数在调试时如果设错轻则信号不稳定重则Sensor直接不给输出。3.2 示波器实测信号特征调MIPI最常用到的仪器就是示波器最好用1GHz以上带宽的配合差分探头或者利用示波器自带差分通道。实测HS波形时最醒目的特征就是一小段一小段的突发Burst每一段对应一个包突发之间是LP状态的高电平。用示波器测量时要先触发到SoT区域才能稳定看到完整突发。测出来的HS信号差分幅度正常应该在200~400mV之间太小可能是阻抗不匹配或者链路损耗太大太大则可能是驱动端串阻没加好或者匹配电阻异常。时钟lane和数据lane之间的相位关系也要留意D-PHY是DDR模式数据在时钟的上下沿都有效。用示波器看的时候最好把CLK和DATA叠在一起确认数据切换点是否落在时钟沿附近。如果数据lane与时钟lane的skew超过0.3个UI单位间隔接收端就会很难采样这时候优先检查PCB等长而不是调Sensor寄存器。3.3 PCB布线经验MIPI CSI-2的PCB布线核心三个词差分、等长、包地。差分阻抗控制在100Ω通常采用100Ω差分对表层微带走线线宽和间距需要根据叠层计算别凭经验拍脑袋我见过不少板子线宽和间距随便填最后阻抗跑到85Ω信号质量明显变差误码率直线上升。等长要求上同一lane内的P/N两根线要尽量等长误差控制在5mil以内不同lane之间以及lane与时钟lane之间的等长误差也要尽量控制在50mil以内。注意P/N对内等长比lane间等长更重要因为对内不等长直接产生共模噪声而lane间skew还能靠协议层的lane对齐机制去纠正一部分。走线要整段参考完整地平面远离开关电源、时钟芯片、DC-DC电感这些干扰源最好在顶层加包地铜皮特别是穿过连接器的那一段。另外很多方案会在Sensor端加串阻官方推荐通常22Ω左右具体看手册来抑制振铃。有ESD防护需求的ESD管必须靠近连接器放置但要注意它的寄生电容不能太大否则会吃掉信号边沿。高速信号线上别串联磁珠这是很多人容易踩的坑。4. SoC平台实战RK平台CSI-2摄像头调试4.1 硬件连接与Sensor初始化RK平台调试MIPI摄像头第一步是确认硬件连接。Sensor的MIPI差分对直接连到SoC的CSI-2 Host控制器I2C挂在对应总线上MCLK由SoC提供复位和电源使能由GPIO控制。上电顺序很关键先给Sensor电源再给MCLK时钟最后释放复位这个顺序错了Sensor可能锁死或者I2C从机不响应。我的习惯是查看Sensor数据手册的上电时序图按最小延迟要求写一个上电脚本先用I2C工具单独验证。很多基于Linux的RK平台有一个通用Sensor驱动框架通过设备树描述每个Sensor的I2C地址、reset-gpio、pwdn-gpio、时钟频率、lane数和格式。上电之后驱动会通过I2C读取Sensor的chip id来确认I2C通信正常。如果这一步卡住先量I2C上拉、测Sensor供电和复位信号用i2cdetect扫一下设备地址大多能快速定位。4.2 设备树与驱动配置RK平台以rk3588/rk3568早期内核为例配置一个CSI-2摄像头设备树里主要有三块Sensor节点、MIPI DPHY节点、MIPI CSI2 Host节点。Sensor节点里要注明接口类型、lane数、数据格式、时钟频率并且把电源时序控制引脚引出来。下面是一份简化到能看懂的OV5647节点写法i2c2 { status okay; ov5647: ov564736 { compatible ovti,ov5647; reg 0x36; clocks cru CLK_MIPICAM_OUT; clock-names xvclk; pinctrl-names default; pinctrl-0 mipim0_camera1_clk; rockchip,camera-module-index 0; rockchip,camera-module-facing back; rockchip,camera-module-name default; rockchip,camera-module-lens-name default; port { ov5647_out: endpoint { remote-endpoint mipi_dphy_in; >media-ctl -p然后设置数据通路并配置格式media-ctl -r -l m00_b_ov5647 10-0036:0 - rockchip-mipi-csi2:0 [1] media-ctl -V m00_b_ov5647 10-0036:0 [fmt:SRGGB10_1X10/1920x1080] v4l2-ctl --set-fmt-videowidth1920,height1080,pixelformatRG10P v4l2-ctl --stream-mmap --stream-count1 --stream-toframe.raw把这些做完再拿RAW文件用工具转成图片就能确认图像链路是否打通。最常见的问题是dmesg里报MIPI CSI2: No data received之类的错误原因可能是一开始的enable GPIO没拉对、Sensor没有输出或者CSI2 PHY没有正确使能。遇到这种问题我一般先用示波器量Sensor的MIPI输出是否有HS突发再量MCLK是否起振然后看I2C能否读到chip id这三步走完基本能定位到是物理层还是协议层。5. FPGA实现MIPI CSI-2接收架构与代码5.1 接收链路划分很多图像采集项目用FPGA做主控需要直接接收MIPI CSI-2 Sensor的数据再转换成并行或AXI-Stream接口送给后端。FPGA端的接收链路通常分成四块物理层D-PHY RX、字节同步与lane对齐、协议层解析、像素组装与接口输出。D-PHY RX是最麻烦也最容易踩坑的部分它负责把差分信号恢复到字节流。如果FPGA自带MIPI硬核比如Xilinx的MIPI CSI-2 RX Subsystem、Lattice的MIPI硬核优先用硬核稳定且省资源。没有硬核的话常见的做法是用LVDS差分IO加相位偏移IODELAY来采样或者用GTP/GTX高速收发器去接收。用GTP的话要小心参考时钟和CDR配置lane间skew还要靠可编程延迟补偿。用普通IO接收只能跑到几百Mbps适合低分辨率低速场景。字节同步环节做的事是找到字节边界。D-PHY传输时没有独立的帧同步信号接收端要靠SoT码型来建立字节对齐。具体来说SoT在物理层发送一个固定的0xB8前导码型接收端在比特流里不断移位搜索这个码型找到后就把后面的数据按字节切分。多lane模式下各个lane独立完成字节同步之后还要做lane对齐因为物理走线长度不同会导致不同lane到达的延迟不同。这个对齐一般靠每种包前的固定码型或者HS的同步头来完成。5.2 核心FSM实现协议层解析主要是一个包解析状态机它的工作就是识别短包和长包解析包头保存Payload再校验CRC。下面是一个很典型的包解析FSM片段用Verilog写出来大概长这样localparam IDLE 3d0; localparam HEADER 3d1; localparam PAYLOAD 3d2; localparam CRC_WAIT 3d3; localparam ERROR 3d4; reg [2:0] state; reg [1:0] vc; reg [5:0] dt; reg [15:0] word_count; reg [15:0] counter; reg [7:0] byte0, byte1, byte2; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; vc 2d0; dt 6d0; word_count 16d0; end else begin case (state) IDLE: begin if (byte_valid) begin byte0 byte_in; state HEADER; end end HEADER: begin if (byte_cnt 2d0) begin byte1 byte_in; byte_cnt 2d1; end else if (byte_cnt 2d1) begin byte2 byte_in; byte_cnt 2d2; vc byte0[7:6]; dt byte0[5:0]; word_count {byte_in, byte1}; if (is_short_packet(byte0[5:0])) state IDLE; else state PAYLOAD; end end PAYLOAD: begin if (counter 16d0) begin state CRC_WAIT; end else begin payload_fifo_valid byte_valid; payload_fifo_data byte_in; counter counter - 1b1; end end CRC_WAIT: begin if (crc_byte_cnt 2d1) begin state IDLE; crc_byte_cnt 2d0; end else begin crc_byte_cnt crc_byte_cnt 1b1; end end default: state IDLE; endcase end end这里我没有加上ECC纠错和CRC校验的具体逻辑但思路很清楚IDLE等第一个字节HEADER凑齐三个字节后判断包类型短包直接回到IDLE长包则进入PAYLOAD按WC计数搬数据最后吃掉两个CRC字节回到IDLE。FSM里最容易被忽略的是包头三个字节的时序CSI-2包头三字节是连续到达的每一拍一个字节所以这里要用一个byte_cnt来计数而不是每个字节占用独立状态。像素组装放在协议层后面。以RAW10为例如果你在协议层按字节流抛给后面就需要一个打包模块把5个字节还原成4个像素。最简单的方式是把字节流写入一个小FIFO攒够5字节做一次移位输出4个像素。这里要注意解析FSM输出的字节流速率是每lane一个字节每拍多lane合路以后速率会翻倍所以FIFO的读写位宽要按实际吞吐来设计不然必然溢出丢数据。5.3 带宽规划FPGA做MIPI接收之前一定要先把带宽算清不然做到一半发现资源不够或者时序收敛不了非常痛苦。还是拿1080p60 RAW10举例子有效数据速率是1920 x 1080 x 60 x 10 1.24Gbps。如果走4 lane每lane是310Mbps加上包头、CRC、消隐等开销物理链路大概到1.5Gbps。对于一块入门级FPGA用普通IO加IODELAY采样的方案单lane跑310Mbps问题不大但时序要仔细约束。如果用2 lane每lane要跑到620Mbps很多低端FPGA的普通IO就吃力了得上高速收发器。另外一个容易被低估的是内部逻辑时钟频率。如果后端输出AXI-Stream走32bit位宽内部逻辑频率至少要到1.24Gbps / 32bit 38.75MHz看起来不高但如果要同时做ISP预处理、缩放、多路缓存逻辑频率就会被推到一两百兆这时要留意整个数据通路的FIFO深度和AXI带宽避免突发流量打满总线。还有一个经验多lane合路后数据不是均匀的中间有消隐空隙后端FIFO深度至少要能吸收一个最长行的突发长度不然行尾数据会丢。6. 调试排障速查这些年踩过的坑6.1 信号链路问题信号链路问题排在第一位因为大部分MIPI调试卡住都出在物理层。表现形式是I2C能通、Sensor寄存器能配、但就是没图像。这时候不要怀疑协议先上示波器量Sensor输出端有没有HS突发。如果Sensor根本没发起SoT大概率是上电时序、复位保持时间、MCLK频率不对或者是Sensor处于软件shutdown状态。如果Sensor有输出但SoC抓不到检查D-PHY的供电和dphy配置是否开启有些平台DPHY的PLL没有lock会导致无法接收。还有一种是链路中途经过FPC连接器或者转接板信号完整性变差这时波形可能看起来还有但眼图已经闭合接收端采不到正确数据。我的经验是优先缩短FPC长度或者在连接器附近加串阻改善反射少去调Sensor驱动电流那往往是治标不治本。6.2 图像质量问题图像最为常见的问题是花屏、错行、颜色不对。花屏一般有两个方向一个是信号链路的CRC错误导致丢包表现为画面撕裂、局部花屏另一个是传输格式和解析格式不匹配比如Sensor输出RAW10后端按RAW8解析画面就是花噪。颜色不对一般和Bayer模式排列有关同样一个RAW10数据按BGGR输出但后端按RGGB解析图像就会偏色红蓝通道互换了。错行问题则多半是行同步和帧同步解析出错。有些Sensor在帧内没有发Line Start/End而是只靠Frame Start和连续的长包来指示行如果FPGA解析时把行长包当成了整帧数据出来的图像就是所有行拼成一个超长条。这时候要去核对FSM对包类型的判断以及在每个包解析完成后是否正确复位行的列计数器。6.3 排障流程与速查表我调MIPI CSI-2已经养成了一套固定流程基本能覆盖八成问题先看硬件量电源、时钟、复位然后确认I2C读chip id再抓波形确认Sensor有没有HS突发接着看SoC或FPGA的接收log确认是否有包解析错误最后才怀疑图像格式配置。每换一个环节都先排除掉上一环节的可能性避免在错误层次浪费时间。下面这张表是我经常翻的速查表列几个高频问题大家可以参考现象可能原因排查方向I2C不通Sensor未上电/复位未释放/地址错误量电源时序用i2cdetect扫地址无图像且无HS波形MCLK未起振/Sensor软复位被拉死量MCLK频率检查GPIO配置无图像但HS波形正常DPHY配置错误/lane数不匹配核对设备树data-lanes检查DPHY PLL图像花屏CRC丢包信号质量差示波器查眼图缩短链路调串阻图像偏色Bayer顺序不匹配换Bayer pattern排列图像左右错位lane映射错误或lane对齐失败核对data-lanes编号检查lane对齐逻辑分辨率和帧率不对WC解析错误/Sensor输出格式不符抓包检查包头WC和DT值靠这张表基本可以应对90%以上的CSI-2调试场景。还有一个我在FPGA调试中反复碰到的坑Sensor上电到真正输出第一帧图像中间可能隔了几百毫秒甚至几秒因为内部要做PLL锁定、曝光初始化、行场同步建立。很多工程师一上电立刻去抓包什么都没抓到就怀疑FPGA逻辑错了其实只要在FPGA里加一个fallback超时重传机制同时拉长示波器触发时间就能看到Sensor是慢慢起来的。这个细节建议做接收端的朋友都记住能帮你省下大量排查时间。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进