目录
1. 简介
函数例化(Function Instantiation)是 Vitis HLS 中的一个高级优化技术。它允许开发者在保持函数层次结构的同时,对函数的特定实例进行局部优化。如果函数的某些输入参数在调用时是已知的常量,那么可以利用这些信息来简化函数的控制逻辑,从而可能改善延迟和吞吐量。
2. 功能分析
在 Vitis HLS 中的函数,在没有特别指定编译指示的情况下,HLS 工具会遵循以下原则进行处理:
- 函数保留为独立层级块:每个函数在生成的RTL(寄存器传输级)代码中都会作为一个独立的模块存在。这有助于保持设计的模块化,使得每个函数都可以独立地被验证和复用。
- 函数分解(或内联)到更高层次的函数中:如果HLS工具决定将一个函数内联到另一个函数中,那么在RTL代码中,原本独立的函数将不再作为单独的模块存在。相反,它的逻辑会被合并到调用它的函数中。这通常是为了优化性能,减少函数调用的开销。
- 所有实例使用单一RTL实现:如果一个函数被多次调用,每个调用实例在默认情况下都会共享相同的RTL代码块。这有助于减少生成的硬件资源的数量,因为不需要为每个函数调用创建单独的硬件实现。
Function Instantiation 语法:
#pragma HLS FUNCTION_INSTANTIATE variable=<variable>
其中,variable=<variable>:这是必需的实参,用于定义要用作为常量的函数实参。
FUNCTION_INSTANTIATE 编译指示用于为函数的每个实例创建唯一的 RTL 实现,允许根据函数调用对每个实例进行局部最优化。鉴于调用函数时部分函数输入可能是常量,此编译指示可藉此简化周围控制结构,并生成进一步优化的、更小的函数块。
3. 示例分析
3.1 不使用 FUNCTION_INSTANTIATE
char foo(char inval, char incr) {
#pragma HLS INLINE OFF
//#pragma HLS FUNCTION_INSTANTIATE variable = incr
return inval + incr;
}
void top(char inval1, char inval2, char inval3, char* outval1, char* outval2,
char* outval3) {
*outval1 = foo(inval1, 0);
*outval2 = foo(inval2, 1);
*outval3 = foo(inval3, 100);
}
经过综合,得到如下文件结构:
可以发现,代码会为 top 中的函数的全部 3 个实例生成函数 foo 的单一 RTL 实现。函数 foo 的每个实例都是以相同方式实现的。这对于函数复用并无影响,并且可以减少函数的每次实例调用所需的面积,但是函数内部的控制逻辑必须更复杂,以便应对每次调用 foo 时产生的变化。
如下代码可以看到每次函数调用的情况:
...
example_foo tmp_foo_fu_79(
.ap_ready(tmp_foo_fu_79_ap_ready),
.inval(inval1),
.incr(6'd0),
.ap_return(tmp_foo_fu_79_ap_return)
);
example_foo tmp_1_foo_fu_88(
.ap_ready(tmp_1_foo_fu_88_ap_ready),
.inval(inval2),
.incr(6'd1),
.ap_return(tmp_1_foo_fu_88_ap_return)
);
example_foo tmp_2_foo_fu_97(
.ap_ready(tmp_2_foo_fu_97_ap_ready),
.inval(inval3),
.incr(6'd36),
.ap_return(tmp_2_foo_fu_97_ap_return)
);
always @ (*) begin
if ((ap_start == 1'b1)) begin
outval1_ap_vld = 1'b1;
end else begin
outval1_ap_vld = 1'b0;
end
end
always @ (*) begin
if ((ap_start == 1'b1)) begin
outval2_ap_vld = 1'b1;
end else begin
outval2_ap_vld = 1'b0;
end
end
always @ (*) begin
if ((ap_start == 1'b1)) begin
outval3_ap_vld = 1'b1;
end else begin
outval3_ap_vld = 1'b0;
end
end
assign ap_done = ap_start;
assign ap_idle = 1'b1;
assign ap_ready = ap_start;
assign outval1 = tmp_foo_fu_79_ap_return;
assign outval2 = tmp_1_foo_fu_88_ap_return;
assign outval3 = tmp_2_foo_fu_97_ap_return;
endmodule //example
3.2 使用 FUNCTION_INSTANTIATE
char foo(char inval, char incr) {
#pragma HLS INLINE OFF
#pragma HLS FUNCTION_INSTANTIATE variable = incr
return inval + incr;
}
void top(char inval1, char inval2, char inval3, char* outval1, char* outval2,
char* outval3) {
*outval1 = foo(inval1, 0);
*outval2 = foo(inval2, 1);
*outval3 = foo(inval3, 100);
}
经过综合,得到如下文件结构:
可以发现,代码会为 top 中的函数的由 3 个独立的实例生成函数 foo 的 RTL 实现:example_foo_0.v、example_foo_1.v、example_foo_2.v。
在以上代码样本中:
#pragma HLS FUNCTION_INSTANTIATE variable = incr
FUNCTION_INSTANTIATE 编译指示会生成函数 foo 的 3 个不同实现,每个实现都会按incr 指定值进行最优化,从而减少面积并改善函数实现的性能。
提示:Vitis HLS 工具会将小函数自动分解(或内联)到更高层次的调用函数中。即使对于函数例化也同样如此。将 INLINE 编译指示与 OFF 选项搭配使用即可阻止此自动内联操作。
4. 总结
函数例化是Vitis HLS中的一种高级优化技术,它允许开发者在保持函数层次结构的同时,对特定函数实例进行局部优化。这种技术通过利用编译时已知的常量输入参数,简化函数的控制逻辑,从而可能改善延迟和吞吐量。默认情况下,函数在RTL中作为独立层级块保留,或者分解到更高层次的函数中,所有实例共享单一RTL实现。通过使用FUNCTION_INSTANTIATE编译指示,可以为每个函数调用创建唯一的RTL实现,允许针对每个实例进行局部最优化。这样,即使原始函数包含复杂的控制逻辑,每个实例化的函数也可以被优化以仅包含必要的逻辑,减少硬件资源消耗并提高性能。