ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA实战:用Verilog手写MIPI CSI-2 D-PHY接收器,搞定PPI时序

FPGA实战:用Verilog手写MIPI CSI-2 D-PHY接收器,搞定PPI时序 很多做FPGA的兄弟应该都有这种经历摄像头模组上明明印着 MIPI CSI-2 接口板子上也焊了 FPC 座子结果一查厂商 IP 价格劝退或者好不容易找到开源方案代码一跑全是时序报错。这篇文章就基于 Verilog 手写一个 MIPI CSI-2 D-PHY 接收器把 PPI 接口时序掰开揉碎讲清楚并且给出从差分信号恢复到字节对齐、再到 CSI-2 包头解析的完整思路。适合有一定 Verilog 基础、想自己做图像采集链路或者面试前临时补 MIPI 协议的工程师读完至少能对“FPGA 怎么把摄像头数据收进来”这件事形成一个可靠的落地方案。我默认你手里有一块带 MIPI 接口的 FPGA 开发板摄像头选用常见的 OV5640 这类模组工具链以 Vivado 为例。文中代码是教学性质的框架不是能直接商用的完整 IP但主干逻辑和实际项目是一致的。1. 先拆清设计MIPI CSI-2 接收器到底要干几件事很多新手一上来就找源码结果被一堆 IOBUFDS、IDDR、异步 FIFO 绕晕。与其这样不如先把协议栈拆开搞明白每层负责什么再动手写代码。1.1 协议栈三层各管什么D-PHY、CSI-2、PPI 一次说明白MIPI CSI-2 从物理层到协议层分三块最底下是 D-PHY负责把字节流变成高速差分信号同时处理高功耗和低功耗两种模式的切换中间是 CSI-2 协议层负责把像素数据打包成带包头、包尾的数据包连接这两层的是一个叫 PPIPHY-Protocol Interface的接口它定义了协议层怎么把数据交给物理层、物理层怎么把数据交给协议层。展开说D-PHY 的物理信号由 clock lane 和 data lane 组成。clock lane 提供双沿采样时钟data lane 负责传输串行数据。每一个逻辑通道都是一对差分线 P/N高速模式下摆幅只有 200mV 左右所以叫 HSHigh Speed。低功耗模式 LP 则用单端电平传输状态信息幅度接近 0 到 1.2V。CSI-2 协议层则负责把摄像头 sensor 输出的像素数据按照 Data Type、Word Count、ECC、CRC 等字段打包然后逐字节交给 D-PHY。PPI 在中间扮演适配器的角色。FPGA 里写代码时通常把 D-PHY 接收逻辑和 CSI-2 协议解析逻辑分开中间用 PPI 信号连接。如果你去看 Xilinx 的 MIPI IP 或 Lattice 的 MIPI IP会发现它们内部都有类似的接口只是封装形式不同。自己写接收器时理解 PPI 尤其重要因为很多调试问题最后都出在协议层和物理层之间的握手信号上。1.2 为什么不直接买厂商IP偏要自己用Verilog写厂商提供的 MIPI IP 分为两类一类是硬核少数器件内部集成了物理层但数量有限另一类是软核用 FPGA 逻辑实现价格不低而且授权往往绑定器件型号。对于小批量产品、教学验证或者学习目的自己用 Verilog 写一个接收器是非常划算的。自己写的另一个好处是可控性强。比如只需要支持 RAW8 一种 Data Type协议层可以大幅裁剪或者只需要单 lane 输入物理层就只需要做一个数据通道。商用 IP 往往把功能做大做全资源也烧得厉害。自己做接收器还能精确控制 PPI 的行为方便和自定义的图像处理逻辑集成。不过也要泼一盆冷水自己写 D-PHY 接收器重点在于数字逻辑部分也就是把已经转换成单端或者 LVDS 电平的 HS 信号采下来。MIPI 的物理层模拟前端比如终端电阻、共模偏置、LP 信号分压这些往往由开发板硬件或者外部芯片完成。FPGA 代码救不了不合理的硬件设计这一点动手前要有预期。1.3 模块划分与数据流从差分信号到像素字节一个可用的接收器模块至少包含四块逻辑时钟通道接收、数据通道接收、字节对齐、CSI-2 协议解析。再加上跨时钟域 FIFO 和顶层控制大致数据流是这样MIPI_D0P/N -- IBUFDS -- IDDR -- shift_reg -- SoT检测/字节对齐 -- PPI_FIFO -- CSI2_parser -- 像素数据 MIPI_CLKP/N -- IBUFDS -- BUFG -- hs_clk时钟通道经过 IBUFDS 得到单端时钟这是所有高速采样和字节时钟生成的基础。数据通道同样经过 IBUFDS然后用 IDDR 在时钟双沿采样得到串行比特流再通过移位寄存器拼成 8 位字节。字节对齐模块负责识别 SoTStart of Transmission序列确定字节边界之后出来的就是对齐后的字节流送入 PPI 接口。协议解析模块从 PPI 拿到字节流后做 0xB8 同步确认、解析包头、按 Word Count 收集有效像素数据。多 lane 场景下还需要一个 lane alignment 模块把几个 data lane 的字节对齐到同一时刻再合并成宽总线。初学阶段先做单 lane把链路跑通再扩展到多 lane 会顺手很多。1.4 动手前先算数lane数、数据率、字节时钟怎么定不要拿到代码就开干先算清楚 MIPI HS 时钟和字节时钟的关系这个直接决定代码时序约束怎么写。MIPI D-PHY 是 DDR 传输一个 HS 时钟周期内有上升沿和下降沿两次采样每秒每 lane 传输的比特数除以 2 就是 HS 时钟频率再除以 8 就是字节时钟频率。举个典型例子1080p30 RGB888分辨率 1920x1080每像素 3 字节一帧数据量约 6.22MB30fps 就是 186.6MB/s。如果采用 4 lane每 lane 约 46.65MB/s也就是 373.2MbpsHS 时钟就是 186.6MHz字节时钟为 46.6MHz。如果是 2 lane每 lane 跑到 93.3MB/s 即 746.4MbpsHS 时钟约 373MHz这个频率在通用 FPGA 逻辑里已经比较紧张了通常需要专用 IO 逻辑和严格的约束。把这些数字算在纸上你会发现很多事情就清楚了为什么开发板上的 MIPI 接口大多用 4 lane为什么入门做 720p 比 1080p 容易调试。字节时钟只有几十兆赫状态机和协议解析逻辑的时序压力就小很多主要难点集中在高速 IO 采样部分。2. 核心细节逐项拆解D-PHY信号、字节对齐与PPI时序这一部分是整个接收器的灵魂。如果只是照着网上的代码例化两个原语大概率会遇到数据错位或者 PPI 信号死活不对的问题。下面把协议里的关键序列、采样方案和接口时序逐个说明白。2.1 HS/LP模式切换SoT、EoT序列逐个学MIPI D-PHY 的数据通道在进入高速传输前会有一段低功耗状态切换这个切换序列叫 SoTStart of Transmission。标准做法是从 LP-11 开始依次经历 LP-01、LP-00然后进入 HS-0再发一个同步序列。LP 状态由两根线 Dp、Dn 的电压共同决定。LP-11 是待机态LP-01 是进入高速的准备态LP-00 是桥接态表示即将进入 HS。HS-0 之后发送器开始在数据通道上送出差分信号并用 8 位同步序列 0xB8 标记高速数据流的起点。接收端通过检测这个 0xB8 就能找到字节边界。EoTEnd of Transmission则反过来发送器先让数据通道保持 HS-0 一段时间然后转为 LP-00再经历 LP-10最后回到 LP-11。需要注意在 HS 模式下数据通道的差分电平接近 0 和 200mV如果直接拿单端引脚去读大概率读成 LP-00。所以在设计退出检测时不能只看数据通道的 LP 状态更可靠的做法是监测 clock lane 是否停振或者对 HS-0 持续的时间做计数。我刚做这套逻辑时也踩过这个坑状态机在 HS_RX 阶段收到 LP-00 就退出结果高速数据还没传完就误判成 EoT。后来改成检测连续多个字节为 0再配合 clock lane 的停振信号做退出问题才解决。2.2 FPGA前端接收IBUFDS和IDDR怎么用FPGA 接收差分信号最常用的方法是 IBUFDS 原语它把一对差分输入转成单端。对于 MIPI HS 信号硬件上通常已经做了电平适配或 AC 耦合IO 标准要根据板卡的供电电压来设置常见的是 LVDS_25、LVDS_18 或者 DIFF_SSTL 系列。具体用哪种要看原理图和 Bank 电压不能盲目套用。IDDR 的作用是在一个时钟周期内同时采上升沿和下降沿的数据这是做 MIPI DDR 采样的核心原语。以 Xilinx 为例IDDR 常见的配置是DDR_CLK_EDGE(SAME_EDGE)这样 Q1 和 Q2 会在同一个时钟边沿输出方便后续拼接成并行数据。这里有一个容易搞反的细节在SAME_EDGE模式下Q2 输出的是前一个下降沿采到的比特Q1 输出的是当前上升沿采到的比特。所以如果按时间顺序拼接应该先拼 Q2 再拼 Q1也就是{Q2, Q1}构成连续的两个 bit。不过不同 FPGA 平台的时序细节可能不同最靠谱的办法是仿真时观察数据流如果能正确检测到 0xB8说明顺序对了。代码层面IBUFDS 和 IDDR 的例化大致是wire hs_clk; IBUFDS #( .DIFF_TERM(TRUE), .IBUF_LOW_PWR(FALSE) ) ibufds_clk_inst ( .O(hs_clk), .I(mipi_clk_p), .IB(mipi_clk_n) ); wire hs_data; IBUFDS #( .DIFF_TERM(TRUE), .IBUF_LOW_PWR(FALSE) ) ibufds_data_inst ( .O(hs_data), .I(mipi_data_p), .IB(mipi_data_n) ); reg q1, q2; IDDR #( .DDR_CLK_EDGE(SAME_EDGE) ) iddr_data_inst ( .Q1(q1), .Q2(q2), .C(hs_clk), .CE(1b1), .D(hs_data), .R(1b0), .S(1b0) );以上代码示例中省略了 IO 标准和差分终端的设置实际工程里要根据硬件确定。有些 FPGA 还支持直接调用 BUFIO 把 MIPI HS 时钟引入 IO 时钟网络高速场景下必须用这种专用时钟路径否则内部 BUFG 的延迟会导致采样时序崩溃。2.3 字节对齐为什么0xB8能救你MIPI 数据通道上传输的是串行比特流接收端要想恢复出 8 位字节必须先知道哪一位是字节的起始位。这个问题跟串口通信很像串口靠起始位对齐MIPI CSI-2 靠 SoT 里的 0xB8 同步序列对齐。0xB8 的二进制是 1011_1000MSB 到 LSB但由于 MIPI 是 LSB first 传输物理线路上先看到的比特序列是 0、0、0、1、1、1、0、1也就是十六进制的 0x1D 反过来看。这就是为什么有时候你在调试时看到的 0x1D 其实就是 0xB8 的位序反转不必惊慌。字节对齐的思路是移位寄存器每来一个新的采样 bit就把它放入一个 8 位或 16 位的缓冲区然后检测里面是否出现了 0xB8。一旦匹配说明当前移位寄存器的位置就是字节边界之后每次取 8 个 bit 就都是一个完整的字节。为了提升可靠性很多实现会连续检测两个周期都匹配 0xB8 才确认同步避免误触发。对齐模块的代码框架大致是reg [7:0] shift_reg; reg [7:0] rx_byte; reg byte_aligned; always (posedge hs_clk) begin // 这里采用 {q2, q1} 作为连续两个bit shift_reg {shift_reg[5:0], q2, q1}; end always (posedge hs_clk) begin if (!byte_aligned) begin if (shift_reg 8hB8) begin byte_aligned 1b1; rx_byte shift_reg; end end end这个代码只是框架实际工程中需要加入状态控制、超时复位和多周期确认。要注意的是如果你发现能采样到数据但始终对齐不上先检查 bit 拼序再检查采样时钟极性最后检查是否需要对数据通道做 IDELAY 微调采样相位。2.4 PPI接口时序详解收到数据后怎么交给协议层PPI 接口是 D-PHY 物理层和 CSI-2 协议层之间的标准握手接口。接收方向的信号主要有 rxbyteclkhs、rxdatahs、rxvalidhs、rxactivehs。简单理解就是物理层在恢复出来的字节时钟下把有效数据通过 rxdatahs 总线送给协议层并用 rxvalidhs 表示数据有效用 rxactivehs 表示整个 HS 接收过程是否在进行中。更具体一点rxbyteclkhs 是物理层生成或分频出来的字节时钟频率等于 HS 比特率除以 8。rxdatahs 是并行 8 位数据线在 rxbyteclkhs 的上升沿同步。rxvalidhs 是数据有效信号只在每个有效字节对应的时钟周期拉高。rxactivehs 从检测到 SoT 后拉高到检测到 EoT 后拉低相当于整个高速接收过程的门控信号。我采用的教学约定是同步序列 0xB8 不输出到协议层rxvalidhs 在 0xB8 检测完成之后的下一个字节拉高rxdatahs 上出现的第一个有效字节就是 CSI-2 的 packet header 第一字节。下面用一段文本波形示意rxbyteclkhs _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_ rxactivehs ______|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|____ rxvalidhs ________|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|____ rxdatahs XXXXXXXX| DT | WC_L | WC_H | ECC | payload...协议层看到 rxvalidhs 拉高后就按 CSI-2 包格式去读 rxdatahs。第一字节是 Data Type比如 RAW8 对应 0x2ARAW10 对应 0x2BRGB888 对应 0x24。第二第三字节是 Word Count也就是 payload 的字节数。第四字节是 ECC用于校验包头。之后就是有效像素数据。最后还可以加 CRC 校验但很多场景为了节省逻辑可以忽略。PPI 这个接口设计得非常直白没有复杂的读使能、没有 ready/valid 反压就是时钟加数据加有效位。这也意味着协议层必须在一个字节时钟周期内完成对 rxdatahs 的采样如果处理不过来就得在 PPI 之后接 FIFO 缓冲。2.5 跨时钟域rxbyteclkhs和系统时钟不是一家人很多新手会忽略一个问题MIPI 接收链路恢复出来的 rxbyteclkhs 是从摄像头传来的 HS 时钟分频得到的它和你 FPGA 里的系统时钟比如 50MHz 晶振是异步关系。也就是说两边不仅频率可能不同相位也没有固定关系。直接把 rxdatahs 送给系统时钟域的逻辑是不安全的亚稳态迟早会找上门。解决办法是在 PPI 之后加异步 FIFO写时钟用 rxbyteclkhs读时钟用系统时钟。FIFO 深度根据一帧或一行最大的数据突发长度来定。如果只是做行缓存深度 1024 通常够用如果要缓存整帧深度得按最大分辨率的一帧数据量算或者用 DDR 内存配合 DMA。异步 FIFO 的读写指针一般用格雷码跨时钟域转换。FPGA 厂商都有现成的 FIFO IP直接用就行不用自己手写。但要注意FIFO 的写使能必须由 rxvalidhs 控制否则 HS 空闲时 rxdatahs 上无意义的垃圾数据也会被写进 FIFO。3. 保姆级实操Verilog代码与仿真全流程光讲协议比较虚下面进入实操环节。我会按照从环境准备到板级调试的顺序把关键步骤和核心代码串起来你可以对照自己的板子一步一步来。3.1 环境准备开发板、摄像头模块、工具链我使用的是 Xilinx Artix-7 系列开发板摄像头是 OV5640 模组输出 MIPI 2 lane最高支持 1080p。工具链用的是 Vivado 2020.2。这套组合非常常见网上资料也多遇到问题容易搜到答案。如果用的是 Intel 或者 Lattice 的 FPGA思路一样只是原语名称和约束写法不同。建议新手先选 Xilinx因为 IDDR、IBUFDS 这类原语资料多仿真模型也全。OV5640 支持通过 I2C 配置输出格式可以先配置成 720p RAW8 单 lane 来降低调试难度。仿真工具方面Vivado 自带 XSim也可以用 ModelSim/Questa。工程不大时 XSim 足够。如果你只有命令行环境Icarus Verilog 也能跑基本仿真但对原语和部分属性的支持不够完整需要自己写模拟模型。3.2 约束文件引脚和电平标准不能拍脑袋拿到开发板原理图先确认 MIPI 差分对接到了 FPGA 的哪个 Bank、哪个引脚以及这个 Bank 的电压是多少。XDC 约束里要正确设置引脚位置、IO 标准、差分终端。比如set_property PACKAGE_PIN AE5 [get_ports {mipi_clk_p}] set_property PACKAGE_PIN AD5 [get_ports {mipi_clk_n}] set_property IOSTANDARD LVDS_18 [get_ports {mipi_clk_p}] set_property IOSTANDARD LVDS_18 [get_ports {mipi_clk_n}]注意差分对的 N 脚一般不需要手动指定位置只要约束 P 脚并保证 P/N 在同一个差分对里即可。IO 标准一定要根据 Bank 电压来如果你的 Bank 是 1.8V就用 LVDS_18是 2.5V就用 LVDS_25。设置错误会导致采样异常严重时还可能损伤引脚。除了引脚约束时钟通道最好加时钟约束。如果 HS 时钟频率在 200MHz 左右可以创建一个虚拟时钟用于时序分析。很多入门工程不做这个约束也能工作但系统跑到 1080p 时容易随机出错。3.3 第1步恢复HS时钟用IDDR采下串行比特上电后摄像头发送 MIPI 信号之前会先进入 LP-11 状态。我们要先例化 IBUFDS 把差分时钟和差分数据转成单端再用 BUFG 或者 BUFIO 处理时钟。高速场景建议用 BUFIO 或者全局时钟缓冲Artix-7 上也可以先用 BUFG频率不高时问题不大。然后是 IDDR。把数据通道的 q1、q2 用一个移位寄存器按时间顺序拼成 8 位数据。这里最容易犯的错误是 bit 顺序搞反。为了便于调试可以在仿真模型里故意给出一段包含 0xB8 的数据观察最终拼出来的字节是不是 0xB8。如果 HS 时钟频率比较高采样数据的建立保持时间可能不满足这时就需要 IDELAY 对输入数据做相位延迟。IDELAY 的步进和时钟频率有关调试时通常要遍历 delay 值找到采样眼图最中间的位置。这一部分比较繁琐但成熟项目都会做。3.4 第2步SoT检测与字节对齐状态机有了稳定的字节流还需要确定字节边界。状态机设计成IDLE → LP11 → LP01 → LP00 → HS_WAIT → SYNC_DET → HS_RX → EOT。IDLE 状态下等 lp 状态变成 LP-11然后按顺序通过 LP-01、LP-00进入 HS_WAIT。在 LP-00 之后摄像头发送 HS 时钟和数据此时 hs_clk 开始翻转我们等待一小段时间让时钟稳定然后开始移位采样。在 SYNC_DET 状态检测移位寄存器中是否出现 0xB8。匹配成功后进入 HS_RX此时输出 PPI 信号。HS_RX 状态需要处理两种结束情况一种是正常 EoT数据通道回到 LP-11另一种是异常断开比如拔线、传感器复位。最简单的实现是通过连续多个字节为 0 来判定 HS-0 超时然后回到 IDLE。复杂一点可以接 clock lane 的 LP 状态作为参考。状态机代码框架localparam IDLE 3d0; localparam LP11 3d1; localparam LP01 3d2; localparam LP00 3d3; localparam HS_WAIT 3d4; localparam SYNC_DET 3d5; localparam HS_RX 3d6; localparam EOT 3d7; reg [2:0] state; always (posedge hs_clk or posedge rst) begin if (rst) begin state IDLE; end else begin case (state) IDLE: if (lp_state LP_11) state LP11; LP11: if (lp_state LP_01) state LP01; LP01: if (lp_state LP_00) state LP00; LP00: if (hs_clk_stable) state HS_WAIT; HS_WAIT: if (sync_detected) state HS_RX; HS_RX: if (eot_detected) state IDLE; default: state IDLE; endcase end end注意 lp_state 信号来自 P/N 单端检测hs_clk_stable 和 sync_detected 需要自己实现。状态机进入 HS_RX 后rxactivehs 拉高rxvalidhs 在下一个字节拉高PPI 开始输出有效数据。3.5 第3步PPI输出与CSI-2包头解析PPI 输出逻辑不复杂但信号时序要严格按照前面的约定来。在 HS_RX 状态下rxactivehs 拉高当 sync 检测完成数据对齐确认后rxvalidhs 在输出 packet header 第一字节的同时拉高。之后 CSI-2 协议解析模块开始工作。第一字节判断 Data Type如果不支持就直接丢弃该包第二第三字节拼成 Word Count控制后续读多少个字节第四字节是 ECC可以做校验也可以先忽略。有效数据在 rxvalidhs 为高时采样写入异步 FIFO供图像处理模块读取。如果后续还要做帧同步需要根据 Frame Start、Frame End 这类 Data Type 来划分帧边界。RAW8 图像数据本身没有帧号只能依赖帧同步包。协议解析模块收到 Frame Start 后拉高帧有效信号收到 Frame End 后拉低。3.6 第4步用Testbench和ILA把链路调通写完代码先仿真再上板。Testbench 里需要做两件事模拟 D-PHY 的 LP 切换时序以及模拟 HS 模式的 DDR 串行数据。模拟 HS 时钟可以写一个 always 块生成 100MHz 或 200MHz 时钟。模拟数据通道则需要把一组 8 位测试数据按 LSB first 串行输出到 data_pdata_n 输出反相。为了模拟字节流可以先生成一个包含 0xB8 开头的长度可配的数据包再用移位方式逐比特输出。这类似于打一个串口波形只是时钟是 DDR。上板调试时使用 Vivado 的 ILA 核把 PPI 关键信号拖进去比如 rxactivehs、rxvalidhs、rxdatahs、state。设置成捕获 HS 接收窗口的波形这样摄像头一帧一帧传数据很快就能看到是否进入 HS_RX、数据是否连续、包头是否正确。4. 常见问题与排查技巧实录做接收器调不通大部分问题不是单个模块出错而是模块之间配合出了问题。下面总结几个我实际遇到过的典型场景按症状分类方便你对照排查。4.1 rxvalidhs不拉高多半是SoT没进对现象状态机一直停在 IDLE、LP11 或者 LP01rxactivehs 和 rxvalidhs 始终为 0。最直接的排查办法是抓 LP 状态看是否按 LP-11、LP-01、LP-00 的顺序变化。如果 LP 状态根本不动先检查硬件连接和引脚约束。如果状态能走到 LP00 但进不了 HS_WAIT多半是 hs_clk_stable 条件不满足看看时钟通道的 IBUFDS 输出有没有翻转。另一种可能是 HS 时钟已经来了但 LP 检测电路把 HS 信号误读为 LP-00导致状态机提前退场。这属于硬件电平适配问题逻辑层很难完全修复。检查开发板上 MIPI 接口是否有电平转换电路以及数据通道差分对是否 AC 耦合。4.2 图像花屏/数据错位字节对齐和bit顺序的坑花屏在 MIPI 调试里最常见原因往往不是数据丢了而是字节边界不对。如果你发现能进 HS_RX但图像颜色错乱、噪点密集先看 ILA 里采到的 packet header 是不是正确。如果第一字节不是 0x2A 之类的 Data Type大概率是字节错位。字节错位可以用滑动窗口处理检测到 0xB8 之后再连续确认下一拍也正确然后锁定接收窗口。有的工程只检测一次 0xB8结果在噪声大的环境中被误触发后续数据全偏。这也是为什么很多设计中 SoT 检测会连续看两到三个周期。bit 顺序反了则是另一类问题现象是永远检测不到 0xB8或者检测到了但后续字节全部乱序。解决办法是把 IDDR 拼接顺序从{q2, q1}改成{q1, q2}或者反过来同时在仿真里确认。4.3 HS_RX无法退出EoT检测方案选错了如果 HS_RX 状态一直拉高FIFO 不断被垃圾数据填满多半是 EoT 检测没做到位。前面分析过HS 模式下直接读 LP 状态并不可靠因为 HS 低电平会被判为 LP-00。正确的思路是检测 clock lane 是否停止翻转或者检测 data lane 连续输出多个 0x00 字节。我自己的项目里是用clock lane 停振检测作为主要退出条件。写一个计数器在 HS 时钟停止超过一定周期后状态机回到 IDLE。这个方法对正常 EoT 和异常断开都有效。4.4 丢包/丢行CRC校验和FIFO深度CSI-2 包是可选的很多 sensor 默认开启。如果你发现图像偶尔少一行很可能是协议层没有正确等待 EoT 就复位了内部状态。另一个常见原因是 FIFO 深度不够突发数据把 FIFO 写满后续数据被丢弃表现为图像缺行或者底部数据错位。FIFO 深度计算可以参考一行的最大数据量。例如 1280x720 RAW8一行 1280 字节加上包头包尾不超过 1300 字节用 2048 深度的 FIFO 就很有余量。如果做多 lane 合并FIFO 深度还要考虑 lane 间偏差一般多留几拍余量。4.5 高速跑不稳IDELAY与你只差一步在 720p 单 lane 场景可能直接采样就能工作。但在 1080p 4 lane 或者数据率超过 700Mbps 的 lane 上数据与时钟之间的 skew 可能会让 IDDR 采到建立保持时间边界导致误码率升高。这时候需要把数据通道的采样相位微调也就是用 IDELAY。IDELAY 的 calibration 在 Xilinx 7 系列里比较成熟调试时可以用一个测试模式遍历 delay 值找到一个稳定的窗口。如果项目要从原型走向量产这一步基本是必须的否则环境温度一变就可能出问题。4.6 问题速查表症状、原因、解决症状可能原因排查手段状态机停在 IDLEMIPI 信号没进来或电平不匹配检查引脚约束、原理图、摄像头上电LP 状态异常硬件电阻网络不对万用表测 Dp/Dn 对地电平进不了 SYNC_DEThs_clk 未稳定或采样 bit 序反了ILA 抓 hs_clk 和 shift_reg能进 HS_RX 但没有 rxvalidhssync 检测后没有正确拉高有效信号检查状态机输出逻辑花屏字节错位、bit 序错误连续确认 0xB8调整拼接顺序丢行FIFO 深度不足计算单行最大长度并加大 FIFO偶发误码采样相位偏用 IDELAY 遍历延迟窗口小技巧再补充一个如果条件允许先把摄像头配置成纯色测试图或者彩条这样图像数据的特征非常明显花屏时一眼就能看出是字节错位还是 bit 反转。另外用 ILA 抓信号时触发条件可以设成rxactivehs 1这样每次摄像头发一帧就能抓到一帧的完整时序省不少时间。我个人做这个接收器时最耗时的阶段就是 IDDR 拼接方向和字节对齐确认。后来把 0xB8 检测当成“北极星”所有数据顺序都以它为准来验证才彻底绕开这个问题。如果你也在调 MIPI建议先从单 lane RAW8 开始把 PPI 波形完全看明白再往多 lane、更高帧率扩展这条路稳得多。
返回列表