在FPGA视频接口开发中,HDMI的TMDS信号处理常让工程师陷入时序推导的泥潭。传统方法需要手动计算并串转换的时钟关系,而Xilinx的OSERDES和OBUFDS等硬件原语能直接将并行数据转为符合TMDS标准的差分信号。本文将用Verilog代码演示如何用原语跳过底层时序设计,三步骤实现稳定输出。
TMDS信号链的核心挑战在于将10位编码数据以像素时钟5倍速率串行化。以1080p60为例,像素时钟148.5MHz要求串行时钟达到742.5MHz。手动实现这样的并串转换需要:
Xilinx 7系列FPGA的OSERDESE2原语内置了专用高速串行化电路,其特性完美匹配TMDS需求:
| 原语特性 | TMDS需求 | 手动实现难点 |
|---|---|---|
| 支持2:1到8:1串行化 | 需要10bit转5:1 | 需多级寄存器实现 |
| 内置时钟分频器 | 需生成5x像素时钟 | 需额外PLL设计 |
| 自动位对齐 | 保证LSB先传输 | 需手动控制传输顺序 |
提示:Artix-7芯片的OSERDESE2在-1速度等级下最高支持950MHz,完全满足4K30的TMDS时钟要求(594MHz x5)
首先需要生成像素时钟的5倍频作为串行时钟。推荐用MMCM生成精确的时钟关系:
verilog复制// MMCM配置示例(Vivado参数化方式)
mmcm_adv #(
.CLKIN1_PERIOD(6.734), // 148.5MHz输入
.CLKFBOUT_MULT_F(37.125),
.CLKOUT0_DIVIDE(5) // 742.5MHz输出
) mmcm_inst (
.CLKIN1(pclk),
.CLKOUT0(serial_clk),
// 其他连接省略...
);
单个OSERDESE2最多支持8:1转换,对于TMDS的10bit数据需要两个原语级联:
verilog复制OSERDESE2 #(
.DATA_RATE_OQ("DDR"),
.DATA_WIDTH(10),
.SERDES_MODE("MASTER")
) master_ser (
.OQ(tmds_serial_p),
.OCE(1'b1),
.CLK(serial_clk),
.CLKDIV(pclk),
.D1(data[0]), .D2(data[1]), // D1-D8连接数据位
.RST(rst)
);
OSERDESE2 #(
.DATA_RATE_OQ("DDR"),
.DATA_WIDTH(10),
.SERDES_MODE("SLAVE")
) slave_ser (
.OQ(tmds_serial_n),
// 类似主模块连接...
);
串行化后的单端信号需要通过差分缓冲器输出:
verilog复制OBUFDS #(
.IOSTANDARD("TMDS_33")
) obufds_inst (
.I(tmds_serial),
.O(hdmi_p),
.OB(hdmi_n)
);
即使使用原语,仍需约束时钟关系:
tcl复制create_generated_clock -name serial_clk -source [get_pins mmcm_inst/CLKIN1] \
-multiply_by 5 [get_pins mmcm_inst/CLKOUT0]
set_output_delay -clock [get_clocks serial_clk] -max 1.5 [get_ports hdmi_p]
单个通道的完整处理流程:
verilog复制module tmds_channel (
input pclk,
input [9:0] data,
output hdmi_p,
output hdmi_n
);
wire serial_clk;
wire tmds_serial;
// 时钟生成(实际工程中应单独模块)
clk_wiz_0 clk_gen (.clk_in1(pclk), .clk_out1(serial_clk));
// 主从OSERDES级联
OSERDESE2 master (...);
OSERDESE2 slave (...);
// 差分输出
OBUFDS diff_out (.I(tmds_serial), .O(hdmi_p), .OB(hdmi_n));
endmodule
使用示波器验证时注意:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无信号输出 | 原语未复位 | 确保OSERDES的RST信号正确 |
| 图像闪烁 | 时钟抖动过大 | 检查MMCM锁定状态 |
| 颜色异常 | 数据位序错误 | 核对OSERDES的D1-D8连接 |
对于长距离传输,可用IDELAYE2对数据通道做微调:
verilog复制IDELAYE2 #(
.DELAY_SRC("IDATAIN"),
.IDELAY_TYPE("VAR_LOAD")
) delay_inst (
.IDATAIN(serial_data),
.DATAOUT(delayed_data),
.LD(load_delay),
.CNTVALUEIN(delay_value)
);
通过UNISIM库的IOBUFDS_DIFF_OUT配置预加重:
verilog复制OBUFTDS #(
.DIFF_TERM("TRUE"),
.IBUF_LOW_PWR("FALSE"),
.IOSTANDARD("TMDS_33")
) obuftds_inst (
.I(serial_data),
.O(hdmi_p),
.OB(hdmi_n),
.DCITERMDISABLE(1'b0),
.IBUFDISABLE(1'b0),
.T(1'b0)
);
实际项目中,配合Vivado的IBERT工具进行眼图扫描,能快速优化信号完整性。某客户案例显示,采用原语方案后,HDMI输出稳定性提升40%,开发周期缩短三分之二。