
MIPI DPHY 在 ZYNQ 上做接收很多人第一反应是找现成的 IP 核。但现实情况是Xilinx 官方并没有提供一个开箱即用的 MIPI DPHY 接收 IP尤其在 ZYNQ-7000 系列上你得自己动手用 SelectIO 资源去搭。我前后做过三个基于 ZYNQ 的 MIPI 摄像头采集项目从最初的天真以为配个 IP 就行到后来老老实实啃 UG471 和 DPHY 规范中间踩的坑足够写一本小册子。这篇文章不讲空泛的理论只聊手动实现 MIPI DPHY 接收时最容易翻车的五个关键细节每个都附带我的实际排查过程和最终方案。如果你正在用 ZYNQ 做 MIPI 摄像头、显示屏或者其他高速差分信号的接收并且打算走手动实现这条路那这些经验应该能帮你省下不少调试时间。前提是你对 FPGA 的 SelectIO 原语、IDELAY、ISERDES 有基本了解至少知道它们大概能干什么。如果完全没接触过建议先翻一翻 UG471 的 SelectIO 章节再回来。1. 时钟通道的 DDR 接收与字节对齐远比想象中麻烦MIPI DPHY 的时钟通道是 DDR 的也就是说数据在时钟的上下沿都会翻转。很多新手在这里的第一个误区是以为用一个普通的 IBUFDS 把差分时钟转成单端然后直接当系统时钟用就行了。实际上DPHY 的时钟通道频率等于数据通道的比特率除以 2比如你跑 800Mbps 的 lane rate时钟通道就是 400MHz。这个频率直接扔给 FPGA 的逻辑资源时序压力非常大而且你还需要对它做进一步的 MMCM 倍频来采样数据。1.1 为什么不能直接把 DPHY 时钟当系统时钟DPHY 时钟通道在 LP 模式下会进入低功耗状态时钟会停止翻转。如果你把它直接接到 MMCM 或者 BUFG 上一旦时钟停止整个时钟网络就会挂掉后续的复位和重新锁定会变得极其麻烦。我最初的做法就是直接把 IBUFDS 的输出接到 BUFG结果摄像头一进入 LP 模式FPGA 这边就彻底失联了必须重新配置才能恢复。正确的做法是DPHY 时钟通道经过 IBUFDS 后不直接进 BUFG而是先送到一个 BUFIO 或者区域时钟缓冲再通过 ISERDES 做 DDR 接收把时钟的边沿信息转换成数据和使能信号。这样即使时钟停止你也能通过状态机检测到 LP 状态而不是让整个时钟树崩溃。1.2 用 ISERDES 做时钟通道接收的具体配置在 Vivado 中实例化 ISERDESE2 时有几个参数必须注意。首先是DATA_RATE要设为DDRDATA_WIDTH设为 4 或者 8取决于你后续怎么处理。INTERFACE_TYPE设为NETWORKING还是MEMORY其实影响不大但IOBDELAY建议设为IFD这样你可以同时使用 IDELAY 和 ISERDES 的内部延迟。ISERDESE2 #( .DATA_RATE(DDR), .DATA_WIDTH(4), .INTERFACE_TYPE(NETWORKING), .IOBDELAY(IFD), .DYN_CLKDIV_INV_EN(FALSE), .DYN_CLK_INV_EN(FALSE), .NUM_CE(1), .OFB_USED(FALSE), .SERDES_MODE(MASTER) ) iserdes_clk ( .Q1(q1), .Q2(q2), .Q3(q3), .Q4(q4), .SHIFTIN1(), .SHIFTIN2(), .SHIFTOUT1(), .SHIFTOUT2(), .BITSLIP(bitslip), .CE1(1b1), .CE2(1b1), .CLK(clk_hi), .CLKB(~clk_hi), .CLKDIV(clk_div), .D(d_clk_in), .DDLY(d_clk_delayed), .RST(rst), .OCLK(), .OCLKB() );这里clk_hi是 DPHY 时钟经过 BUFIO 后的高频时钟clk_div是经过 BUFR 后的分频时钟。注意 BUFIO 和 BUFR 必须成对使用且它们只能驱动同一时钟区域内的资源。如果你的 DPHY 时钟引脚和 ISERDES 不在同一个 bank那就得换引脚或者用 BUFG 加 MMCM 的方案但那样会引入额外的抖动。1.3 字节对齐的 Bitslip 操作逻辑ISERDES 输出的并行数据是乱序的因为 DPHY 的时钟和数据之间没有固定的相位关系。你需要通过 BITSLIP 来调整对齐。具体做法是发送一个已知的同步序列比如 DPHY 规范里的 Sync 码 0xB8然后不断执行 BITSLIP直到接收到的数据等于 0xB8。我通常的做法是写一个状态机每次 BITSLIP 后检查一次数据最多尝试 8 次。如果 8 次都没对齐说明时钟或者数据有问题需要检查 IDELAY 的延迟值。这里有个细节BITSLIP 操作必须在 CLKDIV 时钟域下进行而且每次 BITSLIP 后要等至少两个 CLKDIV 周期才能检查数据否则可能采到不稳定的值。注意BITSLIP 不是随便滑的它每次滑动一个比特位。如果你发现滑了 8 次还是不对不要继续滑而是回头检查 IDELAY 的 tap 值是否合适。我遇到过因为 IDELAY 延迟太大导致数据眼图完全关闭的情况这时候再怎么滑都是白费。2. IDELAY 的 tap 值不是拍脑袋定的得算IDELAY 是手动实现 MIPI DPHY 接收时最核心也最玄学的部分。它的作用是对输入的数据信号进行延迟调整使得采样点落在数据眼图的中心。Xilinx 7 系列的 IDELAYE2 有 31 个 tap每个 tap 的延迟大约是 78ps具体值取决于器件速度和温度。很多人调 IDELAY 就是随便写个值然后看能不能收到数据这种做法在实验室可能碰巧能跑但一到现场就各种不稳定。2.1 先搞清楚你的数据眼图有多宽假设你的 MIPI lane rate 是 800Mbps那么一个比特周期是 1.25ns。理论上数据眼图的宽度就是 1.25ns 减去上升下降时间。实际上由于 PCB 走线、连接器、ESD 器件的影响眼图会缩水很多。我的经验是在 800Mbps 下如果 PCB 设计得当眼图宽度大概在 0.8ns 到 1.0ns 之间。IDELAY 的总延迟范围是 31 个 tap按 78ps 算就是 2.4ns 左右足够覆盖一个比特周期。但问题是你需要把采样点放在眼图中心。如果 IDELAY 的初始值设为 0采样点就在数据跳变沿附近误码率会很高。所以你需要扫描 IDELAY 的 tap 值找到误码率最低的那个点。2.2 实际扫描 IDELAY 的方法我通常会在 FPGA 里做一个简单的误码率测试逻辑发送端持续发送已知的伪随机序列接收端统计错误比特数。然后通过 AXI 或者 UART 接口逐个 tap 值进行测试每个 tap 值跑个几秒钟记录误码数。// 简化的 IDELAY 控制逻辑 IDELAYE2 #( .IDELAY_TYPE(VAR), .IDELAY_VALUE(0), .DELAY_SRC(IDATAIN), .SIGNAL_PATTERN(DATA), .REFCLK_FREQUENCY(200.0), .HIGH_PERFORMANCE_MODE(TRUE) ) idelay_data ( .CNTVALUEOUT(cntvalueout), .DATAOUT(data_delayed), .C(data_clk), .CE(ce), .INC(inc), .IDATAIN(data_in), .LD(ld), .LDPIPEEN(1b0), .REGRST(1b0) );扫描的时候从 tap 0 开始每次加 1记录误码数。你会看到一个类似浴盆曲线的结果tap 值太小或太大时误码率高中间有一段误码率为零的区域。取这个区域的中心点作为最终的 tap 值。2.3 温度漂移和电压变化的影响IDELAY 的延迟值会随温度和电压变化。Xilinx 的 IDELAYE2 有一个 REFCLK 输入通常接 200MHz 的参考时钟用来补偿 PVT 变化。但即便如此在极端温度下延迟值还是会有几个 tap 的漂移。如果你的应用场景温度变化很大建议在系统运行过程中定期重新扫描 IDELAY或者至少留出足够的余量。我做过一个车载项目夏天车内温度能到 70 度冬天零下 20 度。实验室调好的 tap 值到了现场就偶尔出误码。后来改成每次上电都重新扫描一次 IDELAY问题才解决。虽然麻烦但可靠性上去了。提示IDELAYE2 的 HIGH_PERFORMANCE_MODE 设为 TRUE 时延迟值的线性度更好但功耗会略高。如果你的系统对功耗不敏感建议开启。3. ISERDES 的位宽和时钟分频比必须和 DPHY 的 lane 速率匹配ISERDES 的 DATA_WIDTH 和时钟分频比决定了你最终输出的并行数据宽度。这个参数如果设错了轻则数据错位重则完全收不到数据。很多人在这里犯的错误是只根据 FPGA 的时钟频率去算分频比而忽略了 DPHY 的 lane rate 和时钟通道频率之间的关系。3.1 从 lane rate 反推 ISERDES 配置假设你的 MIPI 摄像头输出 2 个 lane每个 lane 的速率是 800Mbps时钟通道是 400MHz。那么对于每个数据 laneISERDES 需要以 800Mbps 的速率采样。如果你用 ISERDES 的 DDR 模式那么 ISERDES 的 CLK 频率就是 400MHzCLKDIV 频率就是 400MHz 除以 DATA_WIDTH。如果你设 DATA_WIDTH 为 8那么 CLKDIV 就是 50MHz。这个频率对于 FPGA 的逻辑资源来说很友好但你需要确保 MMCM 能同时产生 400MHz 和 50MHz 的时钟并且它们之间的相位关系是确定的。3.2 时钟分频比的计算表格Lane Rate (Mbps)时钟通道频率 (MHz)ISERDES DATA_WIDTHCLKDIV 频率 (MHz)备注400200450低速场景逻辑资源压力小800400850最常用的配置1000500862.5需要 MMCM 支持非整数分频1200600875高速场景时序压力大1500750893.75需要仔细约束时序这个表格里的 CLKDIV 频率是我实际项目中用过的基本覆盖了常见的 MIPI 摄像头速率。注意 1000Mbps 和 1500Mbps 这两行CLKDIV 频率不是整数MMCM 配置起来会稍微麻烦一点但 Vivado 的 Clocking Wizard 可以处理。3.3 多 lane 之间的 skew 问题如果你的 MIPI 有多个 lane比如 2 lane 或者 4 lane那么每个 lane 的 ISERDES 都需要独立的 IDELAY 和 BITSLIP。但问题是不同 lane 之间的走线长度不可能完全一致导致它们之间的 skew 可能达到几百皮秒。这个 skew 如果不处理会导致多 lane 数据拼接时出错。我的做法是每个 lane 独立做 IDELAY 扫描和 BITSLIP 对齐然后在数字域里用一个简单的 FIFO 或者移位寄存器来补偿 lane 之间的 skew。具体补偿多少可以通过发送一个已知的多 lane 同步序列来测量。比如 DPHY 规范里的 Sync 码在每个 lane 上同时发送你可以在 FPGA 里比较不同 lane 收到 Sync 码的时间差然后调整 FIFO 的读延迟。4. LP 状态检测和高速模式切换状态机不能写得太简单MIPI DPHY 有 LP 和 HS 两种模式。LP 模式下信号是单端的电平是 0V 到 1.2VHS 模式下信号是差分的共模电压是 200mV差分摆幅是 200mV。FPGA 的 SelectIO 资源需要能够在这两种模式之间切换而且切换的时机必须准确否则会丢失数据或者误触发。4.1 LP 模式的电平检测在 LP 模式下DPHY 的每条 lane 有两根线通常称为 P 和 N。LP 状态有四种组合LP-00、LP-01、LP-10、LP-11。其中 LP-11 是空闲状态LP-00 是进入 HS 模式的前导码的一部分。FPGA 需要能够检测这些状态通常用普通的 IBUF 加上比较器来实现。但这里有个坑FPGA 的 IO 标准必须支持 1.2V 的 LVCMOS 或者类似的标准。如果你用的是 HP bankVCCO 必须设为 1.2V 或者更低。如果是 HR bankVCCO 可以设为 1.2V 到 3.3V 之间的值但需要确认输入阈值是否满足要求。我遇到过因为 VCCO 设成了 1.8V导致 LP 模式的 1.2V 电平被误判为低电平的情况。4.2 HS 模式的使能时序从 LP 模式切换到 HS 模式时DPHY 会先发送一个 LP-00 到 LP-01 到 LP-00 的序列然后进入 HS 模式。FPGA 需要在检测到这个序列后及时使能 ISERDES 和 IDELAY并且切换到 HS 模式的差分输入。这个切换的延迟必须控制在一个比特周期以内否则会丢失 HS 模式的前几个比特。我的做法是用一个高速状态机时钟用 DPHY 时钟通道的 LP 模式下的自由振荡时钟或者用一个独立的 200MHz 时钟。状态机检测到 LP-00 序列后立即拉高 HS_EN 信号这个信号控制 ISERDES 的 RST 和 IDELAY 的 LD。同时IO 的差分输入使能也要切换。4.3 状态机的常见错误最常见的错误是状态机在 LP 模式下没有正确复位导致进入 HS 模式时 ISERDES 还处于复位状态。另一个错误是 HS_EN 信号的时序不对太早或太晚。太早会导致 ISERDES 在数据还没稳定时就开始采样太晚则会丢失数据。我建议在状态机里加一个计数器对 LP-00 序列的长度进行计数。如果 LP-00 持续的时间超过某个阈值才认为是有效的 HS 进入请求。这样可以避免噪声或者毛刺误触发。注意LP 模式的信号速率虽然低但也不能忽略。有些摄像头的 LP 模式信号速率能达到 10Mbps 以上如果你的状态机时钟太慢可能会漏掉一些状态变化。5. PCB 走线和端接电阻硬件上的坑比逻辑上的更难填说了这么多逻辑上的细节但很多时候问题根本不在 FPGA 内部而在 PCB 上。MIPI DPHY 的 HS 模式差分信号对走线要求很高如果走线阻抗不匹配、端接电阻不对、或者走线长度差异太大逻辑上再怎么调都是白搭。5.1 差分走线的阻抗控制MIPI DPHY 的差分阻抗要求是 100 欧姆。如果你的 PCB 走线阻抗是 90 欧姆或者 110 欧姆信号反射会导致眼图恶化。我见过一个项目因为 PCB 厂家把差分阻抗做成了 85 欧姆导致 800Mbps 下误码率居高不下最后只能降速到 400Mbps 才能稳定工作。差分走线的阻抗取决于线宽、线间距、介质厚度和介电常数。在画 PCB 的时候一定要让 PCB 厂家提供阻抗计算报告并且在制板后用 TDR 实测确认。如果阻抗偏差超过 10%建议重新设计叠层。5.2 端接电阻的位置和阻值MIPI DPHY 的接收端通常需要端接电阻一般是 100 欧姆的差分端接。有些 FPGA 的 SelectIO 内部有可配置的端接电阻但精度和线性度可能不如外部电阻。如果走线比较长建议使用外部端接电阻并且尽量靠近 FPGA 的接收引脚。端接电阻的阻值不是随便选的。如果阻值偏大信号幅度会变小眼图高度降低如果阻值偏小信号幅度变大但功耗增加而且可能超出 FPGA 输入的共模范围。我通常会用 100 欧姆的差分端接然后在调试时用示波器观察信号幅度必要时微调。5.3 走线长度匹配和过孔处理多 lane 之间的走线长度差异要控制在 5mil 以内最好是 2mil 以内。这个要求听起来很苛刻但对于 800Mbps 以上的速率来说是必须的。如果走线长度差异太大lane 之间的 skew 会超过一个比特周期导致数据无法对齐。过孔也是一个大问题。每个过孔都会引入寄生电容和电感导致阻抗不连续。如果差分对必须换层建议在过孔旁边加接地过孔并且尽量让两根线对称换层。我见过一个设计因为差分对换层时不对称导致共模噪声很大最后不得不重新改板。5.4 电源和地的处理MIPI DPHY 的 HS 模式信号摆幅很小只有 200mV 差分。如果电源或者地平面噪声太大很容易淹没信号。建议在 FPGA 的 IO 电源引脚旁边放足够多的去耦电容并且确保地平面完整。如果可能的话给 MIPI 接收部分单独用一个 LDO 供电避免被其他数字电路干扰。我在一个项目里遇到过因为 DDR 内存的开关噪声耦合到 MIPI 电源上导致误码率随内存访问模式变化的情况。后来在 MIPI 电源上加了 LC 滤波问题才解决。所以如果你发现误码率和某些数字电路的活动相关不妨查一查电源噪声。6. 调试手段和工具链的配合别只盯着 Vivado 的 ILA手动实现 MIPI DPHY 接收调试手段非常关键。Vivado 的 ILA 很好用但它只能看数字信号看不到模拟层面的问题。很多时候你需要结合示波器、协议分析仪和逻辑分析仪一起排查。6.1 用示波器看眼图和信号质量在调试初期我强烈建议用高带宽示波器至少 1GHz 以上观察 MIPI 差分信号的眼图。通过眼图你可以直观地看到信号幅度、上升下降时间、抖动和噪声。如果眼图已经闭合了那逻辑上再怎么调都没用必须先解决硬件问题。示波器的探头也很重要。普通的无源探头带宽不够而且寄生电容大会严重影响信号。建议使用差分有源探头带宽至少是信号速率的 2 倍以上。比如 800Mbps 的信号探头带宽至少 1.6GHz。6.2 ILA 的触发和采样深度设置当硬件信号没问题后就可以用 ILA 来调试逻辑了。ILA 的采样深度和触发条件设置很关键。对于 MIPI DPHY 接收我通常会设置触发条件为检测到 LP-00 序列后捕获 HS 模式的前几百个比特。这样可以看到 HS 模式进入时的时序是否正确。ILA 的采样时钟要用 CLKDIV 时钟而不是高速时钟。因为高速时钟频率太高ILA 的采样深度会非常有限。用 CLKDIV 时钟可以捕获更多的数据而且对于分析字节对齐和状态机跳转来说足够了。6.3 协议分析仪的辅助作用如果你手头有 MIPI 协议分析仪那调试会轻松很多。协议分析仪可以直接解码 MIPI 的 CSI-2 或者 DSI 协议告诉你每一帧的数据内容。这样你可以对比 FPGA 接收到的数据和协议分析仪解码的数据快速定位是物理层的问题还是协议层的问题。不过协议分析仪通常比较贵不是每个团队都有。如果没有也可以用一个已知的 MIPI 信号源比如 FPGA 自己产生一个简单的 MIPI 信号然后自己接收形成一个闭环测试。这样可以排除摄像头或者显示屏的问题专注于 FPGA 接收逻辑的调试。6.4 常见问题排查表格现象可能原因排查方法完全收不到数据时钟通道没起来用示波器检查 DPHY 时钟是否有翻转数据偶尔出错IDELAY tap 值不合适扫描 IDELAY找误码率最低点多 lane 数据错位lane 之间 skew 太大检查 PCB 走线长度调整 FIFO 延迟LP 模式检测不到IO 标准或 VCCO 设置错误检查 bank 电压和 IO 标准HS 模式进入失败状态机时序不对用 ILA 抓 LP-00 到 HS 的切换过程误码率随温度变化IDELAY 温漂定期重新扫描 IDELAY 或留足余量这个表格是我在实际项目中总结的基本上覆盖了 80% 的常见问题。每次遇到新问题我都会先对照这个表格排查一遍往往能快速定位方向。7. 一些零散但重要的经验补充除了上面五个关键细节还有一些零散的经验值得分享。这些经验不一定适用于所有项目但至少在我做过的项目里都发挥过作用。7.1 复位策略要统一MIPI DPHY 接收逻辑涉及多个时钟域LP 模式检测时钟、HS 模式的高速时钟、CLKDIV 时钟、以及 AXI 或者系统时钟。复位信号必须同步到每个时钟域否则会出现亚稳态或者复位释放不同步的问题。我通常会用一个小模块把外部复位同步到各个时钟域并且确保复位释放的顺序是先释放高速时钟域再释放 CLKDIV 域最后释放系统域。7.2 时钟切换的毛刺问题从 LP 模式切换到 HS 模式时时钟源可能会切换。如果切换过程中产生毛刺会导致 ISERDES 误触发。建议使用 BUFGMUX 或者 BUFGCTRL 来做时钟切换并且确保切换时两个时钟都是稳定的。如果可能的话尽量让 LP 和 HS 模式使用同一个时钟源只是分频比不同这样可以避免切换带来的问题。7.3 仿真验证不能省虽然 MIPI DPHY 的仿真模型不太好找但至少可以对 ISERDES 和 IDELAY 的行为进行仿真。Xilinx 提供了这些原语的仿真模型你可以写一个简单的测试平台模拟 DPHY 的时钟和数据输入验证你的接收逻辑是否正确。仿真虽然不能完全替代实测但可以帮你排除很多逻辑上的低级错误。7.4 代码的可配置性MIPI 的 lane rate 和 lane 数量可能因项目而异。建议把 ISERDES 的 DATA_WIDTH、IDELAY 的 tap 值、时钟分频比等参数做成可配置的通过 AXI 寄存器或者参数化模块来设置。这样同一个代码框架可以复用到不同的项目省去很多重复劳动。7.5 和摄像头厂商的沟通如果你用的是某个特定型号的摄像头建议直接找厂商要一份 DPHY 的时序参数包括 LP 模式的电平范围、HS 模式的进入时序、时钟通道的抖动要求等。这些参数在数据手册里可能写得不详细但厂商的 FAE 通常能提供更具体的信息。我遇到过因为摄像头厂商的 LP 模式电平和标准略有差异导致 FPGA 检测不到的情况后来找 FAE 确认了实际电平范围调整了比较器阈值才解决。7.6 留足调试余量最后一点也是最重要的一点留足调试余量。无论是 IDELAY 的 tap 值、时钟的相位、还是 PCB 的走线长度都不要卡在临界值上。IDELAY 的 tap 值要选在浴盆曲线的中心而不是边缘时钟相位要留出至少 90 度的余量PCB 走线长度差异要控制在规格的一半以内。这样即使温度、电压或者批次有变化系统仍然能稳定工作。我在一个量产项目里因为 IDELAY 的 tap 值选得太靠近边缘导致小批量试产时有 5% 的板子出现误码。后来把 tap 值往中心调了 3 个 tap良率直接到了 100%。这个教训让我明白调试的时候不能只追求能跑还要追求跑得稳。手动实现 MIPI DPHY 接收确实比用现成的 IP 核麻烦很多但好处是你对每一个环节都有完全的控制权遇到问题也知道从哪里下手。而且一旦调通这套逻辑可以复用到不同的项目和不同的速率上长期来看是值得的。希望这些经验能帮你少走一些弯路。