FPGA实现HDMI收发:从TMDS解码到发送编码全流程解析

发布时间:2026/10/20 7:25:35
FPGA实现HDMI收发:从TMDS解码到发送编码全流程解析 这块板子在我工作台上躺了快两周终于把计划里的第二阶段调通了。上一篇文章写完了基础环境的搭建和方案选型当时给自己留了两个目标一是把外部输入的HDMI信号完整接收并解码成并行RGB数据二是自己生成视频时序从FPGA侧发出去。这篇文章就聚焦这两个目标的实际落地过程把接收端和发送端的核心代码思路、时序约束、调试踩坑全部分享出来。文中涉及的具体引脚分配和工程路径我会用虚构代号代替但实现方法、参数计算和排查思路都是可以直接照搬到自己的板子上的。1. 整体方案回顾与核心选型思路这次实验用的是一块入门级FPGA开发板主芯片没有内置高速收发器SerDes所以HDMI接收和发送都不能走那种“把TMDS差分对直接怼进GTX/GTP”的路线。选择的是IO引脚外部PHY芯片的方式HDMI接口先接到一块兼容TMDS电平的物理层芯片上由它完成电平转换和阻抗匹配再以单端信号的形式进入FPGA的普通Bank。这样做的优点是选型自由度大、手里这块芯片也能扛住1080p60的像素时钟148.5MHz缺点是IO时序裕量比原生SerDes方案紧张布线布局多花了不少工夫。接收端和发送端共用的关键资源是一套随路时钟处理电路。HDMI的TMDS协议里每个通道带一对差分时钟这个时钟频率等于像素频率数据通道上的10bit在每个时钟周期内被串行移位输出等价于源端发送的是时钟频率10倍的比特流。所以接收时首先要解决两个问题位同步和字同步。位同步依靠锁相环把恢复出来的随路时钟倍频到像素时钟的10倍然后用这个高速时钟去捕捉数据引脚上的电平变化字同步则是找到每个10bit符号的边界因为TMDS编码中规定了一些特殊的控制字符初始帧的Data EnableDE拉高之前会有一段特定的控制序列用来校准边界。发送端相对直接内部生成RGB数据和对应的DE、HSYNC、VSYNC信号经过TMDS编码后由IO引脚并行输出。编码后的10bit信号再经过硬件PHY芯片内部的并转串和差分驱动最终送到HDMI座子。这里有个容易忽略的细节PHY芯片的输入时钟相位要与FPGA输出的并行数据对齐否则采到毛刺就把编码内容破坏掉了。这个后面在第三节细说。整体方案的选型结论摆在前面如果你手里也是没有高速收发器的板子这套“FPGA外部TMDS PHY”的思路是目前成本最低、最成熟的做法。它把最难的串行收发交给了专用芯片FPGA这边只负责协议逻辑调试难度降了一截适合拿来验证协议理解和基础功能。2. 接收链路深入解析从差分信号到并行像素2.1 时钟恢复这一步绕不开的锁相环设计接收端最核心的模块是PLL/MMCM配置。HDMI源端送出的像素时钟是随数据一起的频率跟当前视频模式严格对应不会自己漂。以常见的1080p60为例像素时钟148.5MHz此时TMDS时钟就是148.5MHz数据率是1485Mbps每通道。FPGA内部PLL需要把随路时钟倍频到1.485GHz作为采样时钟。这里有个实践取舍直接倍频到10倍频还是先用快速时钟采再用逻辑分相在普通IO Bank上直接生成1.485GHz的时钟是不现实的一般PLL输出上限也就几百兆到1GHz出头还得留裕量。我实际采用的做法是倍频到5倍频742.5MHz用DDR模式上下沿都采样等效出10倍采样效果。这样把时钟频率控制在器件允许范围内代价是采样逻辑要额外处理上下沿数据的拼接。PLL配置时需要注意时钟源的选择。随路时钟进入PLL之前最好经过一个全局时钟引脚MRCC/SRCC保证时钟质量。如果PHY芯片输出的恢复时钟质量一般示波器看可能边缘有抖动PLL带宽要适当调低一点让它滤掉高频抖动不要盲目追求锁定速度。带宽太高的后果是输出时钟会跟着输入抖动跑危害后级的数据采样。// 接收端PLL配置示意基于Vivado MMCM原语 wire clk_tmds; // 来自PHY芯片的像素时钟 wire clk_pix; // 恢复出来的像素时钟域 wire clk_s5x; // 5倍频采样时钟 wire clk_s5x_div2; // 分频后的辅助时钟可选 MMCME2_BASE #( .CLKIN1_PERIOD(6.734), // 148.5MHz .CLKFBOUT_MULT_F(10.0), // VCO 148.5 * 10 1485MHz .CLKOUT0_DIVIDE_F(10.0), // clk_pix 148.5MHz .CLKOUT1_DIVIDE(2.0) // clk_s5x 742.5MHz ) mmcm_pix_rec ( .CLKOUT0(clk_pix), .CLKOUT1(clk_s5x), .CLKFBIN(clkfb), .CLKOUT0B(), .CLKOUT1B(), .CLKOUT2(), .CLKOUT3(), .CLKOUT4(), .CLKOUT5(), .CLKOUT6(), .CLKFBOUT(clkfb), .LOCKED(pll_locked), .CLKIN1(clk_tmds), .CLKIN2(1b0), .CLKINSEL(1b1), .DCLK(1b0), .PWRDWN(1b0), .RST(!rst_n) );PLL锁定后不是立刻就去采数据建议做一次“锁定稳定延时”等上几百个周期再放开复位。我见过不少次PLL锁定信号刚拉高就急着干活结果采到乱码的问题加个简单的延迟计数器就能消除。2.2 通道对齐与符号边界识别TMDS三通道在电气链路上从源端到接收端会引入不同的延迟到达FPGA引脚的时间有差异差几个ns很正常。这会导致三个通道看起来“错位”了如果不做校正画面上会表现为颜色信息串位、边缘出现彩色噪点。HDMI协议专门为这个准备了对齐机制在每个水平消隐期通道上会连续发送对齐控制序列。接收端要做的是检测这些控制字然后独立调整每个通道的采样相位使三个通道的符号边界落在同一拍。我这边没有采用协议里完整的训练流程只用了一个实用的简化做法用任意通道检测到控制字序列作为基准检测另外两个通道相对它的延迟差值用IODELAY输入延迟链做细调。每个通道延迟步进是固定值本设计约为78ps循环递增直到三个通道同时监控到控制字。这个步骤在调试时看起来很简单实际在FPGA上实现有个麻烦控制字的检测本身要在正确的符号边界上才能进行而边界还没对齐时怎么判断检测结果是否可信解决办法是允许“半字”错位也能检测出特征模式。设计对齐状态机时用三个状态的跳转失步-对准试探-锁定。在试探状态允许一定的误判容差连续命中多个周期才进入锁定允许个别周期错误但不立刻退出这样就兼顾了鲁棒性和误码容忍度。// 通道对齐状态机核心状态伪代码 localparam ALIGN_LOST 2d0; localparam ALIGN_TRYING 2d1; localparam ALIGN_LOCKED 2d2; localparam ALIGN_RESYNC 2d3; always (posedge clk_pix) begin if (!rst_n) state ALIGN_LOST; else begin case (state) ALIGN_LOST: if (ctrl_seq_hit) state ALIGN_TRYING; ALIGN_TRYING: begin if (hit_cnt 8) state ALIGN_LOCKED; else if (miss_cnt 4) state ALIGN_LOST; end ALIGN_LOCKED: begin if (miss_cnt 16) state ALIGN_RESYNC; end ALIGN_RESYNC: begin // 重新扫描IODELAY配置按通道号逐个重调 state ALIGN_LOST; end endcase end end在锁定状态下最好还保留一个“滑动窗口检测”逻辑实时统计控制字命中率。如果发现命中率下降说明链路劣化或热插拔导致信号质量变化可以主动触发重新对齐。实际测试中用一根质量较差的长HDMI线时这个主动重对齐机制挽回了不少画面抖动问题。2.3 TMDS解码器实现细节TMDS解码在概念上很直接把10bit符号还原成8bit数据和2bit控制信号。但在FPGA实现时如果直接照抄标准里的解码表综合出来的面积和时序都不太好看。更常见的做法是用查找表LUT实现解码ROM把10bit作为地址、12bitDE控制位8bit数据作为输出。一个通道对应一个双端口BRAM或一组LUT面积可控且时序容易收敛。如果你想把解码逻辑吃透建议对照标准里的DC平衡解码、异或解码规则逐行理解看懂了能排查很多编码器侧的问题。但工程交付时用LUT方案最稳综合工具会把它映射到分布式RAM不占用太多全局逻辑资源。解码后得到的是RGB三个通道的并行像素以及DE、HSYNC、VSYNC。这里要做一步关键的时钟域处理把H/V同步信号同步到像素时钟域。因为HSYNC/VSYNC本身和像素时钟是同步的TMDS解码后天然就在像素时钟域只需要打两拍做亚稳态处理。真正需要留意的是DE信号不能乱打拍它跟像素数据严格对齐打拍会导致花屏错位。DE的处理方式是跟数据走同一级流水线延迟周期数保持一致。// 三个通道解码后数据拼接示意 reg [7:0] r_red_d1, r_green_d1, r_blue_d1; reg r_de_d1, r_hsync_d1, r_vsync_d1; always (posedge clk_pix) begin r_red_d1 rgb_r_from_decode; r_green_d1 rgb_g_from_decode; r_blue_d1 rgb_b_from_decode; r_de_d1 de_from_decode; // 与数据同拍延迟 r_hsync_d1 hsync_from_decode; r_vsync_d1 vsync_from_decode; end接收端真正重要的产物不只是解码后的RGB还有像素时钟域的活动图像窗口信息。你在实际项目里很可能要检测输入的分辨率模式从HSYNC/VSYNC周期数判断是720p还是1080p或者更奇怪的EDID协商出来的非标准分辨率。这个检测逻辑建议做成独立模块统计vsync有效沿之间的clk_pix周期数同时记录每行的像素长度。这样做一方面自动适配输入源另一方面在后续做图像缩放或叠加时只需要读这个模块的寄存器。3. 发送链路设计要点从并行像素到TMDS信号3.1 测试图像生成模块发送端最省事的是先做一个彩条发生器省得接外部图像源直接验证输出通路。彩条信号的特点是每种颜色都是恒定电平对编码逻辑的压力最小适合初级调试。等彩条通过了再换渐变或棋盘格图案验证高频内容下的信号完整性最后才外接真实的图像处理模块。其实彩条发生器也可以设计得稍微灵活点用一组参数控制行场消隐区和活跃区尺寸活动区内部用计数器做二维坐标判断输出8bit的R/G/B。比如“白-黄-青-绿-紫-红-蓝-黑”的标准彩条每种色条宽度是水平活跃区宽度的1/8高度铺满整个竖直区域。代码里用像素计数器和行计数器两个变量就能实现。// 彩条颜色切换示意按行像素位置划分8段 always (posedge clk_pix_fast) begin if (!de_in) begin {r_out, g_out, b_out} 24h000000; end else begin case (pixel_cnt[9:7]) // 取高位做分段每段1/8宽度 3d0: {r_out, g_out, b_out} 24hFFFFFF; // 白 3d1: {r_out, g_out, b_out} 24hFFFF00; // 黄 3d2: {r_out, g_out, b_out} 24h00FFFF; // 青 3d3: {r_out, g_out, b_out} 24h00FF00; // 绿 3d4: {r_out, g_out, b_out} 24hFF00FF; // 紫 3d5: {r_out, g_out, b_out} 24hFF0000; // 红 3d6: {r_out, g_out, b_out} 24h0000FF; // 蓝 3d7: {r_out, g_out, b_out} 24h000000; // 黑 endcase end end这里有个小讲究彩条生成器要跑在发送像素时钟域而不是接收恢复时钟域。两个时钟域如果频率一样但相位无关直接跨时钟域会出现亚稳态。实验里用同一个PLL的两个输出引脚分别给接收域和发送域相位关系是固定的但复位不同步也可能有半周期相位差。处理方式是在彩条模块的复位逻辑里用发送域时钟打拍同步避免异步复位释放问题。同步信号发生器放在彩条模块之前生成DE、HSYNC、VSYNC。这三个信号的时序参数按VESA规范填死写个本地参数表就行。重点是把行前肩、行同步宽度、行后肩的周期数算清楚总像素数要严格等于输入模式期望值比如1920x1080p60一行总共2200个像素时钟其中可见1920前肩88同步脉冲44后肩148。3.2 TMDS编码的运算细节TMDS编码是发送链路里唯一需要“算”的部分因为每个像素都要做8bit到10bit的变换。编码的核心逻辑分两段第一段把8bit数据根据“最小化传输差异”原则映射成9bit第二段根据已发送符号的直流平衡状态决定第10bit是取反还是保持。FM编码的判定条件看着复杂但用Verilog实现时其实就是若干个异或门和选择器不要被协议文档唬住。我直接把编码公式抄成组合逻辑用几个中间信号做复用综合工具能优化得很好。关键是关键路径要短因为10bit符号要在一个像素时钟内算完如果组合逻辑深度太大时序收敛会很痛苦。可以引入一级流水让编码延迟一个周期不算错误只要同步调整DE等信号的延迟即可。// TMDS编码关键计算以XOR映射与平衡决策示意 wire [7:0] d_in; // 8bit像素 wire de_in; // 数据有效 wire c0_in, c1_in; // 控制信号H/V同步 wire [8:0] q_m; // 9bit中间编码 // 第9位的XOR模式决定是否在位置n将位取反以保证跳变最少 // 平衡决策统计当前符号中1的个数与运行差异比较 wire balance_pos, balance_neg; assign balance_pos (q_m[8:4]) || (q_m[8:0] 9b111111110); assign balance_neg 1b0; // 反模式需要满足的条件 assign q_out[9] ~balance; // 第10bit翻转标志 assign q_out[8:0] balance ? ~q_m : q_m;实际编码时控制期的处理参考协议给的编码表。注意控制时段DE0期间的编码结果和像素数据编码完全不同编码器必须处理两种输入模式不能只用像素编码逻辑覆盖。初学者最容易漏掉的是DE拉低后编码器没有切到控制字符状态导致消隐期的信号不满足TMDS规范显示器会判定非法信号而拒绝显示。对于调试来说编码器本身不是故障高发区更多是编码时钟和输出时钟的相位关系没弄对。编码结果是10bit宽的并行数据要用某种方式转换成串行位流输出。由于没有高速收发器这里采用OSERDES DDR引脚输出组合10bit数据被拆成两个5bit半字节在时钟上升沿和下降沿分别输出。具体还是在工具原语层面做让工具自动插到IOB里。3.3 串行输出与引脚锁定的配合串行输出的核心是原语配置。Xilinx器件里OSERDESE2可以配置成10:1模式但某些型号原生只支持8:1超出部分要级联或者换思路。我的做法是把10bit拆成“5bit DDR输出”在IOB里做双沿采样等效出10倍时钟。这个做法要求IOB里串行器时钟必须是像素时钟的5倍频即5倍频时钟与并行数据同步。引脚分配时务必把三路数据通道和一路时钟通道的物理位置尽量靠近且走同一Bank的差分对区域。数据时钟相对位置要满足PHY芯片对输入建立保持时间的要求一般PHY芯片手册会给出skew容限超过就可能导致误码。我在第一版布局时为了布线方便把时钟通道放了很远结果上板测试经常出现偶发性花屏后来把时钟引脚挪到数据通道中间位置就好了。// OSERDES原语配置示意5:1双沿模式等效10:1 OSERDESE2 #( .DATA_RATE_OQ(DDR), // DDR模式上下沿都输出 .DATA_WIDTH(10), // 10:1串行化 .SERDES_MODE(MASTER), // 主模式 .TRISTATE_WIDTH(1) ) oserdes_ch0 ( .OQ(tmds_data_p), // 差分正端输出通过OBUFDS .CLK(clk_s5x), // 5倍频时钟 .CLKDIV(clk_pix_tx), // 像素时钟分频 .D1(dout_10b[8]), .D2(dout_10b[7]), .D3(dout_10b[6]), .D4(dout_10b[5]), .D5(dout_10b[4]), .D6(dout_10b[3]), .D7(dout_10b[2]), .D8(dout_10b[1]), .D9(dout_10b[0]), .D10(dout_10b[9]), // 注意D10与并行数据的对应关系 .T1(1b0), .T2(1b0), .T3(1b0), .T4(1b0) );这块容易出错的地方是并行数据位序与串行时序的对应关系。工具手册里的时序图显示D1~D10对应的输出顺序很多人想当然把bit9放D1、bit0放D10结果出来的图像左右颠倒或者完全花掉。正确的做法是参考具体器件手册的时序波型确认或者先用一根已知正确的HDMI线接到显示器上验证逻辑再调纯。我自己第一次也是按直觉接的折腾了半个下午才发现纯是位序问题。输出级时钟的选择也值得说一句像素时钟PLL产生5倍频时钟给OSERDES作为CLK像素时钟本身给并转串的CLKDIV。这两个时钟相位虽然是同源的但是经过全局时钟树后延迟不同必要时需要对着采样关系调一下。我的经验是整体都不动只调CLK和CLKDIV的相位偏移观察花屏情况选择最佳档位。4. 实测中踩过的坑时序收敛与信号完整性问题4.1 时钟域边界的处理接收和发送两个域在同一个工程里共存最危险的情况是接收端恢复时钟域的数据直接拿去发送域用。因为接收时钟和发送时钟虽然标称频率一致但分别由不同的源产生一个来自外部HDMI的随路时钟一个来自板载晶振。两者频率存在PPm级别的差异时间久了会产生FIFO溢出或欠载。实验里接收到的数据如果想回传给PC或显示器就必须做异步FIFO跨时钟域。FIFO深度至少预留两行像素用帧同步信号做指针复位。但更稳的做法是同步到帧边界在检测到VSYNC有效后对FIFO读写指针做一次校准避免长期积累的相位偏移。我这边第一版直接跳过FIFO想当然认为两边时钟都是148.5MHz就能直接对接结果上板后画面每几十秒钟就出现一次横向撕裂后来用逻辑分析仪抓FIFO空满标志才发现问题。加了一个深度1024的异步FIFO后该现象彻底消失。如果你的场景不需要回传而只是本地显示可以省略FIFO直接用接收时钟驱动显示侧逻辑。4.2 引脚分配与PCB寄生参数的影响信号完整性的坑往往不是逻辑层的问题而是物理层布线。HDMI信号速率在1080p时高达1.485Gbps每通道即使经过PHY芯片转成了单端信号进入FPGA引脚后依然是几百兆赫兹的数字信号。如果FPGA到PHY芯片之间的走线过长或者过孔过多眼图会明显收窄。我的板子上这段走线长度大约1.2英寸虽然有PHY芯片做缓冲但测试中发现靠近数据通道末端的电源噪声会导致偶发误码。检查后发现PHY芯片的数字电源和模拟电源没有完全隔离地平面在芯片正下方有开槽导致回流路径变差。后来在电源管脚旁边补了多个去耦电容并把信号走线调整到完整地平面参考层上方问题明显改善。如果你用的是现成开发板没法改PCB那就只能在器件配置上留余地。比如接收端把IODELAY的初始值设置为一个“经验值”发送端在OSERDES的CLKDIV相位上做扫描总能找到一个让系统稳定的工作点。这个操作听起来不够“优雅”但很多量产方案在阶段验证时就是这么干的。4.3 可以快速定位逻辑错误的调试思路没有高速示波器的情况下定位接收链路的问题主要靠FPGA内置逻辑分析仪ILA。但ILA不是在1.485Gbps数据率上直接抓的因为普通Bank的IO不能直接采样高速差分数据必须等到并行解码之后在像素时钟域采样。所以ILA的采样时钟就是clk_pix抓下来的是10bit符号和DE等信息。抓数据时要确认触发条件尽量用vsync下降沿作为触发这样每次抓到的东西都在帧边界附近便于对比。我调试时惯用的做法是先抓DE和HSYNC波形看时序参数是否符合预期再抓一组像素值看彩条颜色分布是否正确最后再检查通道对齐状态机的当前状态值。三步走完基本能定位是物理层、协议层还是逻辑层的问题。发送端调试相反没有外部抓取设备时只能靠显示器反馈。建议先在显示器和FPGA之间串一个便宜的HDMI分线器方便后续接分析设备或者在FPGA端加一个“自检回环”把发送数据和接收数据做比对通过JTAG读回比对结果。这种回环方式在调编码器和解码器兼容性时非常有用。5. 常见问题速查与调试心得5.1 无显示、花屏、间歇闪屏的排查顺序遇到的最常见问题就是看着屏幕一会儿正常一会儿花或者直接黑屏无信号。我总结了一套固定排查流程能规避大多数低级问题。无显示显示器提示无信号优先检查以下顺序有没有误把TMDS时钟通道和数据通道接反了PHY输出信号是否正确。PLL有没有锁定锁定了再看输入时钟频率对不对。输出端复位有没有正常释放复位被拉死不会出画面。显示器的EDID协商有没有完成如果你的HDMI源端没有做EDID读取显示器可能不输出。 实测中需要外接一个EDID模拟器才能继续。花屏有画面信息但错乱按这几类处理颜色错位色偏大多是通道对齐问题重新扫描IODELAY。显示位置偏移或半个画面错位多半是H/V同步时序参数不对检查行场前肩/后肩数值。竖向条纹且颜色随机优先怀疑TMDS编码器位序接反查OSERDES的D1~D10对应关系。画面上下滚动多半是VSYNC极性配置错了换极性试一下。间歇性闪屏优先检查电源纹波HDMI PHY供电不良会导致周期性误码。再检查有无跨时钟域FIFO溢出抓FIFO满标志。最后怀疑物理链路接触不良换线材或换HDMI公座再试。5.2 接收端误码率的初步判断没有误码仪的情况下怎么判断接收链路稳不稳定一个粗糙但有效的办法是统计控制字错误次数。在消隐期间TMDS控制字是固定的如果解码器频繁出现非预期控制字说明链路误码率偏高。在逻辑里加一个计数器锁存错误次数调试时通过UART或JTAG读出即可。我实测中长线劣化时这个计数器的值从个位数跳到几千非常直观地显示了链路状态变化。如果再配合通道对齐状态机的重对齐次数基本上能判断是不是需要优化硬件布局。5.3 一些值得保留的设计习惯这轮实验踩坑下来的几个习惯建议直接沉淀成流程所有关键信号加断言检查DE拉高期间RGB必须为有效值HSYNC/VSYNC在不同模式下的周期数必须锁定在期望范围内一旦断言失败就拉报警灯。复位策略统一接收域和发送域各自生成自己的复位不要共用一个异步复位信号避免一个域复位影响另一个域。版本日志写清楚每修改一次通道延迟参数、PLL相位配置都记录当时的画面表现方便回退对比。留好测试点HDL里把未使用的LED引脚映射到关键状态信号上比如pll_locked、align_state、fifo_full调试时不用开ILA也能知道大概状态。这套流程跑完之后这块板子的HDMI收发链路基本稳定了。下一步我准备往接收链路后面挂一个简单的图像处理模块做一下帧率转换和图像缩放再把发送端的彩条换成真实桌面截图做端到端的图像通路验证。后面有进展再单独写一篇。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询