
做DP接收这件事是我在一个视频采集项目里啃下来的硬骨头。板卡用的是Zynq UltraScale RFSoC系列具体型号是XCZU47DR需求是把PC显卡输出的DisplayPort信号通过PL侧RX接进来经过裁剪和算法处理之后写入DDR。很多人一开始觉得DP RX就是把Xilinx的DisplayPort IP拖出来、连线、跑个中断就行但真正做完一轮会发现链路训练、GT参考时钟、AXI-Stream对齐和中断处理这四个环节互相咬合任何一个地方没处理干净最终表现就是黑屏或者花屏。这篇文章把这套流程从头到尾拆开包括整体架构怎么定、硬件和IP参数怎么填、链路训练和DPCD怎么交互、PL到PS的中断链路怎么搭再加上我在实际调试中踩过的坑。适合正在用PL侧接DP信号做采集、转换、处理的工程师特别是第一次玩DisplayPort IP的人。文章里有些排查思路不完全是文档上的标准答案但都是实测下来真实有效的东西。1. 架构选型与整体数据通路设计1.1 为什么非要在PL侧做DP RX先回答一个很多人都会问的问题Zynq UltraScale的PS侧不是自带DisplayPort控制器吗为什么还要在PL侧做接收这里面有个很关键的区别——PS侧自带的DisplayPort硬核是source方向也就是用来往外输出视频信号的芯片手册里它对应的是DisplayPort TX。如果你想做视频采集、图像回显、画中画、机器视觉这类需要把外部设备画面“吞”进来的应用PS硬核根本用不上。还有一个现实场景是接口类型。很多设备输出的DP信号是4 lane的或者走Type-C形态的DP Alternate ModePS硬核很难直接覆盖这些灵活多变的应用。PL侧DP RX的灵活性在于GT资源是通用的速率等级可以配置视频数据从IP出来之后直接进AXI-Stream总线再往DDR写还是往任意一个IP送都由你自己控制。这个灵活度是做视频采集板卡非常需要的。当然用RFSoC的GT去干DP接收有点“杀鸡用牛刀”的感觉但工程上的稳定性优先。GT本来就支持各种高速串行协议8b/10b编码、CDR、时钟恢复这些东西都是现成的只要把DisplayPort IP的sink模式例化好协议适配的事情就省了一大半。真正需要开发人员操心的其实是从IP出来之后的视频流怎么接、中断怎么用。1.2 从连接器到DDR的完整通路PL侧DP RX的整体数据通路我建议在动手写代码之前先画一张框图至少包含下面这几段DP物理接口4 lane差分对 AUX HPD热插拔检测GT的高速收发器通道负责CDR、解串、8b/10b解码Xilinx DisplayPort IPsink模式负责AUX事务、链路训练、视频流解析AXI4-Stream视频输出总线输出已经解析好的像素数据和同步信息VDMA或者自研DMA把AXI-Stream数据写进DDRPS侧中断处理通过GIC接收事件并驱动上层状态机在sink模式下DisplayPort IP的AUX接口是自动工作的IP内部有对应的寄存器用来响应source主机发来的DPCD读取、写入请求。链路训练完成后IP会把接收到的视频时序参数解析出来并通过AXI4-Stream输出像素和同步信号。这个通路里有一个容易忽视的点DP IP输出的视频流是像素时钟域的而VDMA和DDR系统通常工作在另一个时钟域两者之间天然需要跨时钟处理。好在AXI4-Stream的tready/tvalid握手机制就是为这种跨时钟场景设计的只要VDMA的S2MM通道配置正确数据不会丢。但是如果你用自研DMA那两边的FIFO深度和反压机制就要自己想清楚。1.3 带宽要提前算别等花屏再后悔我在这个项目里第一件事不是连IP而是把DP链路的带宽和DDR的写入带宽都拉了一张表。很多人觉得算带宽很简单但实际上疏忽的人不在少数尤其在做4K分辨率的时候。DP链路的可用带宽取决于协商出来的线速率和lane数。拿HBR2为例单lane编码前速率是5.4Gbps因为DP 1.4用的是8b/10b编码有效带宽要乘以0.8单lane就是4.32Gbps4 lane合计17.28Gbps。HBR3单lane编码前8.1Gbps扣除编码开销后单lane约6.48Gbps4 lane合计25.92Gbps。再看视频本身要吃多少带宽。RGB888是24 bit每像素可以把空白时段算进去用像素时钟直接算视频总带宽 像素时钟频率 x 每像素bit数分辨率刷新率像素时钟数据带宽4 lane HBR2剩余空间4 lane HBR3剩余空间1080p6060Hz148.5MHz3.56Gbps充足充足1440p6060Hz297MHz7.13Gbps充足充足4K6060Hz594MHz14.26Gbps较紧张充足DDR写入带宽也要算。1080p60的RGB888有效像素数据量是1920x1080x3字节x60帧约373MB/s。但如果按实际像素时钟算加上blankingIP输出到VDMA的瞬时速率约445MB/s。DDR3/DDR4随便都能扛住这个量但如果你是4K60瞬时带宽约1.78GB/s再加上PS侧的缓存刷新、协议开销、AXI突发效率损失建议DDR位宽至少配到64bit控制器时钟频率别太低。这个计算不是精确到每一拍而是判断整个设计有没有在带宽上留下足够余量。2. 硬件配置GT、参考时钟与IP参数2.1 GT位置和参考时钟的选择门道DisplayPort IP在Vivado里例化之后GT的位置通常是自动分配的但你必须确认对应的GT Quad有没有可用的参考时钟管脚而且这个参考时钟的频率必须和IP配置匹配。DP的GT参考时钟一般用100MHz差分时钟走专用参考时钟引脚进来经过IBUFDS_GTE4直接给GT参考。这里有个特别容易踩的坑不要把GT参考时钟随便从普通IO绕进来即使你后面加了PLL或者MMCM也不行。GT的CDR对参考时钟的抖动极其敏感一旦参考时钟不干净最典型的故障就是链路训练时CDR无法锁定表现为source端一直发TP1/TP2训练序列sink这边却始终锁不上。在PCB设计阶段就应该把专用参考时钟引脚的位置预留好靠近GT的电源也要做好滤波。再说GT复位。做过Aurora IP核的人对gt_reset、reset、power_down这几个信号应该不陌生Aurora核通常要求用户自己管理GT复位时序。但DisplayPort IP不一样它内部会自动管理GT的复位和power down用户侧只需要保证IP的全局复位信号复位后正常释放即可。如果你在别的地方又把GT的power_down信号拉低破坏了IP内部的状态机DP链路就会一直起不来。排查这类问题的时候先查一下是不是有其他逻辑占用了同一个GT Quad的资源。2.2 DisplayPort IP核sink模式的关键参数怎么填以Vivado里的Xilinx DisplayPort IP核对应PG245为例例化时方向选成sink剩下几个参数要特别注意。第一是最大链路速率等级。你至少要支持到HBR2只做到RBR/HBR那基本只能接很老的低分辨率设备。如果项目目标是4K60建议直接选到HBR3同时确认所选的FPGA器件GT在那个速率档位上能稳定工作。HBR3的8.1Gbps对PCB走线和连接器要求更高不是光IP配置支持就完事。第二是lane数。DP标准支持1、2、4 lane实际应用里PC显卡和DP转接器基本都按4 lane输出。选成4 lane同时保留协商能力这样当线缆质量不好或者远端只支持2 lane时source会降级协商不至于直接黑屏。第三是参考时钟频率。大部分模板默认100MHz如果你板上有别的GT也用了不同的参考时钟要仔细核对IP内部期望的时钟源。这个频率错了链路训练时CDR出来的恢复时钟会偏训练大概率失败。作为一个补充现在很多设备的DP信号是走Type-C口出来的。Type-C转DP涉及DP Alternate Mode需要在Type-C侧做CC逻辑和方向检测这部分通常由PD控制芯片完成FPGA侧看到的还是标准的DP信号。但如果你计划直接用Type-C座子接DP信号不能把DP的lane直接连到GT就算完了CC检测不做主机端根本不会输出信号。板级方案上这块必须单独处理。2.3 视频时序解析与AXI-Stream对齐sink模式下IP会自动解析source端发来的视频时序参数包括Htotal、Vtotal、分辨率、像素格式等这些参数可以从寄存器读出来确认。IP输出到AXI-Stream的视频流tdata位宽和像素打包格式和你的参数配置有关比如BGR888、RGB888、YUV444等。第一次接触这个IP的人最容易犯的错是拿直觉去猜tdata和像素的对应关系。比如64bit总线上理论上每拍可以带2.67个24bit像素但实际IP通常会有自己的打包方式可能是两像素一组加上填充也可能是按特定的字节排列来简化逻辑。这种问题猜没用直接抓波形看最靠谱。用ILA把tdata、tvalid、tready、tuser和tlast抓下来对照一帧画面的起始和结束信号去验证你的推测。这里还牵扯到一个强制约束的问题。我自己做过IDDR抓RGMII的时序约束深知这类输入时序如果不约束好采样位置稍微偏一点整个数据就对不上。DP IP好在GT和核心逻辑的时序是IP内部处理好的用户不需要给视频流接口额外写set_input_delay之类的约束但AXI-Stream链路在FPGA内部的布线时序必须满足否则跑到高频会出现偶发抖动这类问题非常难查。如果布线紧张建议把IP的时钟和复位在约束里显式声明必要时用Pblock把DP和数据通路圈到相邻的SLR里。3. 链路训练机制与DPCD的交互和状态判读3.1 source和sink是怎么握上手的链路训练是DP协议里最核心的一段握手过程。source端在HPD有效之后通过AUX通道访问sink的DPCD寄存器依次完成下列动作读取能力寄存器、设置链路速率、设置lane数、写入训练模式然后在主链路上发送训练序列。最开始是时钟恢复阶段source发送Training Pattern 1sink的GT在这个阶段要完成CDR锁定锁定之后sink把对应的DPCD状态寄存器置位source读到之后进入均衡训练阶段发送Training Pattern 2调整接收端的均衡器直到sink达到symbol lock并满足误码要求。训练成功后双方把训练模式寄存器清零进入正常工作状态。对FPGA sink来说这个过程大部分由IP内部状态机自动化处理。听起来很省心但我建议把链路训练的几个关键状态抓出来看一遍别什么都不管。原因在于当链路训练失败时如果只知道“失败”两个字排查范围会很大如果知道是卡在CR还是EQ范围一下就缩到GT和参考时钟上。3.2 软件侧如何感知训练状态在裸机环境下软件和DP IP的交互基本就是读状态寄存器、写控制寄存器、等待中断。初始化流程大概是这样的// 1. 复位和初始化DP IP XDp_Reset(DpInstance); // 2. 等待link training完成中断/状态 while ((XDp_GetStatus(DpInstance) DP_STATUS_LINK_TRAINING_DONE) 0) { // 可以加超时保护 } // 3. 读取协商结果 LinkRate XDp_GetLinkRate(DpInstance); LaneCount XDp_GetLaneCount(DpInstance); // 4. 根据读取到的时序参数配置VDMA或后续处理需要注意不同版本的IP核寄存器定义和驱动接口可能有差异以你实际使用的Vivado版本对应的BSP驱动为准。但总体的逻辑是一致的训练做完之后IP的寄存器里能读到实际协商的链路速率和lane数同时能读到source传过来的视频格式。这些信息对上层应用很有用比如你可以根据分辨率动态分配DDR缓冲。3.3 训练失败在调试器里的长相链路训练失败的现场从现象上可以分成几类。我整理了一个简单的判读表方便遇到问题时先对号入座现象可能原因优先排查CDR Lock一直不成功GT参考时钟频率不对、抖动太大参考时钟源、电源纹波EQ阶段反复无法完成线缆质量差、链路损耗大换线、检查连接器Source不发送训练序列HPD没有正常拉高、DPCD读取异常HPD电路、AUX通道训练成功后立即掉链接触不良、GT电源不稳定焊接、电源动态响应在我实际项目里遇到过最隐蔽的一次是GT参考时钟和外部时钟源相位噪声偏大表面上看训练能过但只要帧率一高就会出现偶发黑屏。后来用示波器看时钟频谱才发现是在某个频率上有一个明显的杂散。所以强烈建议DP调试过程别舍不得用频谱仪和示波器很多物理层问题光看寄存器状态是看不出来的。4. 中断处理PL到PS的最后一公里4.1 中断源先梳理清楚DP接收这个场景里你会碰到的中断源其实不少至少包含这几类DP IP自身的状态中断link training完成、视频参数更新、AUX错误等VDMA的S2MM通道中断帧写入完成、帧错误以及你自定义逻辑里可能产生的缓冲切换中断。其中DP IP的中断可以理解为一个“事件汇总口”link training完成这种关键事件会在这个中断里置位。VDMA的帧完成中断则是业务核心上层应用往往需要根据它来触发图像处理或者帧缓冲切换。4.2 PL到PS的硬件连线在Block Design里DP IP的中断输出接到一个xlconcat再连到Zynq UltraScale PS核的pl_ps_irq0或pl_ps_irq1端口。VDMA的s2mm_introut也一样。这样PS侧GIC就能统一收到来自PL的中断。中断号在生成的xparameters.h里会有定义名字通常是XPAR_FABRIC_xxx_INTR。用XScuGic驱动注册中断服务函数时直接拿这个宏当中断ID。这里有个细节多个PL中断共享同一个GIC SPI的情况很常见如果你的DP中断和VDMA中断都挂在pl_ps_irq0上注册时要注意每个ID还是独立分配的别因为共享物理通道就把它们当成同一个中断去处理。4.3 中断服务程序怎么写得稳中断服务程序的原则是“快进快出”。不要在ISR里去拷贝图像数据也不要在里面做耗时算法。正确做法是置标志、喂状态机、清中断。下面是个简化框架void DpIsr(void *CallbackRef) { u32 status XDp_GetInterruptStatus(DpInstance); XDp_ClearInterruptStatus(DpInstance, status); if (status DP_INT_LINK_TRAINING_DONE) { gDpState | DP_STATE_TRAINING_DONE; } if (status DP_INT_VIDEO_PARAM_UPDATE) { gDpState | DP_STATE_PARAM_UPDATED; } } void VdmaS2mmIsr(void *CallbackRef) { u32 irqStatus XVdma_GetS2mmIrqStatus(Vdma, XVDMA_IRQ_ALL_MASK); XVdma_ClearS2mmIrqStatus(Vdma, irqStatus); if (irqStatus XVDMA_IRQ_ALL_MASK) { gFrameCnt; gFrameReady 1; } }这个代码里有两个点要特意说。第一是清中断的时机必须先把状态寄存器里对应的位读出来再回写清除不能随便清。第二是ISR里不要调用太重的外部函数否则嵌套中断和优先级处理会把你拖入深渊。处理视频帧的时候建议用双缓冲或者环形缓冲。VDMA配置成S2MM模式下把帧指针在ISR里切换上层应用读取当前帧时DMA已经在写下一帧这样能避免撕裂和丢帧。如果只有单缓冲ISR里动作稍微慢一点新一帧就会覆盖正在读的数据画面上会看到明显的闪跳。5. 调试实战DP RX项目里绕不开的坑5.1 Link Training失败排查清单链路训练失败永远是DP RX项目里最常见的问题。按我自己的经验排查优先级排列如下先看HPD有没有正常拉高。HPD是source端决定是否开始训练的前提HPD不拉高后面全是白忙活。检查硬件电路到寄存器状态HPD信号是否被正确引到IP。再看AUX通道是否正常。Source通过AUX访问DPCD寄存器如果AUX通路不通source就会认为sink不存在训练根本不开始。AUX是双向半双工它的时序比较特殊用逻辑分析仪看是不是有持续的错误应答。然后看GT参考时钟。频率对不对、抖动大不大直接决定了CDR能不能锁定。如果你在调试时发现电源纹波偏大先别急着怀疑IP配置拿示波器看参考时钟和GT电源。最后才是IP参数和寄存器配置。比如最大链路速率设置过高、而你的线缆/背板根本达不到这个速率时训练会反复失败。这种情况下可以把IP的最大速率临时降到HBR再试用来区分是物理层问题还是配置问题。5.2 画面异常黑屏、花屏与撕裂链路训练都已经通过了但画面出来不对这种问题同样很磨人。黑屏的话很大概率是DP IP到VDMA的视频流没有真正被“消费”掉。检查IP的stream enable寄存器、VDMA的S2MM通道是否使能以及AXI-Stream的tvalid/tready握手是不是一直没拉起来。花屏的话重点关注两件事tdata位宽和像素格式不匹配、DDR写入带宽不足。我在项目里遇到过一次花屏原因是IP输出的像素格式是BGR888而我把VDMA后面的数据处理逻辑当成RGB888来读红蓝通道对调导致颜色错乱。颜色错乱和花屏容易混淆但对齐关系查清楚这类问题半小时就能定位。还有一种现象是画面“撕裂”上半帧是上一画面的内容下半帧变成新画面。这是帧缓冲切换和读取不同步造成的通常要在ISR里切帧指针配合VDMA的帧完成中断来做双缓冲同步。别小看这个很多看似随机出现的闪跳其实就是这个原因。5.3 电源、POR_B与老工具链的坑调试过程中还遇到一个经典的硬件管理问题Vivado Hardware Manager连接器件时偶尔会报“labtools 27-3421: xczu47dr_0 pl power status off, cannot connect pl tapcheck por_b signal”之类的错误。这个报错字面意思是PL电源状态不对或者PL侧的TAP连不上。实际排查方向是确认PL的VCCINT、VCCAUX等电源是否稳定上电确认POR_B信号在复位释放时是否满足时序要求还要确认PS侧的启动模式配置是否允许PL在启动阶段被正确供电。还有一个很小但很烦的问题老版本工具链卸载不干净。我之前在一台机器上装过旧的Xilinx SDK 2015.4后来升级新版本时JTAG驱动和软件环境变量总是冲突。折腾一圈发现是旧版本卸载后残留了环境变量和部分服务。这种情况只能手工清理环境变量、删除残留的安装目录再重装新版本。建议做FPGA工具链切换的时候要么装独立虚拟机要么卸载后把注册表和环境变量查一遍再装新的。多说一句如果你板子上同时有RF-ADC、PLL这类模拟器件注意它们和GT的电源平面之间要做好隔离。我见过一个项目AD9361初始化的时候CP ovrg置高、RX PLL锁不上当时怎么查都像是配置问题后来发现是GT或数字逻辑工作时动态电流太大把共用的模拟电源拉出了毛刺。这和你DP训练时CDR锁不上的情况非常像都是电源质量在背后捣鬼。高速接口的设计里电源噪声永远是第一怀疑对象。5.4 调试手段和辅助工具调试DP RX我习惯把工具分成两层。功能前先用ILA抓IP和DMA之间的AXI-Stream协议确认数据流有没有真正跑起来。这里ILA采样深度尽量配大因为你可能需要跨帧观察数据太浅抓不到有效事件。第二步是用PS侧的软件直接读寄存器比如通过串口打印DP IP的状态值、VDMA的状态和当前帧计数比每次改代码重编译快很多。AUX通道的调试用逻辑分析仪就行。AUX本身是1MHz的低速信号不需要高速示波器就能抓到协议波形。看有没有正常的读请求和正确应答很快就能判断DPCD通路是否顺畅。如果嫌脚本麻烦直接在Vivado里看状态寄存器也可以但不如抓波形直观。文档方面尽量把PG245DisplayPort产品指南和对应VDMA的文档放在手边。它们内容丰富遇到寄存器细节问题直接查比在网上搜强得多。在整个DP RX项目里我个人的体会是这个设计的物理层决定了它能不能用链路训练和GT参考时钟是项目的成败关键一旦训练稳定通过后面的中断和软件处理反而只是常规工作。如果你正在调试DP接收建议先从物理层入手把HPD、AUX、GT参考时钟、线缆这四件事确认干净再考虑软件问题。链路训练通过之前不要急着写中断逻辑否则排查时多个变量叠在一起你会非常痛苦。