1. 为什么MIPI D-PHY的硬核IP配置值得单独拿出来讲
Lattice CrosslinkNx这颗FPGA在嵌入式视觉圈子里火起来,很大程度上是因为它把MIPI D-PHY硬核IP直接做进了芯片里。这意味着你不需要用FPGA逻辑资源去软实现高速差分接收,省下来的LUT和寄存器可以全部拿去跑图像处理流水线。听起来很美,但真正上手配置的时候,很多人会在第一步就卡住——硬核IP的配置界面参数多、约束文件要求严格、参考时钟和Lane映射稍有偏差就直接不出图。
我这次的项目需求很明确:用CrosslinkNx接收OV9734传感器的MIPI CSI-2数据,经过简单的格式转换后通过另一组MIPI D-PHY输出到显示模组。OV9734是一颗720p的CMOS传感器,MIPI输出,单Lane或者双Lane配置都支持。选它是因为功耗低、驱动成熟,适合做原型验证。但实际调试下来,从硬核IP配置到传感器I2C初始化再到数据通路打通,前后踩了不下十个坑。
这篇文章面向的是已经有一定FPGA开发基础、准备或者正在使用CrosslinkNx做MIPI相关项目的工程师。我会把硬核IP配置的每个关键参数拆开讲清楚,然后结合OV9734的实际调试记录,把那些文档里不会写、但实际会遇到的坑一个个列出来。如果你正在用CrosslinkNx做类似的事情,这篇内容应该能帮你省下至少两天的调试时间。
2. CrosslinkNx的MIPI D-PHY硬核到底硬在哪里
2.1 硬核IP和软核IP的本质区别
在聊配置之前,有必要先把“硬核”这个概念说透。MIPI D-PHY的物理层本质上是一组高速差分收发器,工作速率可以到每Lane 1.5Gbps甚至更高。如果用FPGA的普通IO和逻辑资源去实现,时序收敛会非常痛苦,而且功耗和抖动都很难控制。CrosslinkNx的做法是在芯片里直接嵌入专用的D-PHY硬核电路,这部分电路不占用FPGA的逻辑资源,也不需要通过通用布线资源,性能指标是出厂就保证的。
你可以把它理解成:软核是你自己用积木搭一座桥,硬核是芯片里已经修好了一座桥,你只需要告诉它桥的两头接哪里、限速多少。这个区别直接决定了你的开发重心——不需要去调物理层的时序,但需要把配置接口和时钟约束做对。
2.2 CrosslinkNx上D-PHY硬核的资源分布
CrosslinkNx系列里,不同型号的D-PHY硬核数量和位置不一样。以我用的LIFCL-40为例,它有两组D-PHY硬核,分别位于芯片的不同Bank。每组硬核包含若干Lane,可以配置成接收或者发送方向。这里有一个很容易忽略的点:硬核的Lane分配不是随便选的,它和芯片的Bank电压域、参考时钟输入引脚是绑定的。
具体来说,每个D-PHY硬核需要一对差分参考时钟输入,这个时钟的质量直接决定了高速数据的采样精度。CrosslinkNx的参考时钟可以来自外部晶振直接输入,也可以从FPGA内部PLL生成后送到硬核。我建议在条件允许的情况下优先用外部晶振直接给硬核提供参考时钟,因为内部PLL的抖动指标虽然够用,但外部晶振的相噪表现通常更好,尤其是你的传感器输出速率接近硬核上限的时候。
2.3 硬核IP在Radiant里的配置入口
Lattice的Radiant IDE里,D-PHY硬核的配置是通过IP Catalog里的“MIPI D-PHY”模块来完成的。打开配置界面后,你会看到几个关键分组:General、Lane Configuration、Timing Parameters、Clock Settings。每个分组里的参数都有默认值,但默认值不一定适合你的传感器。
我一开始就是直接用了默认配置,结果传感器数据死活进不来。后来逐项对照OV9734的datasheet才发现,默认的Lane数量和速率范围跟我的实际需求不匹配。所以下面我会把每个关键参数单独拎出来讲。
3. 硬核IP配置里那些一填错就不出图的参数
3.1 Lane数量和方向:先搞清楚你的传感器输出什么
OV9734支持1 Lane和2 Lane两种MIPI输出模式,具体用哪种取决于你的寄存器配置。我一开始想省事,直接用2 Lane模式,觉得带宽大总不会错。但实际上,如果你的传感器初始化寄存器没有正确配置成2 Lane,而硬核IP这边配置成了2 Lane,结果就是硬核一直在等第二路Lane的数据,但传感器只在一路Lane上发,自然什么都收不到。
所以第一步是确认传感器的输出Lane数。OV9734的寄存器0x3C01或者类似的Lane控制寄存器需要和硬核配置一致。我的建议是:先用1 Lane模式把链路跑通,确认数据能进来之后,再切到2 Lane提升带宽。这样排查问题的时候变量少一个。
在Radiant的硬核配置界面里,Lane Configuration这一项要选“Receiver”,然后Lane数量选1或者2。注意,CrosslinkNx的硬核在接收模式下,每个Lane的极性也是可以配置的。如果你的PCB走线把P和N搞反了,不用改板子,直接在配置里把Polarity翻转就行。这个功能在实际调试中非常救命。
3.2 数据速率范围:不是越高越好
硬核IP的Timing Parameters里有一个“Data Rate”范围设置,单位是Mbps per Lane。这个参数决定了硬核内部的PLL锁定范围和均衡器配置。很多人会直接填一个很宽的范围,觉得这样兼容性好。但实际上,范围填得太宽会导致硬核的PLL在锁定的时候不稳定,尤其是在低速率下。
OV9734在720p分辨率下的MIPI输出速率大概在400Mbps到600Mbps per Lane之间,具体取决于帧率和像素格式。我实测下来,把Data Rate范围设置在300Mbps到800Mbps之间比较合适,既能覆盖传感器的实际输出,又不会让PLL工作在过于宽泛的区间。
这里有一个细节:硬核IP配置里的Data Rate是per Lane的速率,不是总带宽。如果你用2 Lane,总带宽是Data Rate乘以2。这个在计算像素时钟的时候要注意,别搞混了。
3.3 参考时钟频率:必须和实际输入一致
Clock Settings里需要填写参考时钟的频率。这个频率必须和你实际连接到硬核参考时钟引脚上的时钟频率完全一致。我遇到过有人填了24MHz,但板子上焊的是27MHz的晶振,结果硬核的PLL一直锁不定,数据自然出不来。
CrosslinkNx的D-PHY硬核参考时钟范围通常在10MHz到100MHz之间,具体看型号。我用的板子上是24MHz的有源晶振,直接接到硬核的REFCLK引脚。配置里填24MHz,然后硬核内部会自己倍频到Lane速率所需的时钟。
注意:参考时钟的抖动指标很关键。如果你用的晶振相位噪声比较大,硬核的误码率会明显上升。建议选相噪在-140dBc/Hz@100kHz以上的晶振。
3.4 连续时钟模式和非连续时钟模式
这是一个非常容易踩坑的地方。MIPI D-PHY的时钟Lane有两种工作模式:连续时钟模式(Continuous Clock)和非连续时钟模式(Non-Continuous Clock)。在连续模式下,时钟Lane一直在翻转,不管有没有数据。在非连续模式下,时钟Lane只在有数据传输的时候才翻转,其他时间处于低功耗状态。
OV9734默认输出的是非连续时钟模式。如果你的硬核IP配置成了连续时钟模式,而传感器是非连续模式,那么在没有数据的时候,硬核会认为时钟丢失,可能会触发错误状态。反过来,如果传感器是连续模式,硬核配置成非连续模式,硬核可能会在时钟应该存在的时候误判为空闲。
在Radiant的配置界面里,这个选项通常在Clock Settings或者Lane Configuration下面。我建议先查清楚传感器的时钟模式,然后保持一致。OV9734的寄存器0x4800或者类似的时钟控制寄存器可以配置这个模式。
3.5 硬核IP的复位和初始化顺序
硬核IP的复位顺序在文档里写得比较简略,但实际调试中很关键。正确的顺序是:先给硬核的参考时钟,等时钟稳定后释放硬核的复位,然后配置硬核的寄存器,最后再启动传感器的数据输出。
我一开始是把传感器和硬核同时复位、同时释放,结果硬核还没配置完,传感器的数据就过来了,硬核根本没准备好接收。后来改成硬核先初始化,延时100ms之后再启动传感器,问题就解决了。
在Radiant里,硬核IP会生成一个复位信号和一个配置完成信号。你需要在自己的逻辑里确保配置完成信号拉高之后,再去使能传感器的数据输出。这个握手逻辑虽然简单,但少了它就会出各种奇怪的问题。
4. OV9734传感器调试实录:从I2C不通到出图
4.1 硬件连接检查:先确认物理层没问题
在开始调试之前,我先把硬件连接过了一遍。OV9734的模组通过一个FPC排线连接到CrosslinkNx的评估板上。这里有几个检查点:
- 电源:OV9734需要1.8V的模拟电源和1.2V的数字电源,还有2.8V的IO电源。用万用表量一下每个电源引脚,确认电压正确。
- I2C上拉:OV9734的SCCB(也就是I2C)总线需要上拉电阻,通常是4.7kΩ到10kΩ。如果模组上没有自带,需要在评估板上加。
- MIPI差分对:用示波器或者万用表检查差分对的连通性,确认没有虚焊或者短路。
- 复位和时钟:OV9734需要一个外部时钟输入,通常是24MHz。确认这个时钟在传感器端是存在的。
我遇到的一个问题是FPC排线的方向搞反了,导致电源和地接反,传感器发热但不工作。后来换了一根排线,确认方向正确后才继续。
4.2 I2C通信打通:SCCB的时序陷阱
OV9734的寄存器配置是通过SCCB协议,本质上就是I2C。我用CrosslinkNx内部的I2C控制器去读写传感器的寄存器。这里遇到的第一个坑是SCCB的时序和标准I2C略有不同。
标准I2C在写寄存器的时候,通常是:Start + 设备地址 + 寄存器地址 + 数据 + Stop。但OV9734的SCCB在某些情况下需要额外的延时,尤其是在写一些关键寄存器(比如复位寄存器)之后,需要等待至少1ms才能进行下一次读写。
我一开始用标准的I2C写操作,没有加延时,结果传感器的寄存器写不进去,读回来的值一直是默认值。后来在每次写操作之后加了2ms的延时,问题解决。
另外,OV9734的设备地址是0x36(7位地址),写操作是0x6C,读操作是0x6D。这个地址在datasheet里有写,但不同模组可能会有变化,最好先用I2C扫描工具确认一下。
4.3 传感器初始化寄存器序列
OV9734的初始化寄存器序列比较长,通常有几十个寄存器需要配置。这些寄存器的值决定了输出分辨率、帧率、像素格式、MIPI Lane数、时钟模式等。我用的序列是从官方参考代码里拿到的,但直接烧进去之后发现没有数据输出。
排查下来发现,参考代码里的寄存器序列是针对特定模组和特定分辨率的,我的模组可能用的是不同的晶振频率或者不同的Lane配置。后来我逐项对照datasheet,把MIPI相关的寄存器重新配置了一遍,特别是:
- 0x3C01:Lane数量配置
- 0x4800:时钟模式配置
- 0x4801:MIPI速率配置
- 0x3501和0x3502:曝光时间
- 0x3508和0x3509:增益
把这些寄存器配置正确之后,传感器才开始正常输出MIPI数据。
4.4 用硬核IP的状态寄存器判断链路状态
CrosslinkNx的D-PHY硬核IP提供了一组状态寄存器,可以读取链路的状态,包括:PLL是否锁定、每个Lane是否同步、是否有误码等。这些状态寄存器在调试的时候非常有用。
我一开始不知道有这些寄存器,一直在用逻辑分析仪抓MIPI信号,效率很低。后来发现硬核IP的文档里有一个状态寄存器的列表,通过APB接口可以读出来。读出来之后发现PLL是锁定的,但是Lane 0没有同步。这就说明物理层有问题,可能是差分对的极性反了,或者Lane的映射不对。
在Radiant的配置里把Lane 0的极性翻转之后,状态寄存器显示Lane 0同步成功,数据开始进来了。
4.5 数据通路的验证:从硬核输出到图像格式转换
硬核IP输出的是MIPI CSI-2的字节流,需要经过CSI-2解码器解析出像素数据。CrosslinkNx的Radiant里有一个CSI-2解码器的IP,可以配置成不同的像素格式。OV9734输出的是RAW10格式,每个像素10bit,打包成字节流。
这里遇到的坑是CSI-2解码器的配置要和传感器的输出格式完全一致。我一开始配置成了RAW8,结果解码出来的图像全是花屏。改成RAW10之后,图像正常。
另外,CSI-2的虚拟通道号(Virtual Channel)也要匹配。OV9734默认使用Virtual Channel 0,如果解码器配置成了其他通道,数据也会被丢弃。
5. 那些文档里不会写的调试经验和避坑技巧
5.1 先让硬核自己回环测试
在接传感器之前,我强烈建议先让硬核IP做一次回环测试。CrosslinkNx的D-PHY硬核支持内部回环模式,可以把发送端的数据直接环回到接收端。这样你可以在不接任何外部设备的情况下,验证硬核的配置是否正确、时钟是否锁定、数据通路是否通畅。
具体做法是:在Radiant里把硬核配置成同时包含发送和接收,然后在逻辑里把发送端的数据接到接收端。如果回环测试能收到正确的数据,说明硬核配置没问题,问题就在传感器或者外部连接上。这个步骤能帮你快速定位问题范围。
5.2 参考时钟的测量:不要只看频率
参考时钟的频率用示波器很容易量,但抖动和相位噪声就不那么好量了。我遇到过频率正确但抖动很大的晶振,导致硬核的误码率很高,图像上会出现随机噪点。后来换了一个低相噪的晶振,图像就干净了。
如果你没有相位噪声分析仪,可以用一个简单的办法:把硬核的误码率状态寄存器读出来,如果误码率在10的负12次方以上,就说明参考时钟的质量可能不够好。
5.3 Lane映射和PCB走线的对应关系
CrosslinkNx的硬核Lane和芯片引脚之间的映射关系在Pinout文档里有写,但很容易看错。我建议在画PCB之前就把这个映射关系确认清楚,然后在硬核配置里按照实际的引脚连接来设置Lane的顺序。
如果PCB已经画好了,发现Lane的顺序不对,也不用慌。硬核IP支持Lane重映射,可以在配置里把Lane的顺序调整过来。这个功能在调试的时候非常有用,但要注意重映射之后的Lane极性也要对应调整。
5.4 传感器寄存器的批量写入技巧
OV9734的初始化寄存器有几十个,如果一个个写,效率很低。我通常会把寄存器配置写成一个数组,然后用一个循环批量写入。但这里有一个坑:有些寄存器之间需要延时,不能连续写。
我的做法是在数组里插入延时标记,比如用一个特殊的地址值表示“延时1ms”,然后在循环里判断这个标记,执行延时。这样既能批量写入,又能保证时序正确。
5.5 用逻辑分析仪抓MIPI信号的注意事项
MIPI D-PHY的信号速率很高,普通的逻辑分析仪可能抓不到。如果你要用逻辑分析仪调试,需要选支持高速差分信号的型号,而且探头要匹配差分阻抗。我用的是一款支持1.5Gbps的逻辑分析仪,配合差分探头,可以抓到MIPI的波形。
但即使抓到了波形,解析MIPI协议也需要专门的解码软件。我通常只用逻辑分析仪看物理层的信号质量,比如差分对的幅度、共模电压、上升下降时间等。协议层的解析还是靠硬核IP的状态寄存器和CSI-2解码器的输出来判断。
5.6 电源噪声对MIPI信号的影响
MIPI D-PHY的差分信号幅度比较小,通常只有200mV左右,所以对电源噪声很敏感。我在调试的时候发现,如果传感器的电源纹波比较大,图像上会出现规律的横条纹。后来在电源引脚旁边加了几个100nF和10uF的电容,纹波降下来之后,横条纹就消失了。
建议在PCB设计的时候,就给MIPI相关的电源引脚预留足够的去耦电容位置。如果已经画好了板子,可以在调试的时候飞线加电容试试。
6. 从出图到稳定运行还差哪些优化
6.1 时序约束的补充
硬核IP配置完成之后,Radiant会自动生成一些时序约束,但这些约束不一定完整。你需要根据实际的时钟频率和数据速率,补充一些额外的约束,特别是跨时钟域的信号。
我的设计里,硬核输出的像素时钟和FPGA内部的处理时钟是不同的频率,需要做跨时钟域处理。我用了异步FIFO来做时钟域转换,然后在约束文件里对FIFO的读写指针加了false path约束。如果不加这些约束,时序报告里会有很多违例,虽然实际跑起来可能没问题,但温度变化或者电压波动的时候就可能出错。
6.2 误码率的长期监测
硬核IP的状态寄存器里有一个误码率计数器,可以读出累计的误码数量。我在设计里加了一个逻辑,定期读取这个计数器,如果误码率超过阈值就触发一个中断。这样可以在系统运行过程中实时监测链路质量,提前发现潜在问题。
实测下来,在参考时钟质量良好、电源干净的情况下,误码率可以做到10的负14次方以下,基本上可以认为是没有误码。
6.3 温度对MIPI链路的影响
MIPI D-PHY的高速信号对温度比较敏感,尤其是在高温环境下,差分对的阻抗会发生变化,导致信号完整性下降。我在老化测试的时候发现,温度从25度升到70度的时候,误码率会上升一个数量级。
如果你的产品需要在宽温范围内工作,建议在高温下做一次误码率测试,确认链路余量足够。如果余量不够,可能需要降低Lane速率或者优化PCB走线。
6.4 多传感器切换的注意事项
如果你的设计需要支持多个传感器切换,比如同时接OV9734和另一颗传感器,需要注意硬核IP的配置是否支持动态切换。CrosslinkNx的硬核IP在运行时重新配置需要复位,所以如果要在两个传感器之间切换,需要先停止当前传感器的数据输出,然后复位硬核,重新配置,再启动另一个传感器。
这个过程比较耗时,如果切换频率很高,可能需要考虑用两组硬核分别接两个传感器,然后在FPGA内部做数据选择。
7. 个人在实际调试中的几点体会
这次CrosslinkNx加OV9734的调试,前后花了大概一周时间,其中大部分时间是在排查硬核IP配置和传感器寄存器的问题。回过头来看,如果一开始就按照正确的顺序来——先确认硬件连接、再配置硬核IP、然后回环测试、最后接传感器调试——至少能省下一半的时间。
另外,硬核IP的状态寄存器真的是调试利器,比逻辑分析仪和示波器都直接。建议在设计的早期就把这些状态寄存器接到一个调试接口上,比如通过UART输出,这样在调试的时候可以随时读取链路状态。
最后分享一个小技巧:OV9734的寄存器配置序列,不同批次的模组可能会有细微差异。如果发现某个寄存器写不进去,不要怀疑自己的I2C代码,先换一个模组试试。我遇到过一批模组的0x4800寄存器默认值不一样,导致时钟模式配置错误,换了模组就好了。