Quick Start
- 步骤1:下载并安装 Vivado 2023.2(或更高版本),确保包含 H.265 编码器 IP 核(如 Xilinx Video Codec Unit)。
- 步骤2:准备一块支持 H.265 编码的 FPGA 开发板(如 Xilinx Zynq UltraScale+ MPSoC ZCU106)。
- 步骤3:从 Xilinx 官网获取 H.265 编码器 IP 核的评估版或授权版,并添加到 Vivado IP Catalog。
- 步骤4:创建一个新的 Vivado 工程,选择目标器件(如 xczu7ev-ffvc1156-2-e)。
- 步骤5:在 Block Design 中实例化 Video Codec Unit IP,配置编码参数(分辨率 1920x1080、帧率 60fps、码率 20 Mbps)。
- 步骤6:添加视频输入源(如 HDMI RX 或 Test Pattern Generator)和输出接口(如 AXI4-Stream)。
- 步骤7:运行 Synthesis、Implementation,生成 Bitstream。
- 步骤8:导出硬件描述文件(.xsa),在 Vitis 中编写应用程序控制编码器启动/停止,并通过 UART 打印编码状态。
- 步骤9:连接 HDMI 摄像头输入,观察编码器输出(可通过 Ethernet 或 PCIe 抓取码流)。
- 步骤10:验证输出 H.265 码流能被标准解码器(如 ffmpeg)解码播放。
预期结果:开发板输出实时 H.265 码流,延迟 35 dB。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Zynq UltraScale+ MPSoC ZCU106 | Zynq-7000 系列(如 ZC706)但编码性能受限;或使用 Intel Arria 10 并搭配第三方 IP |
| EDA 版本 | Vivado 2023.2 + Vitis 2023.2 | Vivado 2022.2 但部分 IP 版本不兼容;Vitis 统一平台 |
| 仿真器 | Vivado Simulator 或 ModelSim SE-64 2021.1 | QuestaSim、Verilator(仅 RTL 级) |
| 时钟/复位 | 输入时钟 148.5 MHz(1080p60 标准),异步复位高有效 | 可使用 MMCM 生成;复位信号需同步 |
| 接口依赖 | HDMI 2.0 输入(通过 FMC 子卡),AXI4-Stream 视频接口 | SDI 输入、Camera Link;或使用 Test Pattern Generator 模拟 |
| 约束文件 | XDC 约束:时钟周期 6.734 ns(148.5 MHz),输入输出延迟约束 | 使用 Vivado 自动推导,但建议手动指定 |
| IP 核授权 | Xilinx Video Codec Unit (VCU) 需购买 License | 开源 H.265 编码器(如 HEVC Software Reference)但 FPGA 实现需自行移植 |
| 主机系统 | Ubuntu 20.04 LTS 或 Windows 10 64-bit | CentOS 7;内存 ≥ 16 GB,磁盘 ≥ 50 GB |
目标与验收标准
- 功能点:实现 1080p60 实时 H.265 编码,输出符合 ISO/IEC 23008-2 标准的码流。
- 性能指标:编码延迟 ≤ 1 帧(16.7ms),码率控制精度 ±5%(目标 20 Mbps),PSNR ≥ 35 dB(典型测试序列如 ParkScene)。
- 资源占用:LUT ≤ 40K,BRAM ≤ 128 个,DSP ≤ 64 个(以 VCU IP 的典型配置为准)。
- Fmax:≥ 148.5 MHz(满足 1080p60 像素时钟)。
- 验收方式:使用 Vivado 的 Waveform Viewer 观察编码器输出 AXI4-Stream 的 tvalid/tready 握手;通过 Vitis 串口打印编码帧数;将码流保存为 .265 文件,用 ffmpeg 解码播放。
实施步骤
工程结构
project_root/
├── hls/ # 如果使用 HLS 编写预处理模块
├── ip/ # 自定义 IP 核(如颜色空间转换)
├── src/
│ ├── hdl/ # RTL 代码(顶层、时钟生成、复位同步)
│ ├── constraints/ # XDC 约束文件
│ └── simulation/ # 仿真测试平台
├── block_design.tcl # 生成 Block Design 的脚本
└── vitis_app/ # Vitis 应用程序源码逐行说明
- 第 1 行:hls/ 目录存放使用 Vitis HLS 开发的预处理模块(如缩放、去噪),可综合为 RTL。
- 第 2 行:ip/ 目录存放自定义 IP 核,如 RGB 到 YUV 转换,便于在 Block Design 中集成。
- 第 3 行:src/hdl/ 包含顶层模块(top.v),负责例化时钟生成、复位同步和 VCU IP。
- 第 4 行:src/constraints/ 存放 .xdc 文件,定义时钟周期、输入输出延迟等时序约束。
- 第 5 行:src/simulation/ 包含 Testbench,用于功能仿真验证编码器行为。
- 第 6 行:block_design.tcl 是可重现的脚本,通过 source 命令在 Vivado Tcl Console 中运行,自动创建 Block Design。
- 第 7 行:vitis_app/ 存放 C/C++ 源码,用于配置 VCU 寄存器、启动编码、读取状态。
关键模块:顶层 RTL
module top (
input wire clk_148m5, // 148.5 MHz 像素时钟
input wire rst_n, // 异步复位,低有效
input wire [23:0] video_data, // 24-bit RGB 视频数据
input wire video_vsync, // 帧同步信号
input wire video_hsync, // 行同步信号
input wire video_de, // 数据使能
output wire [31:0] encoded_data, // 编码后码流(AXI4-Stream)
output wire encoded_valid,
input wire encoded_ready,
output wire irq // 编码完成中断
);
// 内部信号
wire clk_locked;
wire rst_sync;
wire [23:0] yuv_data;
wire yuv_valid;
// 时钟与复位
clk_wiz_0 u_clk_mmcm (
.clk_in1 (clk_148m5),
.clk_out1 (clk_vcu), // VCU 核心时钟,例如 300 MHz
.locked (clk_locked)
);
rst_sync u_rst_sync (
.clk (clk_vcu),
.rst_in (rst_n & clk_locked),
.rst_out (rst_sync)
);
// 颜色空间转换
rgb2yuv u_rgb2yuv (
.clk (clk_148m5),
.rst_n (rst_n),
.rgb (video_data),
.yuv (yuv_data),
.valid (yuv_valid)
);
// VCU 编码器实例化
vcu_encoder_0 u_vcu (
.s_axi_CTRL_AWADDR (axi_ctrl_awaddr),
.s_axi_CTRL_AWVALID (axi_ctrl_awvalid),
.s_axi_CTRL_AWREADY (axi_ctrl_awready),
.s_axi_CTRL_WDATA (axi_ctrl_wdata),
.s_axi_CTRL_WVALID (axi_ctrl_wvalid),
.s_axi_CTRL_WREADY (axi_ctrl_wready),
.s_axi_CTRL_BRESP (axi_ctrl_bresp),
.s_axi_CTRL_BVALID (axi_ctrl_bvalid),
.s_axi_CTRL_BREADY (axi_ctrl_bready),
.s_axi_CTRL_ARADDR (axi_ctrl_araddr),
.s_axi_CTRL_ARVALID (axi_ctrl_arvalid),
.s_axi_CTRL_ARREADY (axi_ctrl_arready),
.s_axi_CTRL_RDATA (axi_ctrl_rdata),
.s_axi_CTRL_RRESP (axi_ctrl_rresp),
.s_axi_CTRL_RVALID (axi_ctrl_rvalid),
.s_axi_CTRL_RREADY (axi_ctrl_rready),
.s_axis_video_TDATA (yuv_data),
.s_axis_video_TVALID(yuv_valid),
.s_axis_video_TREADY(video_ready),
.s_axis_video_TUSER (video_vsync),
.s_axis_video_TLAST (video_hsync),
.m_axis_encoded_TDATA (encoded_data),
.m_axis_encoded_TVALID(encoded_valid),
.m_axis_encoded_TREADY(encoded_ready),
.irq (irq)
);
endmodule逐行说明
- 第 1 行:模块声明,输入输出端口定义。clk_148m5 是 1080p60 的标准像素时钟,rst_n 是异步复位。
- 第 2-5 行:视频输入接口,包含 RGB 数据、帧同步(vsync)、行同步(hsync)和数据使能(de)。
- 第 6-8 行:编码输出接口,采用 AXI4-Stream 协议,encoded_data 是 32-bit 码流数据。
- 第 9 行:中断信号,用于通知处理器编码完成。
- 第 12-13 行:内部信号声明,clk_locked 来自 MMCM,rst_sync 是同步复位。
- 第 16-20 行:例化时钟生成 IP(clk_wiz_0),输入 148.5 MHz,输出 300 MHz 给 VCU 核心。
- 第 22-26 行:复位同步模块,确保复位信号与 clk_vcu 同步,避免异步复位导致亚稳态。
- 第 28-34 行:颜色空间转换模块,将 RGB 转换为 YUV 4:2:0 格式,这是 H.265 编码的标准输入。
- 第 36-60 行:VCU 编码器实例化。s_axi_CTRL 是 AXI-Lite 控制接口,用于配置编码参数;s_axis_video 是视频输入流;m_axis_encoded 是码流输出流。irq 输出中断。
- 注意:VCU IP 的端口数量较多,这里仅列出关键端口,实际需根据 IP 配置添加所有端口。
时序与约束
# 主时钟约束
create_clock -period 6.734 -name clk_148m5 [get_ports clk_148m5]
# 生成时钟约束(MMCM 输出)
create_generated_clock -name clk_vcu -source [get_pins u_clk_mmcm/clk_in1]
-divide_by 1 -multiply_by 2 [get_pins u_clk_mmcm/clk_out1]
# 输入延迟约束(假设外部器件延迟 1 ns)
set_input_delay -clock clk_148m5 -max 1.0 [get_ports video_data*]
set_input_delay -clock clk_148m5 -min 0.5 [get_ports video_data*]
# 输出延迟约束
set_output_delay -clock clk_vcu -max 2.0 [get_ports encoded_data*]
set_output_delay -clock clk_vcu -min 1.0 [get_ports encoded_data*]
# 异步复位约束,忽略
set_false_path -from [get_ports rst_n]逐行说明
- 第 1 行:定义主时钟 clk_148m5,周期 6.734 ns(即 148.5 MHz)。
- 第 2-3 行:定义 MMCM 生成的时钟 clk_vcu,源时钟是 clk_148m5,倍频系数 2(300 MHz)。
- 第 4-5 行:输入延迟约束,指定外部器件到 FPGA 引脚的延迟范围,帮助 Vivado 分析建立/保持时间。
- 第 6-7 行:输出延迟约束,指定 FPGA 输出到外部器件的延迟。
- 第 8 行:将异步复位路径设为 false path,因为复位信号不参与时序分析。
常见坑与排查
- 坑 1:VCU IP 配置错误导致编码失败。排查:检查 IP 配置中分辨率、帧率、码率是否与输入匹配;确认 License 已正确加载。
- 坑 2:时钟域交叉(CDC)问题。VCU 核心时钟(300 MHz)与像素时钟(148.5 MHz)不同,需确保 AXI4-Stream 接口的握手信号正确同步(VCU IP 内部已处理,但外部输入需满足时序)。
- 坑 3:颜色空间转换错误。H.265 要求 YUV 4:2:0 输入,若输入为 RGB 或 YUV 4:4:4,编码器可能输出异常。排查:用仿真观察 yuv_data 的格式。
- 坑 4:复位时序问题。异步复位释放时若未同步,可能导致 VCU 状态机进入未知状态。排查:检查 rst_sync 模块输出波形。
验证:仿真测试平台
module tb_top;
reg clk;
reg rst_n;
reg [23:0] video_data;
reg video_vsync, video_hsync, video_de;
wire [31:0] encoded_data;
wire encoded_valid, encoded_ready;
wire irq;
// 时钟生成
always #3.367 clk = ~clk; // 148.5 MHz
// 复位
initial begin
clk = 0;
rst_n = 0;
#100 rst_n = 1;
end
// 视频输入模拟
initial begin
// 等待复位完成
@(posedge rst_n);
#200;
// 发送一帧 1920x1080 的测试图像(简化:仅发送几个像素)
video_vsync = 1;
#10 video_vsync = 0;
repeat (1920) begin
video_hsync = 1;
repeat (1080) begin
video_de = 1;
video_data = 24'hFF0000; // 红色像素
#6.734;
end
video_hsync = 0;
end
// 帧结束
video_vsync = 1;
#10 video_vsync = 0;
#1000 $finish;
end
// 实例化 DUT
top u_top (
.clk_148m5 (clk),
.rst_n (rst_n),
.video_data(video_data),
.video_vsync(video_vsync),
.video_hsync(video_hsync),
.video_de (video_de),
.encoded_data(encoded_data),
.encoded_valid(encoded_valid),
.encoded_ready(1'b1), // 始终准备好接收
.irq (irq)
);
endmodule逐行说明
- 第 1 行:Testbench 模块声明,无端口。
- 第 2-7 行:声明激励信号和监视信号。
- 第 10 行:时钟生成,周期 6.734 ns(148.5 MHz)。
- 第 13-17 行:复位初始化,先置低 100 ns 后释放。
- 第 20-37 行:模拟一帧视频输入。vsync 先拉高一个时钟,然后逐行发送像素数据。这里简化了行同步和像素计数,实际应使用循环精确模拟 1920x1080。
- 第 40-49 行:实例化顶层模块,连接信号。encoded_ready 固定为 1,表示输出端始终准备好接收。
- 注意:仿真时 VCU IP 可能需要额外初始化序列,请参考 Xilinx 文档。
上板验证
- 步骤 1:将 Bitstream 下载到 FPGA。
- 步骤 2:在 Vitis 中运行应用程序,通过 AXI-Lite 配置 VCU 参数(如分辨率、码率)。
- 步骤 3:连接 HDMI 摄像头输入,观察编码器输出。可通过 Ethernet 将码流发送到 PC,使用 ffmpeg 解码:ffmpeg -i encoded_stream.265 -c:v hevc -f mp4 output.mp4。
- 步骤 4:检查解码后的图像质量,计算 PSNR 是否达标。
原理与设计说明
H.265(HEVC)编码器相比 H.264 能节省约 50% 码率,但计算复杂度大幅提升。FPGA 实现的核心优势在于流水线并行处理:每个编码工具(如帧内预测、运动估计、变换量化)可映射为独立硬件模块,实现低延迟实时编码。
关键 trade-off:
- 资源 vs Fmax:VCU IP 内部使用大量 DSP 和 BRAM 实现运动估计和变换。若资源紧张,可降低搜索范围(如 64x64 改为 32x32),但会降低压缩效率。
- 吞吐 vs 延迟:纯硬件流水线可实现每时钟周期输出一个像素,延迟仅几行;但码率控制算法(如 CBR)需要帧级反馈,引入 1 帧延迟。若要求超低延迟(如 < 1ms),需使用固定 QP 模式。
- 易用性 vs 可移植性:Xilinx VCU IP 是黑盒,配置简单但无法修改内部算法。开源方案(如 HEVC Software Reference)可定制,但 FPGA 移植工作量大,且难以达到实时性能。
为什么选择 VCU IP:对于入门项目,VCU 提供了完整的 H.265 编码功能,只需关注接口和配置,降低开发难度。后续可在此基础上替换为自定义编码模块。
验证与结果
| 指标 | 测量值(示例) | 测量条件 |
|---|---|---|
| Fmax | 148.5 MHz(像素时钟) | Vivado 时序报告,worst negative slack = 0.02 ns |
| 资源占用(LUT) | 35,200 | VCU IP 配置为 1080p60,中等质量 |
| 资源占用(BRAM) | 112 个(36Kb) | 同上 |
| 资源占用(DSP) | 48 个 | 同上 |
| 编码延迟 | 16.7 ms(1 帧) | 从输入像素到输出码流,CBR 模式 |
| 码率精度 | 20.3 Mbps(目标 20 Mbps) | 测试序列 ParkScene,300 帧 |
| PSNR | 36.2 dB | Y 分量,QP=32 |
注意:以上数据为示例值,实际结果取决于器件、IP 配置和测试序列。以实际工程和数据手册为准。
故障排查(Troubleshooting)
- 现象 1:编码器无输出。原因:VCU 未正确配置或 License 无效。检查点:Vitis 应用程序中寄存器读写是否正常;Vivado 中 IP 状态是否为“Ready”。修复建议:重新生成 License 并加载。
- 现象 2:输出码流无法解码。原因:编码参数与解码器不匹配(如分辨率、帧率)。检查点:用码流分析工具(如 Elecard HEVC Analyzer)查看 SPS/PPS。修复建议:确保编码配置与解码器支持一致。
- 现象 3:编码图像花屏。原因:视频输入时序错误(如 vsync/hsync 极性不对)。检查点:用逻辑分析仪观察输入信号。修复建议:调整视频源配置。
- 现象 4:码率波动大。原因:码率控制算法参数不合适。检查点:查看编码器统计寄存器。修复建议:调整 VBV buffer size 或启用 CBR 模式。
- 现象 5:



