1. 项目概述:一个纯Verilog实现的FPGA HDMI显示控制器
最近在折腾FPGA上的视频输出,发现很多现成的HDMI IP要么绑定特定厂商工具,要么接口复杂,要么对时钟域处理得不够优雅。于是,我花了不少时间研究并整理了一个自用的、完全用Verilog实现的HDMI发送端控制器。这个项目的核心目标就一个:提供一个干净、可移植、接口简单的HDMI TX模块,让你能专注于图像内容的生成,而不用头疼底层视频时序和高速串行信号的编码。
简单来说,这个IP核就像一个“视频流搬运工”。你只需要按照它规定的节奏提供每个像素的RGB颜色值,它就能帮你处理好一切:从生成标准的HDMI视频时序(行同步、场同步、消隐区),到将8位像素数据编码成10位的TMDS码流,最后通过FPGA的IO引脚以差分信号的形式发送出去,驱动你的显示器。整个设计是纯RTL代码,不依赖任何厂商的专属IP(如Xilinx的SelectIO或Altera的LVDS SerDes),因此它在Intel(Quartus)、AMD/Xilinx(Vivado)、国产高云(Gowin)等多家FPGA平台上都能直接编译使用,实测过Tang Nano 4K、DE10-Standard等几块板子,都很稳定。
这个控制器最大的一个亮点是它的用户接口时钟( clk )和HDMI驱动时钟( pclk_x5 )是异步的。这意味着你生成图像的逻辑可以用一个独立的、更合适的时钟(比如从SDRAM读取数据的时钟),而不必被HDMI像素时钟绑死。模块内部通过巧妙的异步FIFO和握手机制完成了时钟域跨越,对用户完全透明。你只需要关心在 clk 时钟域下,如何响应它的像素请求即可。
2. 核心设计思路与方案选型
为什么选择从头用Verilog实现HDMI TX,而不是用现成的方案?这里有几个关键的考量点,也是这个项目设计的出发点。
2.1 为何选择纯Verilog实现?
市面上的FPGA开发板,其HDMI输出方案大致分三类:一是使用专用的HDMI编码芯片(如ADV7513),FPGA通过I2C配置后并行输出RGB数据;二是使用FPGA厂商提供的HDMI或DisplayPort IP核(如Xilinx的Video PHY Controller);三是直接用FPGA的IO口模拟TMDS差分信号。
第一种方案需要外置芯片,增加了BOM成本和PCB面积。第二种方案最省事,性能也最好,但严重依赖特定EDA工具和授权,代码可移植性差,想换个FPGA平台或者用开源工具链(如Yosys+Nextpnr)就没办法了。 我们这个项目走的是第三条路 ,即用FPGA的普通IO口来实现TMDS。它的优势在于极致简单和完全开源可控:只需要8个单端IO(组成4对差分),代码在任何支持Verilog-2001标准的工具链下都能综合。当然,它对FPGA的IO性能有一定要求,通常需要能跑到几百MHz,但对于当今主流的FPGA(即使是一些低成本的型号)来说,驱动720p或1080p的TMDS时钟(742.5MHz或1.485GHz的五分之一)是完全可以胜任的。
注意 :这里说的“IO性能”主要指IO Bank支持的最高单端数据速率。例如,很多FPGA的普通IO在2.5V LVCMOS电平下,可以稳定工作在200-400MHz。对于640x480@60Hz(像素时钟25MHz,TMDS时钟125MHz)的应用绰绰有余。对于更高分辨率,需要查阅FPGA的数据手册,确认目标IO Bank的“最大输出频率”指标。
2.2 用户接口设计:请求-响应模型
图像数据从哪里来?可能是从片上内存读取的静态图片,可能是通过摄像头采集的实时视频流,也可能是通过算法动态生成的图形。为了适配各种可能的视频源,模块的用户接口设计成了一个简单的“请求-响应”模型。
模块内部有一个严格按照HDMI时序工作的“视频时序发生器”。当它需要显示一个有效像素时,就会通过 req_en 信号向外部发起一次请求。你的逻辑在检测到 req_en 拉高后,需要在固定的延迟周期(由参数 RESP_LATENCY 决定,可选1、2、3个 clk 周期)后,将对应的RGB值放到 resp_red 、 resp_green 、 resp_blue 总线上。这个模型非常直观,它把视频生成的“节奏”完全交给了HDMI控制器,你只需要当一个按时交货的供应商。
这种设计的好处是分离了关注点。你的图像生成逻辑可以自由地运行在另一个时钟域( clk ),只需要实现一个FIFO或缓冲区来应对请求即可。模块提供的 req_sof (帧开始)、 req_eof (帧结束)、 req_sol (行开始)、 req_eol (行结束)这些辅助信号,极大地简化了外部逻辑对视频帧和行位置的判断,方便你实现帧缓冲切换、行缓存预取等高级功能。
2.3 时钟架构:异步时钟域处理
这是本设计的一个技术核心。HDMI的像素时钟(pclk)必须严格符合标准定义(例如,640x480@60Hz是25.175MHz)。但你的图像处理逻辑可能运行在完全不同的频率上,比如从DDR3内存读取数据的133MHz,或者摄像头传感器输出的24MHz。
如果强制让用户逻辑也运行在pclk下,会带来很多麻烦:可能需要进行复杂的时钟分频/倍频,或者让整个系统性能受限于视频输出速率。因此,我决定在模块内部消化这个时钟差异。具体实现上,在用户接口侧( req_* , resp_* )使用用户提供的 clk ,在TMDS编码和发送侧使用5倍像素时钟 pclk_x5 。两者之间通过一个异步FIFO进行像素数据的跨时钟域传递。
这个异步FIFO的深度需要仔细计算。它必须足够深,以吸收两个时钟频率微小差异(即使它们标称值相同,也存在相位和抖动差异)以及突发请求带来的波动。在设计中,我设置了一个合理的默认深度,并提供了参数允许用户根据实际情况调整。 一个重要的约束是:用户时钟 clk 的频率必须略高于 pclk_x5 / 5 。这是因为HDMI时序发生器是“主”,它按照固定速率消耗像素;如果用户侧生产像素的速度慢于消耗速度,FIFO最终会被读空,导致显示出现撕裂或黑屏。让 clk 稍快一点,可以确保FIFO不会下溢,即使有波动也有缓冲余地。
3. 硬件连接与FPGA工程配置详解
理论说得再多,最后还得落到硬件和代码上。这部分是实操的关键,我会结合Tang Nano 4K开发板的例子,把每一步都拆开讲清楚。
3.1 HDMI连接器与FPGA引脚映射
一个标准的HDMI Type A接口有19个引脚,但我们只需要关心其中用于TMDS数据传送的4对差分线:
- TMDS Clock Pair :
hdmi_clk_p/hdmi_clk_n - TMDS Data0 Pair :
hdmi_tx0_p/hdmi_tx0_n(通常对应蓝色通道) - TMDS Data1 Pair :
hdmi_tx1_p/hdmi_tx1_n(通常对应绿色通道) - TMDS D



被折叠的 条评论
为什么被折叠?



