当我们需要在嵌入式系统中实现高清视频输出时,HDMI无疑是最主流的选择。对于使用Xilinx Zynq 7020这类FPGA的开发者和工程师来说,在PL端实现HDMI输出是一项极具挑战性又充满成就感的任务。本文将带你从TMDS编码原理开始,逐步构建完整的HDMI 1.4视频输出系统,涵盖Verilog实现、时序控制以及与PS端的协同设计。
TMDS(Transition Minimized Differential Signaling)是HDMI传输的核心编码技术,它通过三个关键步骤实现数据转换:
典型的TMDS编码器Verilog接口如下:
verilog复制module tmds_encoder (
input clk,
input [7:0] din,
input [1:0] ctrl,
input de,
output reg [9:0] dout
);
// 编码逻辑实现
endmodule
Zynq 7020的PL端引脚需要正确配置以支持HDMI输出:
| 信号名称 | FPGA引脚类型 | 备注 |
|---|---|---|
| TMDS_CLK_P | LVDS_25 | 差分时钟正极 |
| TMDS_CLK_N | LVDS_25 | 差分时钟负极 |
| TMDS_Dx_P | LVDS_25 | 数据通道x正极(x=0-2) |
| TMDS_Dx_N | LVDS_25 | 数据通道x负极(x=0-2) |
| DDC_SCL | LVCMOS33 | 显示数据通道时钟 |
| DDC_SDA | LVCMOS33 | 显示数据通道数据 |
注意:LVDS_25电平标准需要正确设置IO约束,建议在XDC文件中明确指定
实现标准1080p分辨率需要精确控制以下时序参数:
verilog复制parameter H_ACTIVE = 1920;
parameter H_FP = 88;
parameter H_SYNC = 44;
parameter H_BP = 148;
parameter H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;
parameter V_ACTIVE = 1080;
parameter V_FP = 4;
parameter V_SYNC = 5;
parameter V_BP = 36;
parameter V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;
对应的像素时钟计算:
code复制Pixel Clock = (1920 + 88 + 44 + 148) × (1080 + 4 + 5 + 36) × 60 ≈ 148.5MHz
采用三段式状态机实现稳定的时序控制:
verilog复制always @(posedge pix_clk) begin
if (h_counter < H_TOTAL-1)
h_counter <= h_counter + 1;
else begin
h_counter <= 0;
if (v_counter < V_TOTAL-1)
v_counter <= v_counter + 1;
else
v_counter <= 0;
end
end
assign h_sync = (h_counter >= H_ACTIVE + H_FP) &&
(h_counter < H_ACTIVE + H_FP + H_SYNC);
assign v_sync = (v_counter >= V_ACTIVE + V_FP) &&
(v_counter < V_ACTIVE + V_FP + V_SYNC);
assign de = (h_counter < H_ACTIVE) && (v_counter < V_ACTIVE);
完整的HDMI发送器包含以下模块:
顶层模块接口示例:
verilog复制module hdmi_tx_top (
input sys_clk,
input reset,
output tmds_clk_p,
output tmds_clk_n,
output [2:0] tmds_data_p,
output [2:0] tmds_data_n
);
// 各模块实例化
endmodule
与PS端协同工作时,推荐使用AXI Stream接口传输视频数据:
verilog复制axis_video_src u_axis_src (
.aclk(pix_clk),
.aresetn(!reset),
.tvalid(axis_tvalid),
.tready(axis_tready),
.tdata(axis_tdata),
.tuser(axis_tuser),
.tlast(axis_tlast)
);
assign axis_tready = de && (h_counter == 0) && (v_counter == 0);
assign axis_tuser = (h_counter == 0) && (v_counter == 0);
assign axis_tlast = (h_counter == H_ACTIVE-1);
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无信号输出 | 时钟未锁定 | 检查MMCM/PLL配置 |
| 图像偏移 | 时序参数错误 | 重新计算并验证时序参数 |
| 颜色异常 | TMDS编码错误 | 检查各通道编码逻辑 |
| 间歇性闪屏 | 时钟抖动过大 | 优化时钟布局,添加缓冲 |
| 分辨率不支持 | EDID读取失败 | 检查DDC通道连接 |
提示:使用Xilinx IBERT工具可以进行眼图扫描和信号质量分析
通过参数化设计支持多种分辨率:
verilog复制module video_timing_gen #(
parameter H_ACTIVE = 1920,
parameter V_ACTIVE = 1080,
// 其他时序参数...
) (
// 接口定义...
);
// 时序生成逻辑
endmodule
实现完整的HDMI功能需要处理热插拔检测(HPD)和EDID读取:
verilog复制// EDID读取状态机
always @(posedge ddc_clk) begin
case(edid_state)
EDID_IDLE: begin
if (hpd_detected)
edid_state <= EDID_START;
end
EDID_START: begin
// 发起I2C读取
edid_state <= EDID_READ;
end
// 其他状态...
endcase
end
在Zynq 7020项目中使用HDMI输出时,最大的挑战往往是信号完整性和时序收敛问题。通过合理规划时钟域、严格约束IO延迟以及充分的仿真验证,可以构建稳定可靠的HDMI视频输出系统。