ZYNQ手动实现MIPI DPHY接收:5个核心细节与避坑指南
1. 为什么要在ZYNQ上手动实现MIPI DPHY接收1.1 一个真实的项目场景去年接手一个工业相机项目前端是一颗2000万像素的CMOS传感器输出MIPI CSI-2信号4条数据lane加1条时钟lane每lane速率跑在800Mbps左右。主控用的是Xilinx ZYNQ-7020PL端负责接收和缓存PS端跑Linux做图像处理和网络传输。项目立项的时候团队第一反应是找现成的IP核——Xilinx官方有MIPI CSI-2 Receiver Subsystem也有DPHY IP看起来应该能直接用。结果一查手册就傻眼了。官方MIPI DPHY IP核在ZYNQ-7000系列上并不是所有器件都免费开放而且那个IP核的配置灵活性有限很多参数是固定的比如lane的时序调整、deskew的粒度都不太能改。更关键的是这个IP核的license和版本兼容性在Vivado不同版本之间经常出问题团队之前就踩过升级Vivado之后IP核需要重新授权的坑。于是决定手动实现。所谓“手动实现”就是不用Xilinx的MIPI DPHY IP核而是用PL端原生的IDELAY、ISERDES、BUFR这些底层原语自己搭一套DPHY接收逻辑。这样做的好处是完全可控时序调整粒度细不依赖IP授权移植性强。坏处也很明显坑多而且很多坑是文档里不会写的。这篇文章就是把这套方案从选型到落地的完整过程拆开讲重点放在那5个最容易翻车的细节上。如果你也在做类似的事情或者正在评估要不要手动实现希望这些经验能帮你少走几天弯路。1.2 手动实现和用IP核的取舍先把这个决策说清楚因为不是所有项目都适合手动实现。用官方IP核的优势是省事。Xilinx的MIPI DPHY IP已经把物理层的很多细节封装好了包括lane的同步、deskew、字节对齐、ECC校验等。你只需要配置参数接上CSI-2的协议层就能跑。对于速率不高、lane数不多、时序余量大的场景这是最稳妥的选择。但官方IP核有几个硬伤。第一是灵活性DPHY的时序参数比如HS settle、HS term enable这些IP核给的调整范围有限遇到时序比较紧的传感器或者PCB走线不太理想的板子可能调不到最优。第二是资源占用IP核内部用了不少逻辑资源对于资源紧张的7020来说有时候会挤占其他模块的空间。第三是版本依赖不同Vivado版本的IP核接口和配置方式可能变化项目维护成本高。手动实现的核心思路是用IDELAY做输入延迟调整用ISERDES做串并转换用BUFR/BUFIO做时钟域处理自己写状态机做lane同步和字节对齐。这样做的好处是每个环节都可控IDELAY的tap可以逐个调ISERDES的位宽和模式可以按需配时钟方案也可以根据板级实际情况优化。代价是开发周期长调试难度大。尤其是当信号完整性不太好的时候需要反复调整IDELAY的tap值还要处理lane之间的skew。这些在IP核里是自动完成的手动实现就得自己写逻辑。我的建议是如果你的lane速率在1Gbps以下lane数不超过4条而且项目时间充裕可以尝试手动实现。如果速率更高或者lane数更多或者项目时间紧优先考虑官方IP核或者第三方IP。手动实现更适合那些对时序有极致要求、或者需要深度定制化的场景。2. 核心细节一IDELAY的tap调整与校准策略2.1 IDELAY到底在调什么MIPI DPHY的接收端信号从引脚进来之后首先要解决的是采样点的问题。高速差分信号经过PCB走线、连接器、ESD保护器件之后到达FPGA引脚时眼图已经不像理想情况那么开了。IDELAY的作用就是在输入路径上插入一个可控的延迟把采样时钟的边沿对准数据眼图的中心。ZYNQ-7000的IDELAY原语是IDELAYE2每个tap的延迟大约是78ps在200MHz参考时钟下。总共31个tap可调范围大约是2.4ns。这个范围看起来不大但对于800Mbps的信号来说一个UI是1.25ns2.4ns的调整范围已经覆盖了将近两个UI足够用了。关键问题是怎么知道当前应该设多少个tap2.2 手动校准的实操方法最直接的方法是用一个已知的测试图案。让传感器输出一个固定的pattern比如0xAA或0x55然后在FPGA端用ISERDES采集观察采集到的数据是否正确。逐步增加IDELAY的tap值记录从哪个tap开始数据正确到哪个tap数据又变错。正确的窗口就是眼图的宽度窗口中心就是最优的tap值。具体操作上我一般会写一个简单的扫描状态机。上电之后IDELAY从0开始每个tap值停留一段时间比如1ms统计这段时间内采集到的错误字节数。扫完31个tap之后找到错误率最低的那一段取中间值作为最终配置。这里有个细节扫描的时候要用连续的模式不能只采一个字节就判断。因为MIPI是差分信号偶尔的误码可能是随机噪声引起的需要统计一段时间才能判断。我一般会统计至少1000个字节错误率低于某个阈值比如1e-6才认为这个tap是有效的。还有一个坑IDELAY的tap值不是线性的。虽然手册上说每个tap约78ps但实际上由于工艺偏差不同器件、不同bank的IDELAY特性会有差异。所以不能直接按理论值算必须实测。2.3 温度漂移和电压漂移的应对IDELAY的延迟会随温度和电压变化。工业级应用里温度范围可能是-40到85度这个范围内的延迟变化可能达到10%到20%。如果只在上电时校准一次温度变化之后可能就偏了。应对方法有两种。一种是定期重新校准比如每隔几分钟或者检测到错误率上升时重新跑一次扫描。另一种是用IDELAY的VAR_LOAD模式配合一个温度传感器根据温度查表调整tap值。第一种方法简单但会中断数据流第二种方法复杂但不影响正常传输。我一般会在系统启动时做一次完整校准然后在运行过程中监控错误率。如果错误率超过阈值就触发一次快速校准——只扫描当前tap值附近的几个值而不是全范围扫描。这样校准时间短对数据流的影响小。注意IDELAY的参考时钟必须稳定。如果参考时钟有抖动IDELAY的延迟也会抖动校准结果就不准。建议用专用的时钟资源给IDELAY提供参考时钟不要和逻辑时钟混用。3. 核心细节二ISERDES的位宽选择与时序约束3.1 为什么选8位而不是4位ISERDES是ZYNQ PL端的串并转换原语支持SDR和DDR模式位宽可以配成2、3、4、5、6、7、8。对于MIPI DPHY来说常用的位宽是4位或8位。选8位的好处是一次转换8个bit对于800Mbps的lane速率转换后的并行时钟是100MHz。这个频率对于后续的逻辑处理很友好时序余量大布线容易。选4位的话并行时钟是200MHz虽然也能做但对时序的要求更高尤其是在资源利用率高的时候布线可能比较紧张。但8位也有代价。ISERDES的8位模式需要用到两个ISERDES级联会占用更多的资源。而且8位模式的时序约束更复杂需要正确设置CLKDIV和CLK的关系。我的经验是如果资源允许优先选8位。100MHz的并行时钟在ZYNQ-7000上很容易满足时序后续的状态机和缓存逻辑也好写。如果资源特别紧张再考虑4位。3.2 ISERDES的时序约束怎么写ISERDES的时序约束是手动实现里最容易出错的地方之一。Xilinx的UG471手册里有详细的说明但实际写的时候还是容易漏。关键约束有几个。第一是CLK和CLKDIV的相位关系。ISERDES要求CLKDIV是CLK的整数分频而且相位要对齐。在8位DDR模式下CLKDIV的频率是CLK的1/4。这个关系必须通过BUFIO和BUFR来保证不能直接用MMCM分频否则相位关系可能不对。第二是输入数据的建立保持时间。ISERDES的输入是来自IDELAY的输出IDELAY的输出到ISERDES的输入之间要有正确的时序约束。一般用set_input_delay来约束但要注意IDELAY的延迟是动态变化的所以约束的时候要按最坏情况来。第三是CLKDIV到后续逻辑的时序。ISERDES输出的并行数据是同步到CLKDIV的后续逻辑如果用CLKDIV采样需要保证CLKDIV的skew足够小。一般用BUFG来驱动CLKDIV保证全局时钟树的skew可控。我踩过的一个坑是忘了约束IDELAY的参考时钟。IDELAYE2需要一个REFCLK这个时钟的频率决定了tap的延迟值。如果REFCLK不稳定或者频率不对IDELAY的延迟就不准。这个时钟一般用200MHz可以用MMCM从系统时钟分频得到但要注意MMCM的输出抖动要小。3.3 位对齐和字对齐的处理ISERDES输出的是8位并行数据但这8位在字节里的顺序是不确定的。因为ISERDES的串并转换是从某个bit开始具体从哪个bit开始取决于时钟的相位和IDELAY的延迟。所以需要做位对齐。位对齐的方法是发送一个已知的pattern比如0xAA然后观察ISERDES输出的8位数据看它是0xAA还是0x55还是其他移位后的值。然后调整ISERDES的BITSLIP或者调整IDELAY的tap让输出变成正确的0xAA。字对齐是更高一层的问题。MIPI DPHY的字节流里每个lane的数据是独立的需要找到每个lane的字节边界。这个一般用MIPI的同步序列来做比如HS同步序列里的0xB8。检测到0xB8之后就知道字节边界在哪里了。这两个对齐过程一般在初始化的时候做一次之后只要时钟和延迟不变就不需要重新做。但如果温度变化导致IDELAY漂移可能需要重新对齐。4. 核心细节三时钟方案的设计与BUFR/BUFIO的使用4.1 MIPI DPHY的时钟特点MIPI DPHY的时钟lane是DDR的也就是说数据在时钟的上升沿和下降沿都有效。对于800Mbps的lane速率时钟频率是400MHz。这个时钟从引脚进来之后需要经过IBUFDS转成单端然后送到ISERDES作为CLK。但ISERDES的CLK不能直接用这个400MHz的时钟因为ISERDES需要CLK和CLKDIV两个时钟而且它们之间有严格的相位关系。CLK是400MHzCLKDIV是100MHz8位DDR模式下。这两个时钟必须来自同一个源而且相位要对齐。4.2 BUFIO和BUFR的分工在ZYNQ的时钟资源里BUFIO和BUFR是专门为ISERDES/OSERDES设计的。BUFIO驱动CLKBUFR驱动CLKDIV。BUFIO的skew很小适合高速时钟BUFR可以分频适合生成CLKDIV。具体连接方式是时钟lane经过IBUFDS之后送到BUFIOBUFIO的输出驱动ISERDES的CLK。同时这个时钟也送到BUFRBUFR配置成4分频8位DDR模式输出100MHz的CLKDIV驱动ISERDES的CLKDIV。这里的关键是BUFIO和BUFR必须用同一个时钟源而且BUFR的分频比要正确。如果分频比错了CLKDIV和CLK的相位关系就不对ISERDES就采不到正确的数据。还有一个细节BUFIO只能驱动同一个bank里的ISERDES不能跨bank。所以如果lane分布在多个bank里每个bank都需要自己的BUFIO和BUFR。这就涉及到时钟资源的分配问题需要在设计初期就规划好。4.3 多lane的时钟方案对于4条数据lane加1条时钟lane的配置时钟lane一般放在中间的bank数据lane分布在相邻的bank里。每个bank的ISERDES都需要自己的BUFIO和BUFR但它们的时钟源都是同一个时钟lane。具体做法是时钟lane经过IBUFDS之后通过BUFG送到各个bank然后在每个bank里用BUFIO和BUFR重新驱动。这样做的目的是保证每个bank的ISERDES都能得到正确的CLK和CLKDIV同时避免跨bank的时钟skew。这里有个坑BUFG的延迟比较大而且skew也不如BUFIO小。如果直接用BUFG驱动ISERDES的CLK时序可能不满足。所以必须用BUFIOBUFG只是用来把时钟送到各个bank的BUFIO输入。另外BUFR的分频比要一致。如果某个bank的BUFR分频比设错了那个bank的数据就会错位。调试的时候如果发现某个lane的数据总是错的可以先检查这个lane所在bank的BUFR配置。5. 核心细节四lane间skew的测量与补偿5.1 skew从哪里来MIPI DPHY的多个lane之间理论上应该是同步的。但实际上由于PCB走线长度不同、连接器引脚长度不同、FPGA内部布线延迟不同各个lane到达ISERDES的时间会有差异。这个差异就是skew。skew的大小取决于PCB设计。如果走线等长做得好skew可能在几十ps以内。如果走线等长做得不好skew可能达到几百ps甚至超过一个UI。对于800Mbps的信号一个UI是1.25ns如果skew超过这个值就会导致采样错误。5.2 用IDELAY补偿skew补偿skew的方法是用IDELAY。每个lane都有自己的IDELAY可以独立调整。通过调整每个lane的IDELAY tap值让所有lane的数据到达ISERDES的时间对齐。具体操作是先让所有lane的IDELAY都设成中间值然后发送一个已知的pattern观察每个lane的采样结果。如果某个lane的错误率明显高于其他lane说明这个lane的skew比较大需要调整它的IDELAY。调整的方向是如果某个lane的数据总是偏早就增加它的IDELAY tap如果偏晚就减少。反复调整直到所有lane的错误率都降到最低。这里有个技巧可以同时观察多个lane的采样结果找到一个让所有lane都正确的tap组合。因为IDELAY的调整范围有限有时候需要折中让所有lane都在可接受的错误率范围内。5.3 动态skew补偿温度变化会导致skew变化。因为不同lane的走线在PCB上的位置不同温度分布可能不均匀导致skew漂移。如果skew漂移超过一定范围就需要重新补偿。动态补偿的方法是在运行过程中定期检测每个lane的错误率如果某个lane的错误率上升就微调它的IDELAY tap。微调的步长可以小一点比如1到2个tap避免调整过大导致数据中断。我一般会在FPGA里做一个简单的错误计数器每个lane一个。每隔一段时间比如1秒读一次计数器的值如果某个lane的错误数超过阈值就触发一次微调。微调的方向根据错误率的变化趋势来判断如果增加tap之后错误率下降就继续增加如果上升就反向调整。注意动态调整IDELAY的时候要确保不会影响正在传输的数据。一般会在帧消隐期间调整或者用双缓冲的方式先调整备用通道再切换。6. 核心细节五初始化状态机的设计与调试6.1 初始化流程的拆解MIPI DPHY的初始化不是一蹴而就的需要按顺序完成几个步骤。第一步是等待时钟lane稳定检测到HS时钟之后才能开始数据lane的初始化。第二步是每个数据lane进入HS模式发送同步序列。第三步是检测同步序列完成字节对齐。第四步是检查所有lane是否对齐如果对齐就进入正常接收状态。这个流程看起来简单但每一步都有细节。比如时钟lane的稳定检测不能只看有没有时钟还要看时钟的频率是否正确。如果时钟频率不对后面的数据肯定不对。再比如同步序列的检测MIPI的HS同步序列是0xB8但实际收到的可能是0xB8的移位版本需要做模式匹配。6.2 状态机的状态划分我一般会把初始化状态机分成几个状态IDLE、WAIT_CLK、WAIT_SYNC、ALIGN、READY。每个状态的跳转条件要明确避免死锁。IDLE是上电后的初始状态等待系统复位释放。WAIT_CLK是等待时钟lane稳定检测到稳定的HS时钟之后跳转到WAIT_SYNC。WAIT_SYNC是等待数据lane的同步序列检测到0xB8之后跳转到ALIGN。ALIGN是做字节对齐和lane间对齐对齐完成之后跳转到READY。READY是正常接收状态如果检测到错误或者超时可以跳回WAIT_SYNC重新同步。每个状态里都要有超时计数器避免因为某个条件一直不满足而卡死。比如WAIT_CLK状态如果超过一定时间还没检测到时钟就报错或者重试。6.3 调试时怎么定位问题初始化状态机调试的时候最怕的是不知道卡在哪一步。我的做法是在每个状态里加一个计数器记录进入这个状态的次数和停留的时间。然后用ILA抓这些计数器的值就能知道状态机走到哪一步了。如果卡在WAIT_CLK说明时钟lane有问题。先检查IBUFDS的输出有没有时钟再检查BUFIO和BUFR的配置对不对。如果卡在WAIT_SYNC说明数据lane没有收到同步序列。先检查IDELAY的tap值是不是在有效范围内再检查ISERDES的位对齐对不对。如果卡在ALIGN说明字节对齐没完成。检查BITSLIP或者IDELAY的调整逻辑。还有一个常见问题是状态机跳转条件太严格。比如同步序列检测如果要求连续收到多个0xB8才认为同步可能会因为偶尔的误码而一直同步不上。这时候可以放宽条件比如允许中间有一个字节的错误。6.4 常见问题速查表现象可能原因排查方法所有lane都无数据时钟lane未稳定检查IBUFDS输出、BUFIO/BUFR配置某个lane数据错误率高该lane的IDELAY不准重新扫描该lane的IDELAY tap数据偶尔错位lane间skew漂移检查温度重新校准skew同步序列检测不到位对齐错误检查ISERDES的BITSLIP设置状态机卡死超时条件未设置每个状态加超时计数器数据正确但帧率低初始化时间过长优化校准算法减少扫描范围7. 实操心得与避坑建议7.1 板级设计阶段的注意事项很多MIPI DPHY的问题其实是在PCB设计阶段就埋下的。如果走线等长做得不好后面调IDELAY会非常痛苦。我的建议是数据lane和时钟lane的走线长度差异控制在5mil以内差分对内的P/N走线长度差异控制在2mil以内。如果做不到至少要把差异控制在可补偿的范围内。另外ESD保护器件的选型也很重要。有些ESD器件的寄生电容比较大会恶化信号完整性。选的时候要看电容值一般要小于0.5pF。还有连接器的选型阻抗要匹配插损要小。7.2 调试工具的准备调试MIPI DPHYILA是必不可少的。但ILA的采样深度有限抓高速信号的时候可能不够用。我的做法是在FPGA里加一个简单的错误统计模块把每个lane的错误数、IDELAY的tap值、状态机的状态都存到寄存器里然后用PS端读出来。这样不需要ILA也能监控运行状态。另外准备一个已知的测试pattern发生器。可以用FPGA自己产生也可以用传感器输出。测试pattern最好是伪随机序列这样能覆盖更多的数据组合更容易发现时序问题。7.3 版本兼容性的坑Xilinx的工具链版本对原语的支持有差异。比如Vivado 2015.4和2018.3的IDELAYE2原语端口名称和参数可能略有不同。如果项目需要跨版本迁移要提前检查原语的兼容性。还有不同器件的IDELAY特性也有差异。ZYNQ-7020和7045的IDELAY tap延迟可能不一样移植的时候要重新校准。不能直接复制tap值。7.4 资源优化的技巧手动实现MIPI DPHY接收会占用不少资源尤其是ISERDES和IDELAY。如果资源紧张可以考虑以下优化第一只在必要时使用8位模式如果速率不高可以用4位模式节省资源。第二多个lane可以共享一些控制逻辑比如状态机和错误计数器。第三IDELAY的校准逻辑可以复用不需要每个lane一套。但要注意共享逻辑可能会引入额外的时序问题。比如多个lane共享一个状态机状态机的输出到各个lane的IDELAY控制端口之间可能有布线延迟导致调整不同步。这种情况下可以在每个lane加一个简单的同步寄存器保证控制信号同时到达。7.5 长期运行的稳定性工业应用要求7x24小时运行稳定性很重要。除了前面说的动态校准还要考虑异常恢复。比如如果某个lane突然失锁状态机要能自动重新初始化而不是卡死。我的做法是加一个看门狗如果状态机在某个状态停留超过一定时间就强制复位重新初始化。另外错误统计要定期清理避免计数器溢出。可以用PS端定期读取并清零或者在FPGA里做自动清零。8. 这套方案还能怎么扩展手动实现MIPI DPHY接收之后后续可以扩展的方向不少。比如可以支持更高的lane速率只要换用更快的ISERDES模式或者增加IDELAY的调整范围。也可以支持更多的lane数只要时钟资源够用。还可以把接收到的数据直接送到DMA通过AXI总线传到PS端的内存里实现高速图像采集。如果项目需要还可以在PL端做简单的图像处理比如去噪、边缘检测减轻PS端的负担。这些扩展都需要在现有框架上做增量开发核心的DPHY接收逻辑不需要大改。我在实际项目里最大的体会是手动实现MIPI DPHY接收难点不在写代码而在调试。调试的关键是有一套好的监控手段能快速定位问题在哪一步。ILA加错误统计寄存器基本能覆盖大部分问题。剩下的就是耐心一个一个tap地调一个一个lane地对齐。急不得。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →