
上个月调完最后一块板子总算把MIPI CSI-2摄像头采集链路和MIPI DSI屏幕输出在Xilinx Zynq-7035上同时跑通了。这套东西从看协议文档到实际出图前后折腾了大概三周期间把MIPI DPHY这几个字来来回回翻了个遍。回头看真正卡人的反而不是Verilog代码本身而是对物理层那几个电气参数、时序切换逻辑的理解以及Xilinx FPGA上不同实现路线的取舍。这篇文章就以我手头这个项目作为案例把MIPI DPHY接口在Xilinx FPGA上的实现思路、选型依据、硬件布局、调试方法完整记录下来。适合谁看一种是刚到类似项目、正在纠结FPGA怎么接MIPI摄像头/屏幕的工程师另一种是已经有FPGA基础、想搞清楚DPHY物理层到底在传输什么东西的朋友。文章不打算从零教Verilog怎么写而是直接给出一套经过验证的决策框架和避坑清单让你在动手前就能把方案定稳。1. 项目背景为什么要用FPGA去碰MIPI DPHY1.1 这个项目要解决什么实际问题先讲清楚这个案例的任务边界。我这边是一套工业视觉检测设备的前端采集和中转显示摄像头是MIPI CSI-2接口的CMOS Sensor屏幕是MIPI DSI接口的工业屏中间的处理核心是一块Xilinx Zynq-7000系列的FPGA。任务需求拆开有四条把CSI-2的RAW10图像数据逐帧采集到DDR里在FPGA逻辑里做一次图像处理这里做的是坏点校正和直方图均衡处理完的图像通过DSI接口送到屏幕上实时显示整个链路端到端延迟控制在两帧以内。这套需求如果后端不是要求做处理低延迟直接用一颗带MIPI CSI/DSI的SoC平台比如RK、全志的方案会轻松太多。但工业现场往往有一些自研算法、Sensor兼容、非标分辨率的要求SoC的ISP链路反而不够灵活这时候FPGA的价值就体现出来了。我在查资料的时候也看到不少团队并不是非要用FPGA而是手上的SoC平台点不亮某些屏、接不了某些Sensor才被迫回到FPGA层面去兜底。所以这篇文章虽然主要讲FPGA但很多思路对RK平台点MIPI屏、Linux DRM调试竖屏改横屏也有参考价值后面我会专门延伸。1.2 从热词里看到的共性困惑在准备这篇案例分析之前我特意翻了一圈和MIPI相关的热门搜索问题发现最密集的问题集中在几类。第一类是FPGA怎么实现MIPI。这个问题其实很笼统不同人问的可能是完全不同的东西。有的人问的是物理层D-PHY怎么出信号有的人问的是CSI-2协议怎么解包还有人问的是我用GTP能不能直接接摄像头。问题笼统的背后是对协议分层没有概念——MIPI CSI-2/DSI本身就是分层的物理层D-PHY负责比特流协议层负责包结构和高层语义。如果连自己要解决的是哪一层都没分清看再多资料也找不对方向。第二类是帮忙看一下波形/时序比如mipi信号波形、mipi时钟波形这种搜索。这类说明人已经到调试阶段了示波器上能看到波形但不确定波形是否合格。这部分我在后面的调试章节会给出实际判断标准。第三类是mipi接口引脚定义图、mipi布线、st7701s mipi。一看就是硬件工程师在器件选型阶段需要确定管脚复用、电平标准、PCB走线方式的工具型问题。这类问题看似基础实际最容易翻车因为DPHY的电气特性和普通LVDS、LVCMOS接口完全不一样。1.3 先对号入座你的项目属于哪一类DPHY应用在选方案之前至少要对号入座。我在案例里把DPHY应用按方向与速率分成几类方便大家对照CSI-2摄像头输入从图像Sensor接收数据常见分辨率从VGA到4K不等。DSI显示输出往MIPI屏幕发送图像数据和命令常见应用于手机屏、工业屏、车载屏。速率上VGA级别分辨率用2 lane/1Gbps就够1080p60的CSI-2一般需要4 lane每lane在1Gbps到1.5Gbps之间。如果你的需求是4 lane×1.5Gbps以上直接绕过SelectIO自研方案优先考虑高速收发器或者外部桥接芯片。我后来和几个做外包的同行聊发现很多人最后走的都是CPU/SoC FPGA的组合SoC负责MIPI协议FPGA负责算法或者FPGA只做预处理输出RGB/LVDS给后端SoC。真正需要在FPGA上直接出MIPI信号的场景并没有想象中多但既然要出方案就得定稳。2. DPHY物理层到底在传输什么写代码前的必修课2.1 Lane结构时钟与数据的DDR关系MIPI DPHY最容易被忽视的一点是它的时钟通道永远是DDR模式的。也就是说在HS高速传输期间Clock Lane发送的是差分时钟数据Lane在时钟的上升沿和下降沿都会输出数据。这条规则决定了你在FPGA侧不能用简单的单沿逻辑去采样要么用ISERDESE2这类DDR解串原语要么让bit clock跑到数据率的两倍频率然后单沿采样——后者在FPGA里通常不现实。一组DPHY链路一般是一条Clock Lane加一条或多条Data Lane。CSI-2和DSI都支持1、2、4条数据Lane的配置。多Lane之间采用每Lane字节交错的方式传输协议数据所以接收侧需要先把每个Lane的串行比特流解成字节再按Lane数量做字节级的对齐重组。Clock Lane在HS模式发送的DDR时钟和数据是源同步关系也就是说时钟跳变沿和数据有效沿之间有一个严格的训练相移接收侧完全可以用时钟采样数据的方式恢复比特流。我在FPGA里一般把采样时钟相位当成一个可调参数通过IDELAYE2或者MMCM的相移能力去搜索最佳采样点。这块后面会详细说。2.2 LP和HS一根线上的两种人格DPHY的每条Lane包括Clock Lane实际上共享同一对物理引脚。它有两种工作模式LPLow Power和HSHigh Speed。LP模式单端信号逻辑电平在0到1.2V附近速率低主要用于传输命令、状态切换和总线进出HS模式差分信号摆幅典型值只有200mV左右共模电压约200mV速率高用于真正的图像数据流。这两种模式在硬件上的信号特征是截然不同的。LP状态机里有一组定义LP-00、LP-01、LP-10、LP-11分别由P/N两条线上的电平组合而成。HS传输的启动序列SoTStart of Transmission就是先看到LP-11到LP-01的跳变然后进入HS-0再发同步序列00011101也就是0x1D最后才跟着包数据传输结束时进入HS-Trail回到LP-11。这段描述和我在示波器上看到的波形完全对得上。如果你在调试时发现某条Lane一直卡死在某个电平或者HS进去了出不来多半就是LP状态机与HS时序切换没配对。2.3 几组必须提前列出来的参数协议文档里参数很多但工程上真正要记住的其实就几组。以线速率Bit Rate为核心的口诀是1Gbps/lane时bit time是1nsDDR时钟频率是500MHz数据Lane之间的skew要求非常苛刻同一时钟沿驱动的各Lane数据在接收端允许的时间偏差通常在0.2个UI以内。按1Gbps算就是200ps量级HS差分摆幅典型200mV接收端需要能识别最低约70mV到100mV的有效信号这意味着用普通LVDS输入阈值约±100mV勉强可行但余量很小。我自己在硬件规格书阶段就把这些数值列成一张表投板前和Layout工程师逐项核对。后面调试时这张表直接决定了先从物理层查还是先从协议层查省了无数冤枉路。3. 基于Xilinx FPGA的DPHY实现三条路线怎么选3.1 原厂IP核、SelectIO自研、高速收发器在Xilinx平台上做MIPI DPHY大概有三条路线。第一条是使用Xilinx官方的MIPI D-PHY IP核。这个IP核支持特定器件系列覆盖UltraScale/UltraScale为主对7系列的支持比较有限。它在里面封装了物理层的收发功能但协议层CSI-2/DSI包解析还得自己写。我在Zynq-7035上没有直接用官方IP主要原因是器件支持不顺而且我需要对采样相位做更灵活的干预。第二条是用7系列HP Bank的SelectIO原语自己搭D-PHY收发。ISERDESE2、OSERDESE2、IDELAYE2、ODDR这些原语组合起来确实能做到1Gbps/lane级别的收发链路。这条路线我最终采纳了因为它能完全掌控采样相位和链路训练逻辑便于对接非标Sensor或者低分辨率屏幕。缺点也很明确工作量最大时序收敛全靠自己。第三条是用GTP/GTH等高速收发器。不过有个前提GTP/GTH的物理层是CML电平和MIPI DPHY的HS电平不直接兼容通常需要在外部加电平转换芯片或者用AC耦合加偏置的方式把电平拉到收发器可识别的范围链路协商和LP信号处理也要在外部完成。这个方案适合4K高帧率场景PCB复杂度和BOM成本都明显更高。3.2 我最终选择的RX链路结构在7系列FPGA上一条完整的MIPI D-PHY接收Lane电路结构大致是这样的差分输入先从板级进入FPGA的HP Bank引脚HP Bank内部通过IBUFDS将差分信号转成单端逻辑保留差分输入特征数据进入IDELAYE2做180度范围内的细调相这是我用来搜索眼图中心的关键工具再用ISERDESE2做DDR解串将高速串行数据转成8位并行数据并行数据进入一个自建的bit alignment状态机搜索SoT同步序列0x1D完成bit slip多条Lane汇合后做lane alignment再把字节流喂给上层的CSI-2或DSI协议解析模块。这里有个细节非常容易被忽略ISERDESE2在DDR模式下解出的并行数据和实际比特流之间的对应关系是对齐后才成立的。你必须在接收端先跑一个训练过程找到正确的8bit相位这个动作就是bit slip。如果发现解出来的包头永远是乱的先检查bit slip有没有真正锁住而不是急着怀疑协议解析代码。3.3 为什么我给TX端加了一颗外置PHYRX我用SelectIO硬扛下来了但TXDSI到屏幕我选择了一个混合方案FPGA内部做DSI协议数据包的组织和像素流转换把并行RGB/命令数据交给一颗支持MIPI DSI输出的外置PHY芯片由它完成真正的D-PHY物理信号驱动。原因是DSI到屏幕这条链路需要同时处理LP命令和HS视频流。如果全部在FPGA里用普通IO模拟LP信号的时序精度、HS差分信号质量都很难保证尤其屏幕那头还涉及读回状态寄存器需要真正的双向LP通信。外置PHY把最麻烦的物理层隔离出去FPGA只需要把协议逻辑写对整体风险大幅下降。如果你想在FPGA里全自研DSI输出必须同时具备三块能力并行像素转字节、DSI包封装含短包/长包、ECC/CRC计算、以及物理层的LP/HS状态机控制。这个工作量比只做RX接收要大得多所以我最终给出的组合是FPGA做协议外置PHY做物理承载实测下来也是最稳的。4. 案例核心CSI-2摄像头采集链路的设计与实现4.1 Sensor配置与Lane速率估算拿到Sensor之后第一步不是写代码而是翻开datasheet确认输出格式和速率。我这个项目用的Sensor是RAW10格式分辨率1920×108030fps。可以先算一下基本吞吐1920×1080×30×10bit约622Mbps。如果用4条Data Lane每Lane大约155.5Mbps这在SelectIO能力范围内绰绰有余改成2条Lane每Lane约311Mbps也依然可行。最终我选了4 Lane原因并不是带宽不够而是给后续升级留余量。这里一定要把格式算清楚很多人看到1Gbps/lane就说每秒能传1Gbit图像实际上RAW10、RAW12、YUV422编码后的有效数据率差异非常大包开销长度头、校验、消隐区也会占掉一部分带宽。Sensor初始化方面CSI-2的Sensor一般通过I2C配置寄存器输出是连续的MIPI数据流。FPGA侧需要做的是等待链路稳定后从数据流里识别帧起始FS、帧结束FE、行起始LS和行结束LE这几个短包然后才开始取数。行消隐期间HBlank/VBlank数据Lane不会有有效像素但物理层仍然在跑解包模块必须能正确处理空闲状态。4.2 字节解包与像素重组CSI-2的包结构是先发32bit packet header里面包含8bit Data Type、16bit Word CountWC、8bit ECC后面跟着WC个字节的有效数据最后是16bit CRC。对RAW10来说4个像素被打包成5个字节。这是从字节流恢复像素时的关键pixel[9:0]需要按位拆散到5个字节里。我的做法是先写一个位拼接模块把连续5字节重排成4个10bit像素再去做后续的图像处理。如果解出来的像素颜色不对、出现红绿蓝乱序多半是Byte-to-Pixel的映射反了或者Lane重排顺序和Sensor手册中对不上。CSI-2规定Lane0是基准Lane其他Lane可以任意映射但多数Sensor默认按物理顺序输出。调试时我习惯用一块纯色板先做白平衡一旦发现色偏基本就能快速定位是不是Lane映射或者位序问题。下面是一段结构示意展示RAW10位拼接模块的核心思路不是完整代码但方向是对的// 每5个输入字节产出4个10bit像素 // 输入byte_stream[7:0]同步信号byte_valid // 输出pixel_out[9:0]同步信号pixel_valid reg [39:0] shift_buf; always (posedge clk) begin if (byte_valid) begin shift_buf {shift_buf[31:0], byte_stream}; if (byte_cnt 4) begin pixel_out[0] shift_buf[9:0]; pixel_out[1] shift_buf[19:10]; pixel_out[2] shift_buf[29:20]; pixel_out[3] shift_buf[39:30]; pixel_valid 1b1; end end end4.3 帧同步与DDR缓存衔接Zynq平台下我习惯把解包后的像素数据打上hsync/vsync/den信号转成标准视频流送入VDMA或者自研的帧缓存控制器。这样做的最大好处是后续所有图像处理模块、显示输出模块都可以复用同一套视频时序接口不需要关心源头是MIPI还是别的接口。帧同步要注意跨时钟域处理。像素时钟和MIPI接收端的恢复时钟不是同一个域需要用异步FIFO做缓冲并且把FS/FE信号也同步到输出侧用来复位帧计数器和行计数器。我在项目里还额外加了一个帧失步检测逻辑如果超过100ms没有检测到新的FS包就触发报警并重新初始化接收链路。这个功能在连续运行验证中帮我抓到了好几次Sensor偶发丢帧的现场。5. 案例核心DSI屏幕点亮与显示方向的处理5.1 DSI命令模式与视频模式的选择DSI屏幕有两种基本工作模式命令模式Command Mode和视频模式Video Mode。命令模式需要屏幕带GRAM主机通过写命令刷新局部区域适合低功耗静态画面视频模式则类似传统RGB接口时序主机端持续地向屏幕推像素流刷新率由主机控制。我用的工业屏同时支持两种模式但因为业务需要实时显示检测画面我选了视频模式。视频模式下的时序参数HFP/HBP/HSYNC/VFP/VBP/VSYNC和传统VESA时序本质是一回事只是把这些参数封装进DSI包的blanking包BLLP里发送。FPGA端只需要保证自己生成的视频时序参数和屏幕规格书一致屏幕就能正常显示。5.2 让FPGA的VTC时序匹配屏幕要求的porch这里特别想讲一个工程细节同一颗屏幕竖屏和横屏的时序参数往往完全不一样。有的屏幕物理分辨率是1080×1920你以1920×1080去驱动它显示方向不对是小事更多时候是把屏幕直接驱动到异常状态出现闪屏、半屏、花屏。正确做法是先查屏幕规格书里的display resolution方向和推荐的porch值再把FPGA里的VTCVideo Timing Controller参数改成和它一致。我的经验是写一组寄存器开放给软件侧动态修改这些值这样换屏或者改分辨率时不用重新综合整个工程。这在产线换屏验证时非常实用。5.3 竖屏改横屏背后的数据重映射逻辑热词里有一条mipi dsi drm竖屏改横屏显示这更多是Linux DRM子系统的话题。在FPGA/SoC协同的项目里竖屏改横屏可以有两个层次的实现方式一是Linux DRM层面通过rotation属性让驱动做扫描方向旋转这需要SoC的显示控制器和内存带宽支持适合有操作系统的平台二是在FPGA内部做旋转。如果在FPGA内部做90度或270度旋转本质上是一个图像转置操作必须先把整帧数据缓存进DDR然后按转置后的地址顺序读出来送给DSI。这个操作的存储带宽代价大概是正常扫描的两倍而且需要精确控制行缓存分块。对带宽紧张的项目另一个取巧的办法是方向感知显示屏幕物理上固定为横屏但内容渲染时主动用横屏坐标系省掉旋转步骤。这在实际项目里往往比死磕旋转更省资源。6. 调试实录从波形到图像一步步定位问题6.1 第一眼先看HS时钟是否干净拿到板子第一次上电我习惯不先接Sensor的完整数据而是给Sensor发配置后直接抓Clock Lane波形。看两个指标一是HS模式时的时钟差分摆幅是否足够、频率是否稳定二是从LP进入HS的跳变过程是否符合SoT时序。如果时钟波形都不对数据Lane基本不用往下查了。示波器上MIPI HS波形长什么样差分探头挂到Clock Lane的P/N上正常情况下能看到一个稳定的差分方波差分幅度在200mV峰峰值左右频率等于DDR时钟频率。比如1Gbps数据率时是500MHz。如果幅度偏低到100mV以下或者波形拖着明显的过冲/振铃先怀疑PCB走线阻抗和端接不要急着调FPGA里的相位。6.2 三大经典问题花屏、无图、闪屏说一下这三大问题我在实际项目里是怎么定位的。无图先看链路是否建立SoT。用ILA监测解串模块的sync_locked信号如果一直拉不起来检查Sensor是否出图、配置是否正确、时钟相位是否在眼图中心。现场我一般先把IDELAYE2的tap值全扫一遍找出最佳相位区间然后取中间值。这个扫描过程可以写成一个小状态机自动完成。花屏花屏大概率是字节对齐或Lane对齐出了问题。如果图像像被打乱一样先查Header里WC解出来是否合理。如果WC是乱的查bit slip如果WC是对的但像素内容乱再查Lane映射顺序和Byte-to-Pixel位序。闪屏闪屏通常不是物理层问题而是显示端时序参数或数据带宽不足。DSI视频模式下如果porch参数和屏幕要求的对不上屏幕会周期性闪烁或出现滚动条。另外如果帧缓存DDR带宽不够也会在某个固定位置出现撕裂现象。6.3 高速信号在FPGA内部怎么观测很多人会问MIPI数据率那么高FPGA内部的ILA采样率根本跟不上怎么办我的做法是在解串模块后面加一个整包抓拍逻辑把进入协议解析的字节流和包头信息缓存到一块BRAM里触发后把整包数据dump出来通过JTAG或者AXI接口读回上位机分析。这样虽然看不到每个bit的波形但能非常高效地定位协议层的字节错位、降序错误、CRC错误。真正要看清模拟波形还是得靠示波器。如果你手边没有差分探头还有一个土办法用两个普通探头分别挂到P和N然后用示波器的数学通道A-B近似差分信号。注意探头的GND要尽量短否则测量结果会有很大的振铃噪声容易误导判断。测试项目判断标准常见异常HS时钟频率稳定在DDR时钟频率如500MHz频率偏低或漂移检查Sensor配置HS差分摆幅约200mV峰峰值摆幅过低查阻抗和端接HS起始序列LP-11变LP-01后进入HS-0起始序列异常查LP状态机数据Lane抗偏斜各Lane间skew小于0.2UI相差过大查PCB等长同步序列0x1D解串后能稳定检测到检测不到查bit slip和相位7. 硬件层面对FPGA时序的隐性影响PCB设计协同7.1 阻抗、等长、过孔DPHY布线的几个硬性要求DPHY的差分对差分阻抗通常要求100Ω有些规范按85Ω评估走线层最好是完整参考平面避免跨分割。等长方面同一对差分线P/N之间的长度差尽量控制在5mil以内数据Lane之间、数据Lane与时钟Lane之间的长度差在有FPGA相位调节兜底的情况下可以放宽到15~20mil但越短越好。AC耦合电容的位置也有讲究。放在靠近连接器端时后面接FPGA的走线要保持完整参考放在靠近FPGA端时连接器到电容之间的走线也是完好差分。我在项目中习惯把100nF的0402电容放在距连接器5mm以内这样既能隔直流又能尽量减少高速段的断点。电容选型要留意ESR和自谐振频率尽量选高频特性好的MLCC。7.2 端接与共模偏置LVDS口接收MIPI信号的老话题如果你没有用外置PHY而是直接用FPGA的LVDS接收MIPI HS信号共模偏置是需要自己搭的。MIPI HS的共模电平约200mV而7系列HP Bank的LVDS_25输入共模要求是1.2V左右直接用LVDS标准接收会偏。常见的解决方法是AC耦合后在FPGA输入端用两颗电阻从1.2V分压出一个约200mV到400mV的偏置让共模落在LVDS接收器的线性区。这个偏置电阻网络必须并一个小电容滤波否则噪声会直接进到采样沿上导致误码率飙升。7.3 FPGA与PCB开发的几个扯皮点做FPGA的人容易忽略PCB做PCB的人容易把FPGA当黑盒。实际协作中我总结出几个必查项pin assignment时差分P/N不能接反HP Bank的VCCO电压要和接口电平匹配DPHY Lane所在的Bank内时钟资源是否够ISERDESE2需要用到MMCM、PLL和BUFIO引脚分配时要提前留好这些资源不要在差分线上加过大容性负载的ESD保护器件某些ESD器件的结电容会把1Gbps信号的上升沿磨圆导致眼图大幅劣化如果板上还有SensorSensor的MCLK时钟源要离FPGA不远走线尽量短且包地否则很容易把干扰耦合进MIPI链路。这些点如果等板子回来再发现改版周期至少两到三周。在我这个项目里因为前期checklist逼得紧这些问题基本都在投板前提前规避掉了后面调试过程少受了很多罪。跑通这套链路之后我最大的体会是MIPI DPHY在FPGA上能不能稳定工作物理层的余量远比协议逻辑重要。协议层出了问题ILA和仿真就能定位物理层出了问题很多时候只能靠耐心和示波器一点点熬出来。如果你正准备接一个MIPI项目建议先把第2节和第7节里的参数列成一张checklist在选型阶段和Layout阶段各过一遍能省下后面一半的调试时间。换一个思路说MIPI这套东西本质上没有太多高深的算法难就难在它把协议、半导体工艺、板级信号完整性全部绑在了一根差分线上任何一个环节抠不细最后都会反馈到屏幕上那一团乱糟糟的像素里。