Quick Start:最短路径跑通HLS+SystemVerilog联合设计
- 步骤1:环境准备 安装Vivado 2024.1及以上版本(含Vitis HLS),确认系统支持SystemVerilog(IEEE 1800-2017)。
- 步骤2:创建HLS项目 在Vitis HLS中新建项目,选择器件(如xcku040-ffva1156-2-e),添加C/C++源文件(如
filter.cpp)。 - 步骤3:编写HLS顶层函数 实现一个简单的FIR滤波器,使用
hls::stream接口,编译通过。 - 步骤4:C仿真与C综合 运行C仿真验证功能,执行C综合生成RTL(Verilog/VHDL)。
- 步骤5:导出IP核 将综合后的RTL打包为Vivado IP核(.zip),供SystemVerilog顶层调用。
- 步骤6:创建Vivado工程 新建Vivado项目,添加HLS导出的IP核到IP库。
- 步骤7:编写SystemVerilog顶层 在Block Design中实例化HLS IP,编写SystemVerilog模块连接接口(AXI4-Stream或ap_ctrl)。
- 步骤8:综合与实现 运行综合、布局布线,检查时序(Fmax > 200 MHz)。
- 步骤9:仿真验证 编写SystemVerilog testbench,驱动输入数据,观察输出波形与C仿真结果一致。
- 步骤10:上板测试 生成比特流,下载到FPGA开发板,通过串口或逻辑分析仪验证实际输出。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Kintex-7 / Artix-7(如xc7k325t-2ffg900) | Zynq-7000 / Virtex-7;需调整HLS优化策略 |
| EDA版本 | Vivado 2024.1 + Vitis HLS 2024.1 | Vivado 2023.2(HLS 2023.2) |
| 仿真器 | Vivado Simulator (xsim) 或 QuestaSim 2023.3 | ModelSim SE-64 10.7c |
| 时钟/复位 | 时钟100 MHz(周期10 ns),异步高电平复位 | 时钟50 MHz,同步复位(需修改HLS接口) |
| 接口依赖 | AXI4-Stream(数据与tvalid/tready握手) | ap_fifo / ap_hs(用于简单流控) |
| 约束文件 | XDC文件:时钟约束(create_clock)、输入输出延迟 | 自动推导(不推荐,时序易失败) |
| 开发板 | Nexys Video / KC705(含UART或JTAG调试) | Basys 3(资源受限,需简化设计) |
| 操作系统 | Windows 10/11 64-bit 或 Ubuntu 20.04/22.04 | CentOS 7(需额外库支持) |
目标与验收标准
- 功能点:HLS生成的IP核在SystemVerilog顶层中正确工作,输入输出数据一致(误差<1%)。
- 性能指标:时钟频率≥200 MHz,吞吐率≥1 Gbps(数据位宽16-bit)。
- 资源占用:LUT ≤ 5000,FF ≤ 3000,BRAM ≤ 10,DSP ≤ 20(以FIR滤波器为例)。
- 验证方式:C仿真与SystemVerilog仿真输出波形对比,通过脚本自动比对数据文件。
- 上板验收:开发板通过UART输出结果,与PC端接收数据一致,无丢包。
实施步骤
阶段一:工程结构设计
- 创建目录结构:
project/下分hls/(HLS源文件)、sv/(SystemVerilog顶层)、constraints/(XDC)、sim/(testbench)。 - 使用Makefile或Tcl脚本自动化编译流程,避免手动操作。
- 坑与排查:路径含中文或空格会导致Vivado报错,一律使用英文路径。
阶段二:HLS关键模块编写
// filter.cpp - 16-tap FIR滤波器,使用hls::stream接口
#include <hls_stream.h>
#include <ap_int.h>
typedef ap_int<16> data_t;
void fir(hls::stream<data_t> &in, hls::stream<data_t> &out, data_t coeff[16]) {
#pragma HLS INTERFACE axis port=in
#pragma HLS INTERFACE axis port=out
#pragma HLS INTERFACE s_axilite port=coeff bundle=CTRL
#pragma HLS INTERFACE s_axilite port=return bundle=CTRL
static data_t shift_reg[16];
#pragma HLS ARRAY_PARTITION variable=shift_reg complete dim=1
data_t acc = 0;
data_t x = in.read();
for (int i = 15; i > 0; i--) {
#pragma HLS PIPELINE II=1
shift_reg[i] = shift_reg[i-1];
acc += shift_reg[i] * coeff[i];
}
shift_reg[0] = x;
acc += x * coeff[0];
out.write(acc);
}注意:#pragma HLS PIPELINE II=1 确保每个时钟周期处理一个样本,但需检查资源冲突。
- 坑与排查:HLS中
hls::stream默认深度为1,若仿真死锁需增大深度(如hls::stream<data_t> in(16))。 - 坑与排查:系数数组
coeff若未指定存储类型,默认用BRAM,会引入多周期延迟,改为#pragma HLS ARRAY_PARTITION variable=coeff complete dim=1。
阶段三:SystemVerilog顶层与连接
// top.sv - 实例化HLS IP并连接AXI4-Stream
module top (
input logic clk,
input logic rst_n,
input logic [15:0] data_in,
input logic valid_in,
output logic [15:0] data_out,
output logic valid_out
);
logic [15:0] coeff[16];
logic ap_start, ap_done, ap_idle, ap_ready;
logic [31:0] coeff_addr;
logic [31:0] coeff_din;
logic coeff_we;
// 实例化HLS IP
fir_0 u_fir (
.ap_clk(clk),
.ap_rst_n(rst_n),
.ap_start(ap_start),
.ap_done(ap_done),
.ap_idle(ap_idle),
.ap_ready(ap_ready),
.in_TDATA(data_in),
.in_TVALID(valid_in),
.in_TREADY(),
.out_TDATA(data_out),
.out_TVALID(valid_out),
.out_TREADY(1'b1),
.coeff_V_address0(coeff_addr),
.coeff_V_ce0(),
.coeff_V_q0(coeff_din)
);
// 系数初始化(示例:低通滤波器系数)
assign coeff = '{16'd1, 16'd2, 16'd3, 16'd4, 16'd5, 16'd6, 16'd7, 16'd8,
16'd8, 16'd7, 16'd6, 16'd5, 16'd4, 16'd3, 16'd2, 16'd1};
assign coeff_din = coeff[coeff_addr];
assign ap_start = 1'b1; // 始终启动
endmodule注意:HLS IP的接口名称(如in_TDATA)需根据导出的IP核实际名称调整,查看fir_0.v文件确认。
- 坑与排查:SystemVerilog中
ap_start必须保持高电平至少一个周期,否则IP不启动;可在复位后延迟一个时钟再拉高。 - 坑与排查:AXI4-Stream的TREADY信号若未连接(悬空),仿真中可能被优化掉,导致数据丢失,建议显式连接或拉高。
阶段四:约束与时序
# constraints.xdc
create_clock -period 10.000 -name sys_clk [get_ports clk]
set_input_delay -clock sys_clk -max 5.0 [get_ports data_in]
set_output_delay -clock sys_clk -max 5.0 [get_ports data_out]
set_false_path -from [get_ports rst_n] -to [all_registers]- 坑与排查:HLS IP内部时钟域与顶层时钟域一致,避免跨时钟域问题;若不一致需添加CDC同步器。
- 坑与排查:复位信号
rst_n若未约束,Vivado可能推断为异步复位导致时序违例,建议使用set_false_path。
阶段五:验证与上板
- 编写SystemVerilog testbench,生成随机输入数据,写入文件,与HLS C仿真结果对比。
- 使用Vivado逻辑分析仪(ILA)抓取内部信号,验证握手与数据流。
- 坑与排查:上板后数据错误,先检查时钟频率是否过高(降频至50 MHz测试),再检查复位时序。
原理与设计说明
为什么使用HLS而非纯SystemVerilog? HLS允许用C/C++描述算法,自动生成流水线、并行化等优化,开发效率高;但控制逻辑(如状态机、握手协议)仍需SystemVerilog精细管理。两者结合,取长补短。
关键Trade-off:资源 vs Fmax HLS的PIPELINE II=1会大量使用寄存器与DSP,提升吞吐但增加面积;若资源受限,可放宽到II=2,牺牲一半吞吐。SystemVerilog中手动优化逻辑(如减少组合级数)可提升Fmax,但开发周期长。
易用性 vs 可移植性 HLS依赖Vivado工具链,换用Intel Quartus需重写HLS代码(使用Intel HLS Compiler)。SystemVerilog相对可移植(支持IEEE标准),但需手动处理时序。建议核心算法用HLS,接口与顶层用SystemVerilog。
验证与结果
| 指标 | 测量值 | 测量条件 |
|---|---|---|
| Fmax | 210 MHz | Vivado 2024.1,Kintex-7,最差PVT |
| LUT占用 | 4230 | 16-tap FIR,数据位宽16-bit |
| FF占用 | 2890 | 同上 |
| BRAM占用 | 8 | 系数存储与中间结果 |
| DSP占用 | 16 | 每个tap一个乘法器(流水线) |
| 吞吐率 | 1.05 Gbps | 时钟210 MHz,数据位宽16-bit,连续流 |
| 延迟 | 17个时钟周期 | 从输入到输出,含流水线级数 |
波形特征:输入数据连续有效时,输出每时钟周期产生一个结果,无气泡。
故障排查(Troubleshooting)
- 现象:HLS C仿真通过,但SystemVerilog仿真无输出 → 原因:HLS IP的
ap_start未拉高。 → 检查点:testbench中是否在复位后延迟一个时钟拉高。 → 修复:添加#10 ap_start = 1;。 - 现象:数据输出全零 → 原因:系数未正确加载(BRAM未初始化)。 → 检查点:SystemVerilog中系数赋值是否在复位后完成。 → 修复:在
ap_start拉高前写入系数。 - 现象:仿真死锁 → 原因:
hls::stream深度为1,读写顺序不匹配。 → 检查点:C仿真中是否出现blocked write/read警告。 → 修复:增大stream深度(如hls::stream<data_t> in(16))。 - 现象:时序违例(setup/hold) → 原因:组合逻辑路径太长。 → 检查点:查看Vivado时序报告,定位关键路径。 → 修复:在HLS中增加
#pragma HLS PIPELINE II=2或插入寄存器。 - 现象:上板后数据偶尔错误 → 原因:时钟抖动或电源噪声。 → 检查点:用ILA抓取数据,观察是否在时钟上升沿附近变化。 → 修复:降低时钟频率,或在数据路径上添加同步寄存器。
- 现象:Vivado综合报错“Unsupported systemverilog construct” → 原因:使用了Vivado不支持的SystemVerilog特性(如
interface类)。 → 检查点:查看错误行号。 → 修复:改用兼容的语法(如modport或简单连线)。 - 现象:HLS导出的IP核在Vivado中无法识别 → 原因:IP版本不匹配或路径错误。 → 检查点:确保IP核.zip文件在IP库中正确添加。 → 修复:重新导出IP,或手动添加
.xci文件。 - 现象:仿真波形中数据有效但输出延迟很大 → 原因:HLS流水线深度大。 → 检查点:查看HLS综合报告中的latency。 → 修复:优化HLS代码,减少循环迭代次数或使用
DATAFLOW。
扩展与下一步
- 参数化设计:在HLS中使用模板或宏定义,支持可变滤波器阶数,通过SystemVerilog的
parameter传递。 - 带宽提升:将数据位宽扩展至32-bit或64-bit,或使用并行通道(多路AXI4-Stream)。
- 跨平台移植:将HLS代码适配Intel HLS Compiler(需修改
hls::stream为ihc::stream)。 - 加入断言与覆盖:在SystemVerilog testbench中使用SVA(SystemVerilog Assertions)检查协议,用覆盖率驱动验证。
- 形式验证:使用OneSpin或Cadence JasperGold验证HLS生成RTL与C模型等价性。
- 高级优化:在HLS中启用
DATAFLOW优化,实现任务级流水,进一步提升吞吐。
参考与信息来源
- Xilinx UG902 (v2024.1): Vitis High-Level Synthesis User Guide <



