尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

FPGA实现HDMI环回测试:TMDS收发与跨时钟域设计实战

FPGA实现HDMI环回测试:TMDS收发与跨时钟域设计实战 简介本资源是基于Xilinx XC7A200T FPGA的HDMI输入输出环回测试完整工程面向FPGA初学者与数字接口开发工程师解决高速音视频信号在可编程逻辑中实时采集、无损转发与协议合规验证的核心问题适用于多媒体设备兼容性测试、HDMI IP核学习及嵌入式视频处理系统原型开发。压缩包共120个文件涵盖10个Verilog源码.v、2个约束文件.xdc、5个设计检查点.dcp、12个实现报告.rpt、18个综合/实现日志.log/.jou及bit流文件hdmi_loopback.bit等关键产出全面覆盖从RTL设计、时序约束、综合布局布线到硬件烧录的全流程包体仅1.64MB轻量但结构完整。已有160人学习下载提供开箱即用的环回验证方案含可直接加载的bit文件、自动化脚本runme.bat、物理优化后网表及WebTalk统计报告便于快速上手、比对时序收敛结果并定位TMDS同步与解码常见问题。 最近在调一块XC7A200T的板子顺手把HDMI输入输出环回测试这个小项目完整做了一遍。这个项目标题里虽然挂着“环回测试”四个字听起来像个验证小工具但实际把它跑通涉及到的知识点相当密集TMDS协议、ISERDES/OSERDES原语、EDID模拟、跨时钟域处理、管脚约束随便拎出来一个都能让新手折腾半天。这篇文章我把整个项目的设计思路、核心逻辑、代码结构、调试踩坑过程一次性讲清楚希望能帮到正在做FPGA视频接口开发的朋友。先说这个项目能干什么用XC7A200T作为核心芯片板卡上同时具备HDMI输入接口和HDMI输出接口外部视频源比如电脑、播放器通过HDMI线把信号送进FPGAFPGA完成接收、解码、再编码最后从HDMI输出接口把画面环回发送出去。如果连上显示器能看到与源端一致且稳定的画面说明整条视频链路的物理层、链路层、协议层都工作正常。这个工程同时也是一个非常好的FPGA视频通路入门模板适合那些想做图像采集、视频拼接、格式转换、显示控制的朋友作为起点。1. 项目整体设计与方案选型1.1 环回测试到底在测什么环回测试Loopback Test在通信领域是非常经典的自检手段原理很简单把信号送出去再从接收端收回来对比收发数据是否一致从而验证链路完整性。在HDMI接口上做环回本质上是验证三件事物理层能否正确接收高速串行信号、逻辑层能否完成解串和编码恢复、以及发送端能否在FPGA内部逻辑再造出符合HDMI规范的信号。但别小看这条“从RX进、从TX出”的链路。HDMI输入信号是3条数据通道加1条时钟通道的TMDS差分信号1080p60格式下像素时钟是148.5MHz单条数据通道的串行速率是1.485Gbps。FPGA要先把这种高速串行数据流接收下来用硬件原语解串成并行数据再完成通道对齐、像素恢复最后把像素数据重新编码后串化输出。这中间任何一个环节出问题体现在显示器上就是黑屏、花屏、偏色、闪屏甚至是完全无信号。这个项目的意义在于它不依赖厂商提供的现成IP核比如Xilinx的Video PHY Controller而是用Verilog HDL结合FPGA原语自己搭一条收发通路。这种方式对理解HDMI协议本身、以及FPGA高速接口设计的基本方法都很有帮助。1.2 为什么选XC7A200T这颗芯片XC7A200T是Xilinx Artix-7系列里的中高端型号属于28nm工艺的7系列FPGA。选择它来做HDMI环回测试主要有几方面考虑。逻辑资源方面XC7A200T拥有大约20万个逻辑单元、740个IO引脚做完整的双HDMI收发通路毫无压力。虽然HDMI的接收解串过程会消耗不少逻辑资源用于通道对齐、FIFO缓冲和跨时钟域处理但这款芯片的容量足够宽松甚至可以在这个基础上再叠加帧缓存、图像缩放、OSD叠加等扩展功能。关键的是它内置了GTP高速收发器虽然在本项目中用不到GTP我们用普通IO加ISERDES/OSERDES实现TMDS收发但芯片提供的灵活IO能力非常关键。Artix-7的HPHigh PerformanceBank支持高达1.8V的IO电平标准配合SelectIO资源中的ISERDESE2和OSERDESE2原语可以在无需外部PHY芯片的情况下直接对接HDMI的TMDS信号前提是外部做电平转换因为FPGA IO不能直接承受HDMI的3.3V电平。从成本和开发板普及度来看XC7A200T也是目前市面上能买到的高性价比视频处理FPGA之一。黑金、米联客、正点原子这些厂商都有对应的开发板双HDMI接口几乎是标配拿来跑这个项目非常合适。1.3 整体通路架构设计整个环回项目在FPGA内部可以划分成两条主线接收通路和发送通路。接收通路负责从HDMI RX接口恢复出像素数据发送通路负责把像素数据重新编码输出。接收链路从HDMI座子进来后首先经过板上的电平转换和ESD保护电路进入FPGA的差分输入引脚。时钟通道进入后需要经过时钟管理模块MMCM/PLL进行去抖和相位调整恢复出干净的像素时钟。三路数据通道分别通过ISERDESE2进行1:7的解串得到7位并行数据再经过解码器把TMDS编码恢复成8位像素数据和2位控制信号。发送链路则正好相反像素数据经过TMDS编码器变成10位数据通过OSERDESE2完成7:1串化实际是10:1用OSERDESE2的10:1模式最后经OBUFDS差分输出缓冲器发送到HDMI座子。这里要注意一个关键设计选择接收和发送的像素时钟是什么关系。最简单的环回方式是把接收恢复的像素时钟直接作为发送时钟这样源端和显示端的像素时钟完全同步不需要做跨时钟域处理。但在实际项目中这种方案有局限性因为外部信号源的时钟可能存在抖动或漂移直接用于发送端会导致输出信号质量不稳定。更稳妥的做法是用一个独立的干净像素时钟作为发送时钟在接收和发送之间加入异步FIFO做缓冲。我这次做的设计采用了后者这也是后面调试时踩过坑的地方后面细说。2. HDMI接口协议与FPGA侧的物理层设计2.1 HDMI与DVI协议的关系要理解HDMI环回先要清楚HDMI和DVI的关系。DVI是数字视频接口HDMI是在DVI基础上发展而来的两者在视频传输部分完全兼容。视频信号都是通过TMDSTransition Minimized Differential Signaling过渡最小化差分信号技术传输每通道8位像素数据经过编码后变成10位然后串行发送。区别在于HDMI在视频信号之外还增加了音频数据通道、CEC消费电子控制通道、以及热插拔检测等附加功能。在纯视频环回的场景下我们只需要处理与DVI兼容的那部分内容也就是3条TMDS数据通道加1条TMDS时钟通道。这就是为什么很多FPGA开发的HDMI项目其实是在做DVI协议只是在物理接口上使用了HDMI座子。这个点很重要因为如果你去查HDMI官方规范会发现内容非常庞杂但实际做视频环回核心只需要处理视频数据区DE有效期间像素数据、消隐区DE无效期间控制信号这两个状态。不需要处理音频帧、InfoFrame数据岛这些HDMI扩展内容。2.2 TMDS通道结构与解串/串化TMDS链路在物理上由3对差分数据通道和1对差分时钟通道组成另外还有DDCDisplay Data Channel通道用于读取显示设备的EDID信息总共5组差分信号。每条数据通道的编码规则是在DE有效期间8位像素数据经过TMDS编码器转换成10位数据在DE无效期间传输2位控制信号这2位控制信号被编码成固定的10位序列。接收端判断当前是视频数据还是控制信号就看前2位控制信号这两位的组合有4种状态分别编码为4种固定的10位序列。在FPGA内部解串的核心是利用SelectIO资源中的ISERDESE2原语。ISERDESE2支持DDR模式下的1:6、1:7、1:8解串我们在1080p下使用1:7模式因为TMDS编码后每条通道的有效数据宽度正好是7位实际上每像素周期每通道传输10位但通过双沿采样拆分在内部用10位宽度处理。还是以1080p60为例像素时钟是148.5MHzTMDS串行速率是1.485Gbps。ISERDESE2在DDR模式下每个时钟周期的上升沿和下降沿各采一个数据所以内部并行数据宽度是148.5MHz×2297M样点/秒采集14个样点不对这里需要仔细算一下。准确来说ISERDESE2在1:7模式下每个像素时钟周期内完成7位数据的解串。由于使用DDR双沿采样7位数据分布在3.5个时钟周期内。但在实际工程中我们通常会用一个数据宽度转换逻辑把1:7的并行数据拼接成10位数据。也就是说物理层解串只负责把串行数据流变成并行数据真正的协议对齐找到10位TMDS编码字边界是在逻辑层实现的。发送端同样使用OSERDESE2原语把10位并行数据以同样的速率串化输出。OSERDESE2支持1:10模式正好匹配TMDS的10位编码宽度。2.3 时钟恢复与跨时钟域处理方案时钟是整个HDMI环回项目中最重要的信号。接收端的TMDS时钟通道输入一个与源端像素时钟同频的时钟信号这个时钟直接决定了数据的采样时机。在XC7A200T上时钟信号进入FPGA后首先通过IBUFDS把差分信号转成单端然后送入MMCM或PLL进行去抖和相移。从频率上看720p60的像素时钟是74.25MHz1080p60是148.5MHz这两个频率是TMDS的常见像素时钟。MMCM需要根据输入时钟频率配置对应的倍频/分频参数产生用于解串的比特时钟串行速率的2倍用于ISERDES的DDR采样以及用于逻辑处理的并行时钟。跨时钟域处理方面接收端的像素数据是在恢复时钟域而发送端如果需要使用独立的像素时钟就形成了两个异步时钟域。此时必须在中间加异步FIFO否则数据在半路丢失或产生亚稳态显示器上的表现就是不断闪屏、画面撕裂。我这次使用了Xilinx的FIFO Generator IP核配置为标准异步FIFO模式读写时钟分别来自接收恢复时钟和发送像素时钟数据宽度32位一个像素时钟周期内同时存储4个像素的RGB分量深度512。这个深度对于1080p一行的数据量1920像素来说不够用但因为环回场景不涉及帧率转换FIFO只需要匹配两个时钟域之间很小的频率差和抖动不需要缓存完整的一帧所以512的深度已经足够。3. Verilog实现与关键模块详解3.1 模块划分及代码结构整个工程的顶层模块名我命名为hdmi_loopback_top内部划分了以下几个子模块hdmi_rx_phy接收物理层模块包含差分输入缓冲、ISERDESE2例化、时钟管理hdmi_rx_align通道对齐模块负责将三路解串数据按TMDS编码字边界对齐hdmi_decoderTMDS解码模块将10位编码数据还原为8位像素数据和控制信号ddc_edidEDID模块通过I2C接口模拟一个EDID存储区响应外部源端的读取请求async_fifo异步FIFO实例用于跨时钟域数据缓冲hdmi_encoderTMDS编码模块将8位像素数据编码为10位hdmi_tx_phy发送物理层模块包含OSERDESE2例化、差分输出缓冲顶层模块负责将各子模块连接起来形成完整的环回数据通路。在代码结构上我采用了类似AXI总线的接口风格数据通路使用valid/ready握手信号便于后续扩展图像处理模块。EDA工具使用的是Vivado 2020.2工程设置中选择了xc7a200tfbg676-2这个具体型号。这里需要特别注意不同封装和速度等级会影响时序收敛结果同一个工程换到-1速度等级的芯片上很可能出现时序违例。3.2 关键代码实现ISERDESE2接收与OSERDESE2发送ISERDESE2的例化是这个项目中最关键的部分。在1:7模式下每个数据通道需要例化一个ISERDESE2。以下是接收通道的核心代码片段ISERDESE2 #( .DATA_RATE(DDR), .DATA_WIDTH(7), .INTERFACE_TYPE(NETWORKING), .DYN_CLKDIV_INV_EN(FALSE), .DYN_CLK_INV_EN(FALSE), .NUM_CE(1) ) iserdese2_data0 ( .O(), .Q1(data_q1), .Q2(data_q2), .Q3(data_q3), .Q4(data_q4), .Q5(data_q5), .Q6(data_q6), .Q7(data_q7), .Q8(), .SHIFTOUT1(), .SHIFTOUT2(), .BITSLIP(bitslip_data0), .CE1(1b1), .CE2(1b1), .CLK(bit_clock), .CLKB(~bit_clock), .CLKDIV(pixel_clock), .CLKDIVP(), .D(rx_data_p), .DDLY(), .RST(rst_n), .SHIFTIN1(1b0), .SHIFTIN2(1b0) );这里需要注意几个关键信号。BITSLIP是通道对齐的关键信号它的作用是让ISERDESE2内部采样窗口移动一位用于对齐10位编码字的边界。CLK和CLKB分别接位时钟和反相位时钟CLKDIV接像素时钟。数据宽度为7位时ISERDESE2输出Q1-Q7共7位并行数据。为了还原完整的10位TMDS编码字需要跨时钟周期拼接。具体的做法是每当收到一个使能脉冲就将当前周期的Q1-Q7与上一周期保留的3位数据拼接成10位。这个逻辑在通道对齐模块中实现。发送端OSERDESE2的例化相对简单使用10:1模式把10位并行数据串化后输出。关键代码如下OSERDESE2 #( .DATA_RATE_OQ(DDR), .DATA_RATE_TQ(BUF), .DATA_WIDTH(10), .SERDES_MODE(MASTER) ) oserdese2_tx ( .OQ(tx_data_serial), .OFB(), .TQ(), .TBYTEOUT(), .TBYTEIN(1b0), .OCE(1b1), .RST(~rst_n), .TCE(1b0), .CLK(bit_clock_tx), .CLKDIV(pixel_clock_tx), .D1(tx_data_10b[0]), .D2(tx_data_10b[1]), .D3(tx_data_10b[2]), .D4(tx_data_10b[3]), .D5(tx_data_10b[4]), .D6(tx_data_10b[5]), .D7(tx_data_10b[6]), .D8(tx_data_10b[7]), .D9(tx_data_10b[8]), .D10(tx_data_10b[9]), .T1(1b0), .T2(1b0), .T3(1b0), .T4(1b0) );发送端的位时钟和像素时钟分别由MMCM产生。输入参考时钟来自板上晶振125MHzMMCM配置为产生148.5MHz像素时钟和742.5MHz位时钟148.5MHz×5因为10:1串化在DDR模式下需要5倍时钟。3.3 TMDS编码与解码逻辑TMDS解码器在收到10位编码数据后需要判断当前是视频数据还是控制信号。如果是控制信号直接根据10位码型映射出2位控制信号如果是视频数据则执行标准的TMDS解码恢复出8位原始数据。解码的关键是区分DE状态。在DVI/HDMI协议中DE为高期间传视频数据DE为低期间传控制信号。控制信号的码型是固定的通过检测特定码型就能分辨出当前处于什么状态。接收端解码时需要一个状态机来跟踪当前是否在视频数据区并在检测到特定控制字时切换状态。编码逻辑相对简单按照TMDS编码算法实现即可。核心是异或/同或编码、最小化跳变、直流平衡三个步骤。这里不展开算法细节但需要注意的是编码器的输入必须和输出之间有一个时钟的流水线延迟对齐到像素时钟和行场同步信号。3.4 管脚约束与关键时序约束约束文件是HDMI项目最容易出错的地方之一。首先是管脚位置约束需要根据开发板的原理图把HDMI座子对应的FPGA引脚一一对应。# HDMI RX 时钟通道 set_property PACKAGE_PIN AD19 [get_ports hdmi_rx_clk_p] set_property IOSTANDARD LVDS_25 [get_ports hdmi_rx_clk_p] set_property PACKAGE_PIN AD18 [get_ports hdmi_rx_clk_n] set_property IOSTANDARD LVDS_25 [get_ports hdmi_rx_clk_n] # HDMI TX 数据通道0 set_property PACKAGE_PIN W19 [get_ports hdmi_tx_d0_p] set_property IOSTANDARD LVDS_25 [get_ports hdmi_tx_d0_p] set_property PACKAGE_PIN W18 [get_ports hdmi_tx_d0_n] set_property IOSTANDARD LVDS_25 [get_ports hdmi_tx_d0_n]这里有一个重要前提FPGA的IO电平标准必须是LVDS_25而不能直接使用TMDS标准因为XC7A200T的HP Bank并不支持TMDS电平标准。板上需要外加电平转换电路将FPGA的LVDS信号转换成HDMI座子需要的TMDS信号。大多数开发板已经集成这级电路所以代码里直接用LVDS_25约束即可。时序约束方面关键的是为TMDS时钟创建输入时钟约束并为位时钟和像素时钟建立时钟分组关系。create_clock -name tmds_clk -period 6.734 [get_ports hdmi_rx_clk_p] set_clock_groups -asynchronous -group [get_clocks tmds_clk] -group [get_clocks tx_pixel_clk]这里6.734ns对应当前测试用的148.5MHz像素时钟如果测试720p信号需要改为13.468ns。实际调试时我们做了一个vip_clk_select引脚用于在74.25MHz和148.5MHz之间切换但初版设计还是固定1080p避免变量太多。4. 调试实录从黑屏到稳定图像的避坑过程4.1 黑屏问题EDID不响应导致源端不输出第一次上板测试时我把HDMI线从电脑连到FPGA开发板再从开发板连到显示器结果显示器完全无信号。用逻辑分析仪抓HDMI DDC通道I2C总线发现源端在反复发送EDID读取请求但总线上完全没有应答。这个问题的根因是FPGA开发板虽然带了HDMI输入接口但板上的I2C总线没有挂接EEPROM存储EDID数据而FPGA内部的EDID逻辑也没有正确实现I2C从机协议。电脑的显卡检测不到显示设备的EDID就默认不输出视频信号。解决办法是在FPGA内部实现一个I2C从机模块模拟EDID存储区。核心逻辑是监听I2C总线上的地址匹配0xA0收到读指令后从内部ROM中读取128字节EDID数据返回。这里建议先使用一个简化的EDID数据块不包含扩展块只声明基础分辨率1080p60和厂商信息。显卡在识别到有效EDID后会自动切换到匹配的分辨率输出。4.2 通道对齐失败导致整屏雪花解决了EDID问题后源端开始输出视频信号但显示器上看到的是满屏的雪花噪点完全无法识别图像。用ILAIntegrated Logic Analyzer抓取解串后的数据发现三路数据通道的并行数据完全随机显然通道对齐没有生效。TMDS数据通道对齐的原理是在消隐期间每条数据通道会传输固定的控制信号码型。比如通道0在消隐期间传输HSYNC和VSYNC信号这两个信号被编码成固定的10位序列。接收端通过检测这个固定码型可以确定当前采样窗口是否对准了编码字边界。我的通道对齐模块没有做状态机而是直接使用简单的“盲搜”方式连续检测若干个像素时钟周期内是否出现固定的控制字码型如果没有就拉高BITSLIP信号让ISERDESE2移动一位采样窗口继续检测。调试中发现由于每个通道需要独立对齐而3个通道之间又会互相影响单纯轮流对齐的方式效率很低容易陷入死循环。最终解决方法是先完全释放所有通道的BITSLIP控制只对齐通道0并锁定然后以通道0为基准逐一对齐通道1和通道2。对齐完成后加上一个“对齐锁定”状态不再允许BITSLIP变化避免运行过程中误触发重新对齐。4.3 跨时钟域导致的花屏在解决了通道对齐后显示器上能看出画面内容了但会出现周期性花屏。不是全黑也不是全花而是画面每隔几百毫秒就撕裂一次像是有规律的干扰。这个问题的根源在跨时钟域处理。初版设计里接收恢复时钟和发送像素时钟用的是同一个MMCM输出的时钟理论上是同步的但实际中接收恢复时钟来自外部源端经过MMCM后虽然频率锁定但相位关系完全是任意的。TX和RX之间的数据通路存在亚稳态风险导致偶发数据错误。修复方案就是在RX和TX之间插入异步FIFO。这里踩过一个坑异步FIFO的读写时钟频率即使标称相同比如都配置成148.5MHz两个独立振荡源产生的时钟也不可能完全同频必然存在微小频差。如果FIFO深度不够运行时间久了就会溢出或读空表现为周期性花屏。我最终把FIFO深度设为512并且在FIFO内部加入了水印管理逻辑当FIFO水位低于128时插入一个垂直消隐期的等待让写入端追赶当水位高于384时跳过一行的数据让读出端追赶。这样在长时间运行中FIFO始终稳定在中间水位附近不再出现溢出或者读空。4.4 偏色问题与数据位序最后一次调试遇到的现象是画面稳定但颜色明显不对红色和蓝色互换了绿色正常。这种问题通常和数据位序有关。RGB三个通道的映射关系是TMDS通道0传送蓝色分量通道1传送绿色分量通道2传送红色分量。如果代码里把通道0误接到了R通道通道2接到了B通道就会出现红蓝互换。检查后发现是顶层模块信号连接时把rx_ch0_r和rx_ch2_r接反了修正后颜色恢复正常。另外一个容易忽略的点是每个颜色通道内部还有8位数据的位序问题。如果TMDS解码器输出的bit0对应原始数据的bit7那颜色的低位和高位就会颠倒表现为颜色整体偏色且带有明显的“色带效应”。这个可以通过查看ILA抓到的数据码型来判断正常白色像素RGB255下解码后三通道数据应该全部为8hFF。5. 常见问题速查表与排查思路调试HDMI环回很多问题都是阶段性的按链路顺序排查会快得多。下面这张表是我把常见问题按“链路位置”分类整理成的速查表遇到问题可以先对号入座。现象可能原因排查方向显示器完全无信号源端未检测到设备用逻辑分析仪抓I2C总线检查EDID是否正常应答源端输出但显示器黑屏TMDS时钟通道异常检查MMCM是否锁定ILA抓像素时钟是否稳定画面满屏雪花通道未对齐检查BITSLIP状态机先单通道对齐再全部锁定画面有规律撕裂FIFO溢出或读空观察FIFO水位加大深度或加水印管理红蓝互换通道映射错误核对顶层连接检查通道0/1/2与RGB的映射颜色整体偏色且有色带并行数据位序颠倒检查解码输出的bit顺序确保MSB/LSB对应正确运行一段时间后画面闪动TX时钟相位漂移检查MMCM输出时钟质量必要时加入BUFG保证时钟树平衡热插拔后不显示EDID模块热插拔检测引脚未处理确认HPDHot Plug Detect引脚是否正确驱动排查建议接到一个HDMI环回项目先不要急着上1080p先用720p6074.25MHz像素时钟跑通链路因为频率低了接近一半布线和时序约束压力都小很多。等720p完全稳定后再切换到1080p验证极限性能。这个过程能帮你把“频率相关”和“逻辑错误”这两类问题区分开。另一个实用技巧是在调试时把发送像素时钟固定为板载晶振产生的干净时钟不要直接使用接收恢复时钟。这样即使接收端信号质量再差发送端也始终输出稳定时钟显示器不会因为源端信号抖动而反复黑屏。6. 后续扩展方向环回测试跑通后这个工程就是一个现成的视频通路平台可以往上面叠加各种功能。最常见的是加一个帧缓冲模块用DDR3存储视频帧实现输入输出帧率异步、画面定格、分屏显示等功能。XC7A200T配合板载DDR3做1080p60的视频帧缓存非常富裕。第二个方向是图像处理。在异步FIFO和TX编码器之间插入图像处理模块比如灰度转换、边缘检测、色度空间转换。由于视频流天然是像素流水线结构这些算法在FPGA上的实现效率远高于处理器。第三个方向是加OSDOn-Screen Display菜单叠加。把字符或者简单图形的像素数据缓存在BRAM中在进TX编码器前根据坐标信息与视频数据进行叠加就能实现类似显示器设置菜单的效果。第四个方向是接口扩展。把HDMI RX替换成MIPI CSI-2接收或者SDI接收TX端保留HDMI输出就能做成一个简易的相机图像显示设备。这类项目在校验摄像头模组时非常实用。我也把这次工程中的代码和约束整理成了一个相对完整的基础模板后续接新板卡时只需要修改引脚约束、EDID数据和时钟配置就能快速搭建出一条可用的HDMI通路。如果你也在做类似方向建议从720p开始、一步一步来先让RX链路工作再接TX链路最后再联调环回。这样遇到问题至少能知道自己改的是哪一段代码。本文还有配套的精品资源点击获取
返回列表