目录
1. 简介
Vitis HLS 工具可以将现有的 Verilog RTL IP(即硬件描述语言编写的模块)集成到 C/C++ HLS 项目中。通过这种方式,Vitis HLS 能够将 RTL 代码与 C/C++ 代码一起综合,形成最终的硬件设计。
RTL 黑盒允许设计者在 HLS 设计中的特定区域(如顺序区域、流水线区域或数据流区域)内使用 Verilog 或 VHDL 编写的 RTL IP。这样做的好处是可以重用现有的硬件模块,同时利用 HLS 的优势来加速整个设计和开发过程。
简单的说,如果你有一个用 Verilog 编写的性能优化好的 IP,可以将其作为黑盒插入到 HLS 项目中,而不需要将其重新用 C/C++ 实现。这样可以节省时间,并确保硬件设计的高效性和可靠性。
2. 用法详解
2.1 需要的文件
2.1.1 RTL 函数签名
RTL 代码的 C 语言函数签名,是指用于代表RTL模块的C函数的声明。这个签名定义了函数的名称、输入参数和返回类型,这样C代码就可以调用它,就像调用任何其他C函数一样。这个签名通常放在一个头文件(.h)中,以便在整个项目中使用。
#include "ap_int.h"
//--------------------------------------------------------
//RTL 代码的 C 语言函数签名
//--------------------------------------------------------
void rtl_model(ap_int<10> a1, ap_int<10> a2, ap_int<10> a3, ap_int<10> a4,
ap_int<10> b1, ap_int<10> b2, ap_int<10> b3, ap_int<10> b4,
ap_int<10>& z1, ap_int<10>& z2, ap_int<10>& z3, ap_int<10>& z4);
//--------------------------------------------------------
void example(ap_int<10> a1, ap_int<10> a2, ap_int<10> a3, ap_int<10> a4,
ap_int<10> b1, ap_int<10> b2, ap_int<10> b3, ap_int<10> b4,
ap_int<10>& sigma) {
ap_int<10> tmp1, tmp2, tmp3, tmp4;
rtl_model(a1, a2, a3, a4, b1, b2, b3, b4, tmp1, tmp2, tmp3, tmp4);
sigma = tmp1 + tmp2 + tmp3 + tmp4;
}
2.1.2 黑盒 JSON 描述文件
{
"c_function_name" : "rtl_model",
"rtl_top_module_name" : "rtl_model",
"c_files" : [{
"c_file" : "rtl_model.cpp",
"cflag" : ""
}],
"rtl_files" : [
"rtl_model.v"
],
"c_parameters" : [{
"c_name" : "a1",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "a1"
}
},
{
"c_name" : "a2",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "a2"
}
},
{
"c_name" : "a3",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "a3"
}
},
{
"c_name" : "a4",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "a4"
}
},{
"c_name" : "b1",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "b1"
}
},
{
"c_name" : "b2",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "b2"
}
},
{
"c_name" : "b3",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "b3"
}
},
{
"c_name" : "b4",
"c_port_direction" : "in",
"rtl_ports" : {
"data_read_in" : "b4"
}
},
{
"c_name" : "z1",
"c_port_direction" : "out",
"rtl_ports" : {
"data_write_out" : "z1",
"data_write_valid" : "z1_ap_vld"
}
},
{
"c_name" : "z2",
"c_port_direction" : "out",
"rtl_ports" : {
"data_write_out" : "z2",
"data_write_valid" : "z2_ap_vld"
}
},
{
"c_name" : "z3",
"c_port_direction" : "out",
"rtl_ports" : {
"data_write_out" : "z3",
"data_write_valid" : "z3_ap_vld"
}
},
{
"c_name" : "z4",
"c_port_direction" : "out",
"rtl_ports" : {
"data_write_out" : "z4",
"data_write_valid" : "z4_ap_vld"
}
}],
"rtl_common_signal" : {
"module_clock" : "ap_clk",
"module_reset" : "ap_rst",
"module_clock_enable" : "ap_ce",
"ap_ctrl_chain_protocol_idle" : "ap_idle",
"ap_ctrl_chain_protocol_start" : "ap_start",
"ap_ctrl_chain_protocol_ready" : "ap_ready",
"ap_ctrl_chain_protocol_done" : "ap_done",
"ap_ctrl_chain_protocol_continue" : "ap_continue"
},
"rtl_performance" : {
"latency" : "2",
"II" : "1"
},
"rtl_resource_usage" : {
"FF" : "0",
"LUT" : "0",
"BRAM" : "0",
"URAM" : "0",
"DSP" : "1"
}
}
2.1.3 RTL IP 文件
`timescale 100ps/100ps
(* use_dsp = "simd" *)
(* dont_touch = "1" *)
module rtl_model (input ap_clk, ap_rst, ap_ce, ap_start, ap_continue,
input [9:0] a1, a2, a3, a4, b1, b2, b3, b4,
output ap_idle, ap_done, ap_ready,
output z1_ap_vld, z2_ap_vld, z3_ap_vld, z4_ap_vld,
output reg [9:0] z1, z2, z3, z4);
wire ce = ap_ce;
reg [9:0] areg1, areg2, areg3, areg4;
reg [9:0] breg1, breg2, breg3, breg4;
reg dly1, dly2;
always @ (posedge ap_clk)
if (ap_rst)
begin
z1 <= 0;
z2 <= 0;
z3 <= 0;
z4 <= 0;
areg1 <= 0;
areg2 <= 0;
areg3 <= 0;
areg4 <= 0;
breg1 <= 0;
breg2 <= 0;
breg3 <= 0;
breg4 <= 0;
dly1 <= 0;
dly2 <= 0;
end
else if (ce)
begin
z1 <= areg1 + breg1;
z2 <= areg2 + breg2;
z3 <= areg3 + breg3;
z4 <= areg4 + breg4;
areg1 <= a1;
areg2 <= a2;
areg3 <= a3;
areg4 <= a4;
breg1 <= b1;
breg2 <= b2;
breg3 <= b3;
breg4 <= b4;
dly1 <= ap_start;
dly2 <= dly1;
end
assign z1_ap_vld = dly2;
assign z2_ap_vld = dly2;
assign z3_ap_vld = dly2;
assign z4_ap_vld = dly2;
assign ap_ready = dly2;
assign ap_done = dly2;
assign ap_idle = ~ap_start;
endmodule // rtl_model
2.2 使用 RTL 黑盒向导
RTL 黑盒向导用于将 C/C++ 文件“加密”成 RTL 语言。
整体步骤和常规的 Vitis HLS 步骤一致,特殊点在于多了一个 JSON 文件需要配置:
- 从顶层函数内或者从 Vitis HLS 工程的子函数内调用 C 语言函数签名。
- 在 Vitis HLS IDE 中使用“Add Files”(添加文件),将黑盒 JSON 描述文件添加到 HLS 工程中。
- 运行 Vitis HLS 设计文件照常进行仿真、综合和协同仿真。
使用 RTL 黑盒向导操作步骤:
在导航到工程中,打开 RTL 黑盒向导:
//--------------------------------------------------------
//rtl_model.cpp
//--------------------------------------------------------
#include "example.h"
//--------------------------------------------------------
void rtl_model(data_t a1, data_t a2, data_t a3, data_t a4, data_t b1, data_t b2,
data_t b3, data_t b4, data_t& z1, data_t& z2, data_t& z3,
data_t& z4) {
#pragma HLS inline off
z1 = a1 + b1;
z2 = a2 + b2;
z3 = a3 + b3;
z4 = a4 + b4;
}
添加 rtl_model.cpp 到对话框中,需要选中其中 rtl_model 函数,可得以下内容:
参数解释:
- C Function:即,C 语言函数;用于指定 RTL IP 的 C 语言函数名称。
- C Argument Name:即,C 语言实参名称;用于指定函数实参的名称。这些名称应与 IP 上的端口相关。
- C Argument Type:即,C 语言实参类型;用于指定用于每个实参的数据类型。
- C Port Direction:即,C 语言端口方向;用于指定实参的端口方向,对应于 IP 中的端口。
- RAM Type:即,RAM 类型;用于指定在接口处使用的 RAM 类型。
- RTL Group Configuration:即,RTL 组配置;用于指定对应的 RTL 信号名称。
上述对话框中,RTL Group Configuration 一列,已经全部设置完毕,但是GUI界面并未显示:
3. 总结
Vitis HLS 工具通过集成 Verilog RTL IP 到 C/C++ HLS 项目中,简化了硬件设计过程。RTL 黑盒技术允许设计者在特定区域内使用已有的 Verilog 或 VHDL 编写的 RTL 模块,从而重用优化好的硬件模块,避免重新实现,提高设计效率和可靠性。使用 Vitis HLS 时,设计者需要编写 RTL 函数签名、创建黑盒 JSON 描述文件并包含 RTL IP 文件。在 Vitis HLS 中添加这些文件后,运行仿真和综合步骤即可完成集成。此外,通过 RTL 黑盒向导,可以简化 C 函数与 RTL 信号的映射过程。