写在前面
本文记录了HLS的所使用的大部分指令,参考UG1270,对每个指令进行讲解,并有相关示例。
内核优化
pragma HLS allocation
说明
指定实例限制以限制已实现内核中的资源分配。这定义并可以限制用于实现特定功能、循环、操作或内核的 RTL 实例和硬件资源的数量。 ALLOCATION pragma 在函数体、循环或代码区域内指定。
例如,如果 C 源代码有四个函数 foo_sub 的实例,则 ALLOCATION pragma 可以确保最终 RTL 中只有一个 foo_sub 实例。 C 函数的所有四个实例都使用相同的 RTL 块实现。 这会减少功能使用的资源,但会对性能产生负面影响。
C 代码中的操作,例如加法、乘法、数组读取和写入,可以受 ALLOCATION 编译指示的限制。 在综合期间映射到的内核可以以与运算符相同的方式进行限制。 您可以选择限制组合乘法器内核的数量,而不是限制乘法运算的总数,从而强制使用流水线乘法器执行任何剩余的乘法(反之亦然)。
ALLOCATION pragma 适用于它在其中指定的范围:函数、循环或代码区域。 但是,您可以使用 config_bind 命令的 -min_op 参数在整个设计中全局最小化运算符。
语法
将 pragma 放在将应用的函数、循环或区域的主体内。
#pragma HLS allocation instances=<list> limit=<value> <type>
其中:
instances=
:指定函数、运算符或内核的名称。limit=
:可选地指定要在内核中使用的实例的限制。:指定分配应用于用于创建设计(例如加法器、乘法器、流水线乘法器和块 RAM)的功能、操作或内核(硬件组件)。 类型指定为以下之一:
示例
示例1
给定具有多个函数 foo 实例的设计,此示例将硬件内核的 RTL 中的 foo 实例数限制为 2。
#pragma HLS allocation instances=foo limit=2 function
示例2
将函数 my_func 的实现中使用的乘法器操作数限制为 1。此限制不适用于 my_func 之外的任何乘法器,或可能驻留在 my_func 子函数中的乘法器。
void my_func(data_t angle) { #pragma HLS allocation instances=mul limit=1 operation ... }
pragma HLS clock
说明
将命名时钟应用于指定函数。
C 和 C++ 设计仅支持单个时钟。 create_clock 指定的时钟周期应用于设计中的所有功能。SystemC 设计支持多个时钟。 可以使用 create_clock 命令指定多个命名时钟,并使用 pragma HLS 时钟应用于各个 SC_MODULE。每个 SC_MODULE 都使用单个时钟进行综合。
语法
将编译指示放在函数体内的 C 源代码中。
#pragma HLS clock domain=<clock>
其中,domain=
,指定时钟名称。
示例
示例1
假设一个 SystemC 设计,其中顶层 foo_top 具有时钟端口 fast_clock 和 slow_clock。 但是, foo_top 在其函数中仅使用 fast_clock。 子块 foo_sub 仅使用 slow_clock。在此示例中,在启动 Vivado HLS 工具时指定的 script.tcl 文件中指定了以下 create_clock 命令:
create_clock -period 15 fast_clk create_clock -period 60 slow_clk
然后在 C 源文件中指定以下编译指示以将时钟分配给指定的函数 foo_sub 和 foo_top:
foo_sub (p, q) { #pragma HLS clock domain=slow_clock ... } void foo_top { a, b, c, d} { #pragma HLS clock domain=fast_clock ...
pragma HLS expression_balance
说明
有时,基于 C 的规范是用一系列操作编写的,从而导致 RTL 中的一长串操作。 如果时钟周期较短,这会增加设计中的延迟。
默认情况下,Vivado HLS 使用关联和交换属性重新排列操作。
这种重新排列创建了一个平衡树,可以缩短链,潜在地以额外硬件为代价减少设计中的延迟。EXPRESSION_BALANCE 编译指示允许在指定范围内禁用或明确启用此表达式平衡。
语法
将编译指示放在 C 源代码中所需位置的边界内。
#pragma HLS expression_balance off
其中,off
:在此位置关闭表达式平衡。将此选项排除在编译指示之外会启用表达式平衡,这是默认模式。
示例
示例1
此示例在函数 my_Func 中显式启用表达式平衡:
void my_func(char inval, char incr) { #pragma HLS expression_balance
示例2
在函数 my_Func 中禁用表达式平衡:
void my_func(char inval, char incr) { #pragma HLS expression_balance off
pragma HLS latency
说明
为函数、循环和区域的完成指定最小或最大延迟值或两者。 延迟定义为产生输出所需的时钟周期数。函数延迟是函数计算所有输出值并返回所需的时钟周期数。 循环延迟是执行循环的所有迭代的周期数。
Vivado HLS 始终尝试将设计中的延迟降至最低。 当指定 LATENCY pragma 时,工具行为如下:
- 延迟大于最小值或小于最大值:满足约束。 没有执行进一步的优化。
- 延迟小于最小值:如果 Vivado HLS 可以实现小于最小指定延迟,它会将延迟扩展到指定值,从而可能增加共享。
- 延迟大于最大值:如果 Vivado HLS 无法在最大限制内进行调度,则会增加实现指定约束的工作量。 如果它仍然无法满足最大延迟,它会发出警告,并生成超过最大延迟的最小可实现延迟的设计。
语法
将pragma放在必须管理延迟的函数、循环或代码区域的边界内。
#pragma HLS latency min=<int> max=<int>
其中:
- min=:可选地指定函数、循环或代码区域的最小延迟。
- max=:可选地指定函数、循环或代码区域的最大延迟。
尽管最小值和最大值都是可选的,但必须指定一个。
示例
示例1
函数foo的最小延迟为4,最大延迟为8:
int foo(char x, char a, char b, char c) { #pragma HLS latency min=4 max=8 char y; y = x*a+b+c; return y }
示例2
以下示例中的指定循环_1的最大延迟为12。将pragma放置在循环体中,如图所示:
void foo (num_samples, ...) { int i; ... loop_1: for(i=0;i< num_samples;i++) { #pragma HLS latency max=12 ... result = a + b; } }
示例3
以下示例通过指定零延迟创建代码区域并对需要在同一时钟周期内更改的信号进行分组:
// create a region { } with a latency = 0 { #pragma HLS LATENCY max=0 min=0 *data = 0xFF; *data_vld = 1; }
pragma HLS reset
说明
添加或删除特定状态变量(全局或静态)的重置。复位端口在FPGA中用于在任何时候应用复位信号时将连接到复位端口的寄存器和块RAM恢复为初始值。RTL重置端口的存在和行为由config_RTL配置文件控制。重置设置包括设置重置极性的能力,并指定重置是同步还是异步,但更重要的是,它通过重置选项控制在应用重置信号时重置哪些寄存器。
通过 RESET pragma 可以更好地控制复位。 如果变量是静态或全局变量,则 RESET 编译指示用于显式添加重置,或者可以通过关闭编译指示将变量从重置中移除。 当设计中存在静态或全局数组时,这可能特别有用。
语法
将pragma放在变量生命周期边界内的C源代码中。
#pragma HLS reset variable=<a> off
其中:
variable=
:指定应用pragma的变量。off
:表示未为指定变量生成重置。
示例
示例1
此示例将reset添加到函数foo中的变量a,即使全局重置设置为none或control:
void foo(int in[3], char a, char b, char c, int out[3]) { #pragma HLS reset variable=a
示例2
即使全局重置设置为state或all,也会从函数foo中的变量a中移除重置。
void foo(int in[3],char a,char b,char c,int out[3]){ #pragma HLS reset variable=a off
pragma HLS resource
说明
指定特定的库资源(核心)用于在RTL中实现变量(数组、算术运算或函数参数)。如果未指定资源pragma,Vivado HLS将确定要使用的资源。Vivado HLS使用硬件内核实现代码中的操作。当库中的多个核心可以实现该操作时,可以指定要与资源pragma一起使用的核心。要生成可用核心的列表,请使用list_core命令。
list_core命令用于获取库中可用核心的详细信息。list_core只能在Vivado HLS Tcl命令界面中使用,必须使用set_part命令指定Xilinx设备。如果未选择设备,则list_core命令不起任何作用。
例如,要指定库中用于实现数组的内存元素,请使用资源pragma。这使您可以控制阵列是作为单端口RAM还是双端口RAM实现。这种用法对于顶级函数接口上的数组非常重要,因为与数组关联的内存类型决定了RTL中所需的端口。
可以使用latency=选项指定内核的延迟。对于接口上的块RAM,latency=选项允许您在接口上建模片外非标准SRAM,例如支持延迟为2或3的SRAM。对于内部操作,latency=选项允许使用更多流水线阶段来实现操作。这些额外的流水阶段可以帮助解决RTL合成过程中的时间问题。要使用latency=选项,操作必须具有可用的多级内核。Vivado HLS为所有基本算术运算(加、减、乘、除)、所有浮点运算和所有块RAM提供多级内核。
为获得最佳结果,赛灵思建议您对 C 使用 -std=c99,对 C 和 C++ 使用 -fno-builtin。 要指定 C 编译选项,例如 -std=c99,请使用带有 -cflags 选项的 Tcl 命令 add_files。 或者,使用项目设置中的编辑 CFLAG 按钮 对话框。
语法
将pragma放在定义变量的函数体中的C源代码中。
#pragma HLS resource variable=<variable> core=<core> latency=<int>
其中,
variable=
:指定要将资源pragma分配到的数组、算术运算或函数参数的必需参数。core=
:指定核心的必需参数,如技术库中所定义。latency=
:指定核心的延迟。
示例
示例1
在下面的示例中,指定了一个2级流水线乘法器来实现函数foo的变量c的乘法。由Vivado HLS决定变量d使用哪个内核。
int foo (int a, int b) { int c, d; #pragma HLS RESOURCE variable=c latency=2 c = a*b; d = a*c; return d; }
示例2
在下面的示例中,变量coefs[128]是顶级函数foo_top的参数。此示例指定使用库中的核心RAM_1P实现系数:
#pragma HLS resource variable=coeffs core=RAM_1P
在RTL中创建的用于访问系数值的端口在RAM_1P核心中定义。