MIPI DPHY这个接口,做图像和显示方向的人迟早都得碰。我最早接触它是在一个多摄像头采集项目上,当时用Xilinx的Artix-7接一颗MIPI摄像头,本以为照着UG文档配一下SelectIO就完事,结果从硬件差分走线到IDELAY校准再到协议解析,前前后后折腾了快三周才把第一帧图像稳定地抓出来。这篇文章就把这套流程完整拆开讲一遍——从硬件设计里那些容易翻车的地方,到FPGA内部怎么用原语把高速差分信号收进来,再到软件侧怎么把包解析成像素。适合已经会写Verilog、用过Vivado,但还没碰过MIPI DPHY的工程师参考。
1. 先搞清楚MIPI DPHY到底在FPGA里扮演什么角色
1.1 DPHY只是物理层,别把它和CSI/DSI混为一谈
很多人一上来就说"我要用FPGA实现MIPI",其实这句话本身是模糊的。MIPI是一个很大的规范族,DPHY(D-PHY)只是其中最底层的物理层协议,它规定了差分信号的电气特性、时钟通道和数据通道的时序关系、以及高速(HS)和低功耗(LP)两种传输模式。而CSI-2和DSI是建立在DPHY之上的上层协议,分别管摄像头和显示屏的数据包格式。
所以当你说"FPGA实现MIPI DPHY接口",准确的含义是:FPGA要能正确接收(或发送)DPHY规定的差分电平信号,恢复出时钟,并把串行的比特流对齐成字节,交给上层做CSI-2或DSI的包解析。这三层是分开的,硬件设计主要管物理层,FPGA逻辑管比特对齐和字节恢复,软件管包解析。
理解这个分层非常关键,因为它决定了你排查问题时该往哪一层看。图像出不来,可能是差分线阻抗不对(物理层),可能是IDELAY没校准好导致比特错位(链路层),也可能是CSI-2的包长字段解析错了(协议层)。分层清楚了,排查才有方向。
1.2 为什么Xilinx FPGA适合干这件事
Xilinx 7系列及以后的FPGA,SelectIO资源里自带ISERDESE2和IDELAYE2这些原语,天生就是为高速源同步接口准备的。ISERDESE2可以把高速串行数据做1:4、1:8的串并转换,IDELAYE2可以对每个比特做精细的延迟调整,这两样东西加起来,就构成了一个软件可调的DPHY接收前端。
相比专用桥接芯片(比如一些MIPI转并口的芯片),用FPGA直接收的好处是灵活——通道数可以自己定,数据可以直接进逻辑做处理,不用再经过一层外部芯片引入额外延迟和成本。代价就是你要自己处理校准和时序,工作量不小。我个人的经验是,如果只是单纯想把MIPI转成并口给后端用,桥接芯片更省事;但如果要在FPGA里直接做图像处理、多路合并、或者需要精确的时间戳,那FPGA直收是唯一合理的选择。
1.3 一个典型的应用场景拆解
拿我做过的一个四路摄像头采集板举例。四颗MIPI摄像头,每颗一路时钟通道加两路数据通道(DPHY的lane),总共12对差分线进FPGA。FPGA内部对每路数据通道做ISERDES解串,恢复出字节流,然后四路CSI-2解析模块并行工作,把YUV数据写进DDR。整个链路里,DPHY接收前端是最容易出问题的一环,因为它直接面对的是几百Mbps到1Gbps以上的差分信号,任何一点阻抗不连续或者延迟没校准好,都会导致误码。
这个场景里,硬件设计决定了信号质量的上限,FPGA逻辑决定了你能不能把这个上限发挥出来。两者缺一不可,这也是为什么这篇文章要把硬件和软件放在一起讲。
2. 硬件设计阶段:差分走线和电平匹配的坑
2.1 DPHY的电气特性决定了布线规则
DPHY的HS模式是低压差分信号,共模电压大约200mV,差分摆幅100mV到300mV左右,速率从80Mbps到每lane 2.5Gbps(不同版本规范上限不同)。LP模式则是单端信号,电平在0到1.2V之间。这两种模式共用同一对差分线,靠线上的电平状态来切换。
这个特性直接决定了布线要求:差分阻抗必须控制在100欧姆(±10%),差分对内两根线的长度差要控制在5mil以内,否则共模抑制会变差,HS模式下误码率会飙升。我见过一块板子因为对内长度差了15mil,结果在800Mbps以上就开始间歇性丢帧,降到400Mbps才勉强稳定。后来重新绕线把长度差压到3mil,问题立刻消失。
另外,DPHY的走线要尽量短,尤其是时钟通道。时钟通道的抖动会直接传递到数据采样上,走线越长,引入的抖动和损耗越大。一般建议时钟和数据通道的走线长度差控制在合理范围内,具体数值要看你的速率,速率越高要求越严。
2.2 端接和共模电压的处理
DPHY接收端通常需要外部提供共模电压偏置。有些FPGA的HP bank支持内部差分端接(DIFF_TERM),但DPHY的共模电压比较低,直接用内部端接可能不匹配。我的做法是在靠近FPGA引脚的地方加一对电阻做端接,同时用电阻分压网络把共模电压拉到合适的位置。
这里有个容易忽略的点:LP模式下的电平是0到1.2V,而FPGA的HP bank VCCO通常是1.8V或1.5V。如果你的bank电压设成1.8V,LP信号的高电平1.2V可能识别不稳。所以接收LP信号的引脚,bank电压最好设成1.2V或者用支持1.2V标准的bank。这一点在选引脚的时候就要规划好,不能等PCB画完了才发现bank电压不对。
2.3 电源和参考时钟的干净程度
DPHY对电源噪声很敏感,尤其是HS模式下的共模电压只有200mV,电源上稍微有点纹波就可能淹没有效信号。我的经验是给MIPI接收相关的bank单独做电源滤波,用磁珠加电容的方式隔离,电容要选低ESR的,靠近引脚放置。
参考时钟方面,如果你的FPGA逻辑里要用到IDELAYCTRL,它需要一个参考时钟(通常200MHz或300MHz),这个时钟的抖动要尽可能低。我一般用专用的时钟输入引脚接一个有源晶振,而不是从普通IO分频出来,因为分频出来的时钟抖动大,会直接影响IDELAY的精度。
2.4 硬件设计检查清单
在投板之前,我习惯按下面这张表逐项过一遍,能省掉很多返工:
| 检查项 | 要求 | 常见错误 |
|---|---|---|
| 差分阻抗 | 100欧姆±10% | 叠层没算对,实际90或110 |
| 对内长度差 | 小于5mil | 绕线时没对齐 |
| 通道间长度差 | 尽量小,按速率定 | 时钟和数据差太多 |
| 共模电压 | 按DPHY规范偏置 | 忘记加偏置网络 |
| Bank电压 | 匹配LP电平 | 用了1.8V导致LP识别不稳 |
| 电源滤波 | 单独滤波隔离 | 和数字电源混在一起 |
| 参考时钟 | 低抖动有源晶振 | 用普通IO分频 |
这张表看着简单,但每一条我都踩过或者见别人踩过。尤其是bank电压那条,很多人画完板子才发现LP信号识别不了,只能飞线改电压,非常麻烦。
3. FPGA逻辑侧:用ISERDES和IDELAY搭接收前端
3.1 ISERDESE2的工作模式和位宽选择
ISERDESE2是Xilinx 7系列里做高速解串的核心原语。它支持SDR和DDR两种模式,位宽可以配成2、3、4、5、6、7、8。对于DPHY接收,我们通常用DDR模式,因为DPHY在HS模式下是双沿采样(时钟的上升沿和下降沿都传数据)。
位宽的选择要看你的速率和FPGA能跑的逻辑时钟频率。假设你的lane速率是800Mbps,用DDR模式,那么采样时钟是400MHz。如果你选8位宽,逻辑侧的输出时钟就是400/4=100MHz(因为DDR下8位需要4个时钟周期来串出),这个频率对FPGA逻辑来说很轻松。如果选4位宽,逻辑时钟就是200MHz,稍微高一点但也能接受。
我的建议是优先选8位宽,把逻辑时钟压下来,给后面的处理留更多时序余量。代价是占用更多的ISERDES资源,但对于大多数项目来说这不是瓶颈。
3.2 IDELAYE2的校准逻辑
IDELAYE2用来对输入信号做精细延迟调整,步进精度和参考时钟有关。用200MHz参考时钟时,每个tap大约是78ps。DPHY的一个比特周期在800Mbps下是1250ps,所以理论上你需要找到那个采样点最稳的tap值。
校准的思路是这样的:先让IDELAY从0开始逐步增加,同时用一个已知的测试图案(比如固定的0xAA或0x55)去比对接收到的数据。当接收数据开始正确时,记下这个tap值;继续增加直到数据开始出错,再记下这个tap值。这两个值的中间就是最佳采样点。
实际操作中,我会写一个状态机自动跑这个过程,把每个lane的最佳tap值存到寄存器里。这样每次上电都能自动校准,不用手动调。这个自动校准逻辑是整个接收前端里最值得花时间打磨的部分,因为它是稳定性的基础。
3.3 时钟通道和数据通道的对齐
DPHY的时钟通道和数据通道是分开的,时钟通道提供采样时钟,数据通道传数据。但时钟和数据之间会有相位差,这个相位差在PCB上由走线长度决定,在FPGA内部还会受到时钟树延迟的影响。
处理方法是:时钟通道也经过一个IDELAY(或者用MMCM做相位调整),然后去采样数据通道。对齐的目标是让采样时钟的边沿落在数据眼图的中心。这个过程和IDELAY校准是耦合的,通常先粗调时钟相位,再细调每个数据lane的IDELAY。
我一般会先用MMCM把时钟相位调到大致居中,然后用IDELAY做精细调整。如果时钟和数据走线长度控制得好,这个调整范围不会很大;如果走线差得多,可能需要MMCM做较大范围的相位移动。
3.4 一段可参考的接收逻辑框架
下面这段代码是接收前端的骨架,展示了ISERDES和IDELAY的例化方式。实际项目中还需要加上校准状态机和错误统计,但框架是这样的:
// 单个lane的接收前端 IDELAYE2 #( .IDELAY_TYPE("VAR_LOAD"), .IDELAY_VALUE(0), .REFCLK_FREQUENCY(200.0), .DELAY_SRC("IDATAIN") ) u_idelay ( .IDATAIN(data_in_p), // 来自IBUFDS的差分输入 .DATAOUT(data_delayed), .CNTVALUEIN(tap_value), // 校准得到的tap值 .LD(load_tap), .C(ref_clk), .CE(1'b0), .INC(1'b0), .RST(reset) ); ISERDESE2 #( .DATA_RATE("DDR"), .DATA_WIDTH(8), .INTERFACE_TYPE("NETWORKING"), .IOBDELAY("IFD") ) u_iserdese ( .D(data_delayed), .DDLY(data_delayed), .CLK(div_clk), // 逻辑侧时钟 .CLKB(~div_clk), .CLKDIV(div_clk), .RST(reset), .Q1(q1), .Q2(q2), .Q3(q3), .Q4(q4), .Q5(q5), .Q6(q6), .Q7(q7), .Q8(q8) );这段代码里,tap_value就是校准状态机算出来的最佳延迟值。div_clk是MMCM分频出来的逻辑时钟。实际使用时要根据你的具体速率和器件型号调整参数。
4. 从比特流到字节:DPHY的协议解析细节
4.1 HS和LP模式的切换检测
DPHY在传输数据前会先进入HS模式,传输结束后回到LP模式。FPGA需要能检测这个切换,否则会把LP状态的信号误当成数据。检测的方法是看差分线上的共模电平:LP模式下共模电平高,HS模式下共模电平低。
实际实现时,可以用一个比较器或者FPGA内部的LVDS输入缓冲来检测。当检测到HS模式后,启动ISERDES开始采样;检测到回到LP模式后,停止采样并复位解串逻辑。这个切换检测的响应速度要快,否则会丢掉HS模式开始时的同步序列。
4.2 同步序列和字节对齐
DPHY在HS模式开始时,会先发送一段同步序列(Sync Sequence),通常是0x0000B8或者类似的图案。FPGA收到这段序列后,用它来做字节对齐——因为ISERDES输出的是8位数据,但哪一位是字节的起始位是不确定的,需要用同步序列来定位。
对齐的方法是:把ISERDES输出的8位数据做循环移位,直到匹配上同步序列。匹配上之后,记录下移位量,后续所有数据都按这个移位量对齐。这个过程每个lane都要做一次,因为每个lane的延迟可能不同。
4.3 CSI-2包结构的解析
字节对齐之后,数据流就是CSI-2的包了。CSI-2的包结构是:包头(4字节,包含数据类型和长度)、数据载荷、包尾(2字节的CRC)。解析的时候先读包头,根据数据类型判断这是图像数据还是控制信息,然后按长度读取载荷,最后校验CRC。
这里有个细节:CSI-2的短包和长包格式不同。短包只有包头和包尾,没有载荷,用来传帧同步、行同步这些信息。长包才有载荷,用来传实际的图像数据。解析逻辑要能区分这两种包,否则会把短包当成空的长包处理。
4.4 一个常见的解析错误
我踩过的一个坑是:CSI-2的长度字段是16位,但字节序是低字节在前。我一开始按高字节在前解析,结果长度算出来完全不对,图像数据全乱了。后来对着规范仔细看才发现字节序的问题。这种细节在规范里写得很清楚,但如果不仔细看很容易忽略。
另一个坑是CRC校验。CSI-2的CRC用的是特定的多项式,不是标准的CRC32。如果你用通用的CRC模块去校验,永远校验不过。这个多项式在CSI-2规范里有明确定义,实现的时候要照着写。
5. 调试与验证:怎么知道问题出在哪一层
5.1 用ILA抓信号的基本方法
Vivado的ILA是调试这类接口的利器。我的做法是在几个关键节点插ILA:ISERDES的原始输出、字节对齐后的数据、CSI-2解析后的包头。这样一旦出问题,可以逐级往上看,定位到是哪一层出的错。
抓ILA的时候要注意采样深度和触发条件。DPHY的数据速率高,ILA的采样时钟如果跟不上,抓到的数据会失真。我一般用逻辑侧的时钟(100MHz左右)去采,这个频率下ILA能稳定工作。触发条件设成"收到包头"或者"CRC错误",这样能精准抓到出问题的时刻。
5.2 误码率的统计和判断
判断链路是否稳定的一个量化指标是误码率。我的做法是在FPGA里加一个误码统计模块,用已知的测试图案(比如PRBS)去比对接收数据,统计错误比特数。如果误码率在1e-12以下,基本可以认为链路是稳的;如果高于1e-9,说明还有问题需要调。
误码率高的常见原因有三个:IDELAY没校准好、差分信号质量差、参考时钟抖动大。排查的时候先看IDELAY的校准结果是否落在合理范围,再看硬件信号质量,最后查时钟。
5.3 分层排查的完整链路
下面这张表是我总结的分层排查思路,从现象反推问题所在层:
| 现象 | 可能的问题层 | 排查方向 |
|---|---|---|
| 完全收不到数据 | 物理层 | 差分线是否接对、bank电压、电源 |
| 数据间歇性错误 | 链路层 | IDELAY校准、时钟相位 |
| 包头正确但载荷错 | 协议层 | 长度字段、字节序 |
| CRC校验失败 | 协议层 | CRC多项式、对齐 |
| 图像有条纹 | 链路层或协议层 | 逐lane检查、包解析 |
这张表在实际调试中帮我省了很多时间。每次遇到问题,先对照现象定位到层,再往那个方向查,比盲目试要高效得多。
6. 几个让我印象深刻的实操教训
6.1 别忽视LP模式的信号完整性
很多人把注意力全放在HS模式上,觉得LP模式速率低不用管。但实际上LP模式的信号如果质量差,会导致模式切换检测出错,进而影响HS模式的启动。我遇到过一块板子,LP信号的上升沿太缓,导致模式切换检测延迟,HS模式的前几个字节被丢掉了。后来在LP信号线上加了个小电容加速边沿,问题解决。
6.2 校准逻辑要考虑温度漂移
IDELAY的最佳tap值会随温度变化。如果你的设备工作环境温度变化大,上电时校准一次可能不够。我的做法是在逻辑里加一个周期性的重新校准,比如每隔几秒重新跑一次校准状态机,把新的tap值更新进去。这样即使温度漂移,链路也能保持稳定。
6.3 多lane之间的偏斜要控制
多lane的DPHY,lane与lane之间会有偏斜(skew)。如果偏斜太大,即使每个lane单独都对齐了,合并成完整数据时还是会错位。处理方法是在每个lane的接收前端加一个小的FIFO,用统一的时钟读出,把偏斜吸收掉。FIFO的深度根据最大偏斜量来定,一般几个字节就够了。
6.4 参考时钟的选择影响很大
前面提过参考时钟要低抖动,这里再强调一次。我试过用普通IO分频出来的时钟做IDELAYCTRL的参考,结果校准出来的tap值抖动很大,链路时好时坏。换成专用时钟引脚接有源晶振后,tap值非常稳定。这个改动成本很低,但效果立竿见影。
7. 从能收到能稳定收:性能优化的几个方向
7.1 提高采样余量的方法
采样余量决定了链路对抖动的容忍度。提高余量的方法有几个:一是优化IDELAY校准算法,找到眼图真正的中心而不是边缘;二是用MMCM做更精细的相位调整;三是改善硬件信号质量,减小抖动。
我一般会先确保硬件信号质量达标,然后在逻辑侧做精细校准。校准算法上,我会多采几组数据做平均,避免单次校准的偶然误差。
7.2 资源占用的权衡
ISERDES和IDELAY都是有限资源。如果你的lane数很多,资源可能不够用。这时候可以考虑用更少的ISERDES位宽(比如4位而不是8位),代价是逻辑时钟频率要提高。或者用时间复用的方式,多个lane共享一套解串逻辑,但这样会增加控制复杂度。
我的经验是,对于四路以下的lane,直接用8位宽最省心;超过四路就要算一下资源,必要时做权衡。
7.3 功耗和散热的考虑
高速接口的功耗不低,尤其是多lane同时工作的时候。FPGA的功耗上去之后,温度升高,又会影响IDELAY的稳定性,形成恶性循环。所以散热设计也要考虑进去,该加散热片就加,该留风道就留。
我在一个密闭机箱的项目里就吃过这个亏,夏天的时候链路开始不稳定,查了半天发现是温度太高导致校准值漂移。后来加了风扇强制散热,问题解决。
8. 写在最后的一点个人体会
这套MIPI DPHY接收方案,我从最早的磕磕绊绊到后来能比较顺畅地跑通,核心的转折点是想明白了"分层"这件事。硬件、链路、协议三层各管各的,出了问题先定位到层,再往那个方向查,效率比盲目试高太多。
另外就是校准逻辑值得多花时间。很多人觉得校准就是个简单的状态机,随便写写就行,但实际上校准的质量直接决定了链路的稳定性。我在校准上花的调试时间,大概占了整个项目的三分之一,但事后看是值得的,因为后面再也没为链路稳定性操过心。
如果你也在做类似的项目,建议先把硬件设计检查清单过一遍,把信号质量的基础打牢,然后再在逻辑侧做精细校准。基础不牢的话,逻辑侧再怎么调也是事倍功半。