ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FPGA实战:HDMI视频输入与环路输出设计与调试全解析

FPGA实战:HDMI视频输入与环路输出设计与调试全解析 做FPGA开发这几年我越来越觉得视频接口这块是绕不开的坎。不管是做图像处理、工业相机采集还是简单的显示驱动HDMI几乎成了标配接口。很多刚入门的朋友跑通了流水灯、跑通了串口一到视频输入就开始懵——时序参数一堆、编码协议复杂、还要处理跨时钟域再加上环路输出这种看似简单实则坑不少的功能很容易卡死。这篇博文就围绕黑金开发板上的HDMI视频输入与环路输出实验把整个设计思路、核心原理、具体实现和调试排障过程完整拆开讲一遍。这个实验说白了就是用FPGA把HDMI输入的视频信号接收进来经过处理后或者不做处理再通过HDMI接口原样输出出去实现“输入即输出”的环路效果。它能解决什么问题呢最典型的是视频链路调试、信号中继、以及后续做图像处理算法时的数据源验证。适合FPGA入门进阶、想搞图像处理又不知道从哪下手的同学参考。下面我从头到尾把整个项目过一遍。1. 项目概述与整体设计思路1.1 实验目标拆解环路输出Loop-through在专业视频设备里非常常见比如切换台、分配器、采集卡上都有这个功能。它本质上是把输入端口的视频信号经过内部处理后再从输出端口送出去。这个实验在FPGA上的具体表现就是HDMI接口A接收视频源比如电脑、机顶盒、相机FPGA内部完成串行信号转并行像素数据、时序恢复、缓冲同步然后把恢复出来的像素数据重新编码成TMDS串行信号从HDMI接口B输出到显示器。我这里用的是黑金AX7A200开发板核心芯片是Xilinx Artix-7系列。板上有标准的HDMI输入接口和输出接口分别通过TMDS差分对连接到FPGA的引脚。整个设计主要包含三个层次硬件接口层HDMI连接器、保护电路、阻抗匹配、FPGA逻辑层接收端、处理端、发送端、以及主机配置层通过I2C/DDC通道读取显示设备的EDID信息。三者缺一不可硬件不对直接黑屏逻辑不对花屏配置不对没信号。1.2 为什么选HDMI而不是其他接口很多初学者会问做视频输入实验为什么不用VGA或者DVP并口非要上HDMI这种高速串行接口答案是HDMI是目前消费电子领域应用最广泛的视频接口电脑、游戏机、电视、投影全都是HDMI信号源随手就能找到。而且HDMI本身就承载了音频、视频、控制信号学会HDMI的接收与发送后续再学MIPI、DisplayPort这类高速串行接口就有迁移基础底层都是SerDes那一套思想。从学习角度HDMI也足够有挑战性但不至于劝退。它的TMDS通道速率在1080p60下大约每通道1.485Gbps这个速率用Artix-7的ISERDESE2和OSERDESE2原语可以直接处理。比MIPI的DPHY层简单没有Lane管理、没有上下电握手机制比VGA多了编码和串行化难度正好卡在“跳一跳够得着”的位置。所以我一直觉得HDMI是FPGA图像处理入门的最佳切入点没有之一。1.3 环路输出方案选型考量环路输出有两种实现路径。第一种是模拟/芯片方案在硬件上用HDMI分配器芯片比如Silicon Image的芯片直接从输入端分出一路信号给输出端不经过FPGA逻辑。第二种是数字方案信号进FPGA经过接收、处理、再发送的完整链路。本实验采用第二种原因很直接我们不是在做一个硬件分线器而是要掌握FPGA处理视频信号的完整数据流。只有走数字方案你才能真正理解像素时钟恢复、行场同步对齐、数据缓冲这些核心知识点。当然走了数字方案就要接受它带来的挑战输入时钟和输出时钟都是外部晶振通过PLL产生的它们之间必然存在微小频差接收端的像素数据和本地时钟不同步需要做跨时钟域处理。这就是环路输出实验中最容易翻车的地方后面我会详细讲怎么绕过去。2. 硬件方案与核心器件拆解2.1 黑金开发板的视频链路架构黑金AX7A200板子的HDMI链路设计得比较典型值得参考。输入侧有一颗HDMI接收芯片或者直接接到FPGA的HP Bank高质量引脚上负责把HDMI的TMDS差分信号转化为FPGA能处理的电平输出侧类似FPGA产生的TMDS信号经过电平转换和驱动后送到HDMI连接器。板上的引脚分配、阻抗匹配、静电保护电路在原理图上都能直接看到这对理解高速信号设计的约束非常有帮助。具体到逻辑资源HDMI接收端需要用到FPGA的IDELAYE2输入延迟单元、ISERDESE2输入串并转换器、IBUFDS差分转单端缓冲器。发送端对应OSERDESE2并串转换器、OBUFDS单端转差分缓冲器。时钟部分由MMCM/PLL产生像素时钟和5倍频的串行时钟。这些原语是Xilinx 7系列实现SerDes的基础单元吃透它们基本就掌握了FPGA高速接口设计的底层逻辑。2.2 HDMI接口与引脚分配细节HDMI接口有19个引脚核心的是4对差分信号TMDS Data0、Data1、Data2和TMDS Clock。Data0通道在控制周期承载HSYNC、VSYNC和辅助数据Data1和Data2在控制周期承载辅助数据。数据周期则由三个通道一起并行传输R、G、B像素数据各8位。除了TMDS信号还有DDC通道I2C、CEC控制线、HPD热插拔检测、5V电源线。FPGA引脚分配时必须把同一对差分信号分配在同一个差分引脚对上而且尽量靠近不能让正负端的走线绕太远。对Artix-7这类芯片建议优先使用HPHigh PerformanceBank的引脚它们的IO速率支持更高。引脚分配在XDC约束文件里体现为set_property PACKAGE_PIN R4 [get_ports hdmi_in_clk_p] set_property IOSTANDARD TMDS_33 [get_ports hdmi_in_clk_p]TMDS_33是Xilinx专门为HDMI这种3.3V TMDS电平提供的IO标准它会自动配置输入差分对的正确端接和阻抗特性。这个细节很容易被忽略——有些同学用LVDS或者LVCMOS标准去约束HDMI引脚结果就是显示设备完全识别不到信号。2.3 HDMI供电与电流需求这里顺便说一个经常被问到的问题HDMI接口供电电流到底需要多大。标准HDMI源端需要提供5V电源最小电流是50mA但实际设计时建议留足余量到500mA以上。因为HDMI线缆本身有压降接收端设备可能需要从源端取电比如某些便携显示器、采集棒电流需求可能比规范最小值大得多。如果你自己设计板子HDMI的5V电源一定要单独加磁珠和滤波电容不能和FPGA其他电源混在一起否则视频信号上的噪声会导致花屏。在黑金板子上这个5V是通过板载LDO从主电源转换出来的。如果外接的设备功耗较高比如某些HDMI转接器就要注意电源指示灯是否变暗这往往是供电不足的前兆。3. 核心原理你要先搞懂这三件事3.1 TMDS编码从8位像素到10位串行数据HDMI的物理层叫TMDSTransition Minimized Differential Signaling最小跳变差分信号。它做了一件很巧妙的事情每一路数据通道上每个像素时钟周期串行发送10位数据而这10位数据是由8位像素数据编码而来。为什么要编码成10位两个原因一是尽量减少信号的跳变次数降低EMI二是嵌入时钟信息方便接收端做时钟恢复。具体分两步第一步是把8位数据转换成9位其中第9位表示传输的“跳变次数”是否符合减少跳变的策略第二步是在9位数据的基础上选择添加第10位保证直流平衡DC-balance即信号的高低电平累计时间基本相等。这个编码过程在FPGA里可以用查找表实现也可以直接用Xilinx GTP/GTX高速收发器自带的8b/10b编码器。对于本实验用的引脚直连方案我们需要自己写编码器或者生成LUT这一步看似简单但写错了会导致画面出现规律性噪点。我自己在调试时就遇到过这种情况画面能显示但每个像素值都差“一点点”具体表现是颜色偏色、垂直条纹。查了半天最后发现是编码表里DC平衡位的判定写反了。所以这里提醒一下编码模块一定要写一个简单的自测逻辑用已知像素值比如纯红色0xFF0000去验证编码后再解码是否还原准确。3.2 视频时序Hsync、Vsync与有效数据区HDMI的视频数据不是一秒不停传像素的它有严格的时间结构。每一帧图像分为有效显示区和消隐区blanking消隐区又细分为水平前沿HFP、水平同步脉冲HSYNC、水平后沿HBP和垂直方向的对应部分。只有处于有效显示区的数据才是真正的图像消隐期传输的是控制信号和音频辅助数据。拿1080p60来说一组常用参数是这样来自CTA-861标准参数数值有效水平像素1920水平前沿HFP88水平同步宽度HSYNC44水平后沿HBP148整行总像素2200有效垂直行数1080垂直前沿VFP4垂直同步宽度VSYNC5垂直后沿VBP36整帧总行数1125像素时钟148.5MHz看懂这个表整个HDMI接收端的设计就清楚了一半。FPGA要做的事情就是不断检测HSYNC和VSYNC的脉冲位置用计数器精确跟踪当前处于哪个阶段。一旦进入有效数据区就把三个通道的10位并行数据解码成8位RGB再拼接成24位像素数据。3.3 环路输出为什么不是“一根线便宜”完事很多初学者觉得环路输出简单——输入信号直接接到输出不就行了实际上当你用FPGA做中间环节时必须重新生成输出信号。原因至少有三个方面一是物理层信号格式可能不匹配比如输入是HDMI 1.4的深色模式12bit输出设备期望8bit二是信号经过线缆和连接器会有衰减和抖动直接转发会在输出端产生误码三是我们做环路输出的目的往往不是为了“转发”而是为了在中间插入处理算法缩放、叠加、滤波所以必须把信号彻底“解码成像素”再“重新编码”。因此真正可用的环路输出链路是输入TMDS串行信号 → FPGA引脚接收 → 串并转换ISERDESE2→ TMDS解码 → 像素数据同步信号 → 可选处理 → TMDS编码 → 并串转换OSERDESE2 → 输出TMDS串行信号。这个链路里的每一个环节都必须在同一个像素时钟域下工作而且要保证时序收敛。3.4 时钟体系PLL与相位对齐本实验最关键、也最容易出问题的是时钟设计。HDMI输入端的TMDS Clock是从源端设备传过来的频率等于像素时钟。我们通过IBUFDS收到这个时钟后用MMCMMixed-Mode Clock Manager对它进行倍频产生用于ISERDESE2采样的5倍频时钟比如745MHz。输出端的时钟则来自板载晶振经过另一个PLL产生148.5MHz像素时钟和742.5MHz串行时钟。两个时钟域之间用异步FIFOFirst-In First-Out做缓冲FIFO读写指针分别由两个时钟域控制有效隔离频差。这里有个经验环路输出的FIFO深度不用太大一般4行缓冲就足够应对时钟抖动但如果后面要做图像算法比如缩放、去隔行建议至少做一帧的DDR3缓存。另外FIFO的前后都必须加上同步信号和像素数据的“对齐打包”逻辑保证读出的数据在行场同步信号的正确位置插入。4. 实操过程与关键环节实现4.1 Vivado工程搭建和约束文件准备开发环境我用的是Vivado 2019.1黑金官方资料对应版本新版Vivado也兼容。建工程时选择芯片型号XC7A200T-2FBG676这个型号资源非常充裕实际设计利用率不到15%。工程建好后首先要做的事情是写XDC约束除了引脚分配还要定义输入时钟周期、设置时序例外。时钟约束这块有个坑输入端TMDS Clock来自外部设备Vivado不知道它的频率必须在XDC里用create_clock显式声明。如果漏了这条约束综合布线时会报告大量时序错误就算勉强跑通上板也是“间歇性工作”——放在桌面能显示稍微动一下线就黑屏就是因为时钟约束不对导致时序收敛点错误。create_clock -period 6.734 [get_ports hdmi_in_clk_p]6.734ns是148.5MHz的周期。对于1080p60输入源端设备会遵循标准频率如果你的信号源规格不同比如720p需要对应修改这个数值。4.2 视频输入采集模块ISERDESE2与IDELAYE2配合输入侧的逻辑核心是把1.485Gbps的串行数据流转换成148.5MHz的10位并行数据。Artix-7的ISERDESE2支持1:10模式吗直接支持到1:8再宽的就需要用两个ISERDESE2拼接。HDMI的10位数据正好触发这个限制所以大多数设计中采用的方式是用两个ISERDESE2分别采样奇数位和偶数位各输出5位拼接成10位。这个“5位拆分”逻辑非常考验时序必须在采样时钟相位正确的前提下才能稳定工作。IDELAYE2则用于精细调整输入数据的相位。线缆长度、连接器阻抗差异会导致数据相对于时钟的相位偏移不定通过IDELAYE2可以做到78psArtix-7的精度步进调节。调试时用ILAIntegrated Logic Analyzer观察数据稳定性从0开始逐步增加IDELAY值找到数据有效窗口的中心位置。我一般在粗调用固定值然后配合“眼图扫描”——写一个状态机自动遍历IDELAY值并统计误码率把所有误码率低的延迟值找出来取中间值设定。这一步是接收端稳定性的“生死线”。如果IDELAY不校准最典型的故障是某个分辨率能显示但刷新率不对或者不同线缆插上后表现不一致。说白了就是数据建立保持时间不满足输入采样点在数据跳变沿附近打摆。4.3 TMDS解码模块从10位并行数据到RGB像素ISERDESE2出来的10位数据是TMDS编码后的结果需要解码成8位原始像素数据。解码逻辑本质是编码的逆过程先去掉DC平衡位第10位再用查找表把9位映射回8位数据。这个逻辑不算复杂纯组合逻辑就能完成但对时序有要求必须保证三个通道的解码延时完全一致否则RGB三个分量会错位图像边缘出现彩色镶边。为了确保三通道对齐解码模块的输出统一寄存一拍并且把行场同步信号也延迟相同拍数。这个对齐操作在处理视频信号时无处不在——你想在图像上叠加一个字符就要精确知道当前像素坐标同步信号一旦偏了整个画面就平移甚至翻滚。黑金提供的参考设计里有一个sync_align模块专门干这个事我建议读者不要跳过它直接使用是理解环路实验最快的方式。解码后的数据格式每个像素24位R[7:0]、G[7:0]、B[7:0]加上行有效信号hsync、场有效信号vsync、数据有效信号deData Enable高电平表示当前处于有效像素区域。这三个信号就是后续一切处理模块的控制基础。4.4 环路输出端输出像素拼接与TMDS编码输出端是输入端口的镜像操作但多了几个细节。首先是像素时钟的选择——如果环路不做任何处理可以直接用输入恢复时钟做输出时钟这样输出和输入像素是同步的不需要FIFO。本实验为了演示后续扩展路径采用了异步方案输出时钟独立于输入时钟用异步FIFO隔离。这种方案的代价是输出帧率和输入帧率可能有微小差异比如输入是59.94Hz输出PLL锁到60HzFIFO会周期性地出现下溢或上溢解决办法是监测FIFO水位在消隐期间丢弃或重复一行。然后是TMDS编码模块。输入侧解码是10位转8位输出侧编码是8位转10位。在有效数据区我们对每个通道的8位像素数据进行编码在消隐区不传像素数据而是用2位控制信号对应HSYNC、VSYNC编码成10位控制字。整个编码过程可以用两个查找表配合一个累计状态寄存器实现。最后是OSERDESE2并串转换。每个输出通道需要一个宽度为10的OSERDESE2在5倍频时钟的上升沿和下降沿各发送一位。数据要在像素时钟域下准备好由OSERDESE2内部完成时钟域切换。这里容易犯的错误是直接用组合逻辑把10位并行数据接到OSERDESE2的输入导致并行数据的时序不满足。正确做法是先用寄存器打一拍确保数据稳定后再进入串行器。4.5 I2C配置DDC、EDID与HDCP绕过HDMI输出到显示器时显示设备会通过DDC通道本质是I2C总线把自己的EDID信息告诉源端——包括支持的分辨率、刷新率、色彩空间等。标准做法是FPGA作为中间设备要像显示器一样向源端返回EDID同时要像源端一样去读取下游显示器的EDID。这个“EDID代理”功能通过I2C控制器实现。在这个实验里我直接用黑金参考设计提供的I2C模块它负责两个任务一是存储并提供上游设备需要的EDID通常从板载EEPROM读取二是把下游显示器的EDID原样复制给上游。如果不做EDID代理源端可能不会输出信号或者输出一个默认分辨率往往是640x480达不到1080p60的效果。另外提一句HDCP高清数字内容保护。HDMI规范里如果源端设备检测到下游需要HDCP加密比如播放蓝光碟片而我们的FPGA链路不支持HDCP源端会拒绝输出。这在实验里无解——不是逻辑问题是版权保护机制。所以调试时请使用普通PC桌面、测试图案这类不受HDCP保护的信号源。5. 常见问题与排查技巧实录5.1 屏幕无显示从HDMI握手开始查这是环路实验里出现频率最高的问题可能原因非常多必须按链路顺序排查。第一步查供电用万用表确认HDMI输出口的5V有没有第二步查DDC通道用I2C逻辑分析仪抓一下EDID读取是否正常第三步查TMDS时钟用示波器或频谱分析仪看输入端有没有148.5MHz的时钟信号第四步查FPGA内部用ILA看ISERDESE2输出数据是否稳定翻转。我遇到过最诡异的情况是电脑识别到了显示器说明EDID和DDC都正常但屏幕就是黑的。最后定位到问题时输出侧OBUFDS的电平标准配错了配成了LVDS——信号完全不符合HDMI电平要求显示器端虽然“认识”这个设备但收不到有效信号。换成TMDS_33后立即恢复。5.2 图像偏色、彩色噪点编码表或者通道错位如果画面能显示但颜色明显不对大概率是TMDS解码或编码的通道映射问题。HDMI的Data0对应蓝色通道Data1对应绿色Data2对应红色不少初学者把通道搞混结果就是画面偏黄偏紫。另外检查RGB位序——有些设计是大端在前有些是小端在前统一按照黑金参考设计的数据定义不要自己乱改。还有一种可能性输入数据没有对齐到10位边界。TMDS的串行数据流是逐位传输的接收端必须找到10位数据的起始位置。通常HDMI通过控制周期每两行/两帧之间传输的特殊控制字序列来提供对齐参考。如果检位逻辑没写好接收到的高位和低位就会错位表现就是画面出现斜向条纹而且半天跳动一次。这个非常难查建议直接复用参考设计里的对齐状态机不要自己造轮子。5.3 时钟相关花屏、滚动条和间歇性黑屏花屏画面有水平噪点带和滚动条画面缓慢上下滚动都是时钟问题的典型表现。花屏一般意味着像素时钟抖动太大或者采样相位不对滚动条则说明输出和输入的帧率有微小差异FIFO周期性地空/满画面内容在时间轴上被压缩或拉伸。滚动条的解决办法是前面提到的FIFO水位控制在消隐期间检测FIFO水位低于阈值则重复发送一行数据高于阈值则丢弃一行数据。这个操作对画面影响非常小人类感知不到丢一行但能彻底消除滚动条。我自己实现时用了5行的FIFO阈值设在2行为下界、4行为上界效果稳定。间歇性黑屏则多与时钟相位漂移有关。如果输入端IDELAY值设在了数据窗口边缘温度变化或线缆微微移动都会导致采样失败从而黑屏恢复后又正常。这个问题我在第4.2节提到的眼图扫描自校准方案下基本能解决。如果你的板子没有自校准资源建议把延迟值设在眼图中心靠左约1/3的位置留足余量。5.4 排查工具推荐与Debug思维调试此类实验的工具优先级。最高优先级是逻辑分析仪ILA在Vivado里非常方便用来观察内部信号其次是示波器至少500MHz带宽1GHz更佳用来观察TMDS波形和时钟再次是HDMI协议分析仪专业工具但不便宜可以用逻辑分析仪加差分手持模式替代最后才是盲调——即不断改代码上板试这是效率最低的方式。我还强烈建议写几个纯数字的测试Pattern。比如在环路通道里强行插入一个纯红图案如果输出显示纯红说明数据通路正常如果出现了干扰条纹说明时序或者对齐还有问题。这种“定向诊断”比漫无目的地观察真实视频信号要高效得多。6. 实验扩展思路与收尾建议这个实验做完你掌握的其实是一整套“FPGA处理外部高速串行视频数据”的框架。扩展方向非常多。最简单的改法是把RGB数据经过一个亮度调整模块就能看到图像变亮或变暗进一步可以接入灰度转换、边缘检测Sobel算子、色彩空间转换RGB转YCbCr等经典图像算法这些算法在FPGA上的实现思路和这个实验里的数据流完全兼容。如果想挑战高难度可以做缩放器Scaling或者画中画PiP核心在于行/帧缓冲的管理需要用到DDR3或者BRAM的大容量存储。再往上就涉及MIPI CSI/DSI、DisplayPort、HDMI 2.0这些更高速的接口但你会发现底层思路都是一样的——无非是SerDes、通道对齐、编码解码、时钟恢复、缓存管理。最后分享一个个人习惯每接到一个视频相关项目我第一件事不是写逻辑而是画数据流图。把输入信号从引脚到输出的每一步、每个模块的位宽和时钟域标得清清楚楚再开始写代码。这个实验看起来简单一个环路而已但它的数据流链路其实横跨了三个时钟域、四种格式串行、并行字、解码像素、再编码串行稍不留神就是时序地狱。把这一步做扎实后面做任何图像处理项目你都心里有底。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进