pragma HLS top
说明
将名称附加到函数,然后可以与 set_top 命令一起使用该名称来合成该函数以及从指定顶级调用的任何函数。 这通常用于在 C/C++ 中合成类的成员函数。在活动解决方案中指定编译指示,然后使用具有新名称的 set_top 命令。
语法
将编译指示放在 C 源代码中所需位置的边界内。
#pragma HLS top name=<string>
其中, name=,指定 set_top 命令使用的名称。
示例
函数 foo_long_name 被指定为顶级函数,并重命名为 DESIGN_TOP。在代码中放置 pragma 之后,set_top 命令仍然必须从 Tcl 命令行发出,或者从 GUI 项目设置中指定的顶层发出。
void foo_long_name () { #pragma HLS top name=DESIGN_TOP ... } set_top DESIGN_TOP
函数内联
pragma HLS inline
说明
删除作为层次结构中单独实体的函数。 内联后,函数被分解到调用函数中,不再作为 RTL 中单独的层次结构出现。 在某些情况下,内联函数允许函数内的操作与周围的操作更有效地共享和优化。 内联函数不能共享。 这会增加实现 RTL 所需的面积。
INLINE 编译指示适用于它定义的范围,具体取决于它的指定方式:
INLINE
:没有参数,pragma 意味着指定它的函数应该向上内联到任何调用函数或区域。INLINE OFF
:指定在其中指定的函数不应向上内联到任何调用函数或区域中。 这将禁用可能自动内联或作为区域或递归的一部分内联的特定函数的内联。INLINE REGION
:这将编译指示应用于区域或分配它的函数体。它向下应用,内联区域或函数的内容,但不通过层次结构递归内联。INLINE RECURSIVE
:这将编译指示应用于区域或分配它的函数体。它向下应用,递归地内联区域或函数的内容。
默认情况下,内联仅在函数层次结构的下一级执行,而不是在子函数上执行。但是,递归选项允许您通过层次结构的级别指定内联。
语法
将编译指示放在函数体或代码区域内的 C 源代码中。
#pragma HLS inline <region | recursive | off>
其中,
region
:可选地指定指定区域(或包含在函数体中)的所有函数都将被内联,适用于该区域的范围。recursive
:默认情况下,只执行一级函数内联,指定函数内的函数不内联。 recursive 选项以递归方式内联指定函数或区域内的所有函数。off
:禁用函数内联以防止内联指定的函数。 例如,如果在函数中指定了递归,则此选项可以防止特定调用的函数在所有其他函数都被内联时被内联。Vivado HLS 自动内联小函数,使用带有 off 选项的 INLINE pragma 可用于防止这种自动内联。
示例
示例1
本示例内联指定区域内的所有函数,在本例中为 foo_top 的主体,但不内联这些函数内的任何低级函数。
void foo_top { a, b, c, d} { #pragma HLS inline region ...
示例2
以下示例将 foo_top 主体内的所有函数内联,通过函数层次结构向下递归内联,但函数 foo_sub 未内联。 递归编译指示放在函数 foo_top 中。 禁用内联的编译指示放在函数 foo_sub中:
foo_sub (p, q) { #pragma HLS inline off int q1 = q + 10; foo(p1,q);// foo_3 ... } void foo_top { a, b, c, d} { #pragma HLS inline region recursive ... foo(a,b);//foo_1 foo(a,c);//foo_2 foo_sub(a,d); ... }
INLINE 向下应用于函数 foo_top 的内容,但向上应用于调用 foo_sub 的代码。
示例3
本示例将 copy_output 函数内联到任何调用 copy_output 的函数或区域中。
void copy_output(int *out, int out_lcl[OSize * OSize], int output) { #pragma HLS INLINE // Calculate each work_item's result update location int stride = output * OSize * OSize; // Work_item updates output filter/image in DDR writeOut: for(int itr = 0; itr < OSize * OSize; itr++) { #pragma HLS PIPELINE out[stride + itr] = out_lcl[itr]; }
pragma HLS function_instantiate
说明
FUNCTION_INSTANTIATE指令是一种优化技术,它具有维护函数层次结构的区域优势,但提供了一个额外的强大选项:对函数的特定实例执行有针对性的局部优化。 这可以简化围绕函数调用的控制逻辑,并有可能提高延迟和吞吐量。
默认情况下:
- 函数在 RTL 中保留为单独的层次结构块。
- 同一层次结构中的所有函数实例都使用单个 RTL 实现(块)。
FUNCTION_INSTANTIATE 编译指示用于为函数的每个实例创建唯一的 RTL 实现,允许根据函数调用对每个实例进行本地优化。 该编译指示利用了当函数被调用时函数的某些输入可能是常量值的事实,并使用它来简化周围的控制结构并生成更小更优化的功能块。
char foo_sub(char inval, char incr) { #pragma HLS function_instantiate variable=incr return inval + incr; } void foo(char inval1, char inval2, char inval3, char *outval1, char *outval2, char * outval3) { *outval1 = foo_sub(inval1, 1); *outval2 = foo_sub(inval2, 2); *outval3 = foo_sub(inval3, 3); }
如果没有 FUNCTION_INSTANTIATE pragma,代码将导致 foo 中函数的所有三个实例的函数 foo_sub 的单个 RTL 实现。 函数 foo_sub 的每个实例都以相同的方式实现。 这对于函数重用和减少函数的每个实例调用所需的区域来说很好,但意味着函数内部的控制逻辑必须更加复杂,以解决每次调用 foo_sub 的变化。
在上面的代码示例中,FUNCTION_INSTANTIATE 编译指示导致函数 foo_sub 的三种不同实现,每种实现都针对 incr 参数独立优化,减少了面积并提高了函数的性能。 在 FUNCTION_INSTANTIATE 优化之后,foo_sub 被有效地转换为三个独立的函数,每个函数都针对 incr 的指定值进行了优化。
语法
将编译指示放在 C 源代码中所需位置的边界内。
#pragma HLS function_instantiate variable=<variable>
其中,variable=
定义了用作常量的函数参数的必需参数。
示例
示例1
在以下示例中,放置在函数 swInt) 中的 FUNCTION_INSTANTIATE 编译指示允许函数 swInt 的每个实例相对于 maxv 函数参数进行独立优化:
void swInt(unsigned int *readRefPacked, short *maxr, short *maxc, short *maxv){ #pragma HLS function_instantiate variable=maxv uint2_t d2bit[MAXCOL]; uint2_t q2bit[MAXROW]; #pragma HLS array partition variable=d2bit,q2bit cyclic factor=FACTOR intTo2bit<MAXCOL/16>((readRefPacked + MAXROW/16), d2bit); intTo2bit<MAXROW/16>(readRefPacked, q2bit); sw(d2bit, q2bit, maxr, maxc, maxv); }
接口综合
pragma HLS interface
描述
在基于C 的设计中,所有输入和输出操作都是通过形式函数参数在零时间内执行的。 在 RTL 设计中,这些相同的输入和输出操作必须通过设计接口中的端口执行,并且通常使用特定的 I/O(输入输出)协议进行操作。
INTERFACE pragma 指定如何在接口综合期间从函数定义创建 RTL 端口。
RTL 实现中的端口源自:
- 任何指定的功能级协议。
- 函数参数。
- 由顶级函数访问并在其作用域外定义的全局变量。
函数级协议,也称为块级 I/O 协议,提供信号来控制函数何时开始操作,并指示函数操作何时结束、空闲和准备好接受新输入。 函数级协议的实现:
由 值 ap_ctrl_none、ap_ctrl_hs 或 ap_ctrl_chain 指定。 ap_ctrl_hs 块级 I/O 协议是默认的。
与函数名称相关联。
每个函数参数都可以指定为具有自己的端口级 (I/O) 接口协议,例如有效握手 (ap_vld) 或确认握手 (ap_ack)。 为顶级函数中的每个参数创建端口级接口协议,如果函数返回一个值,则函数返回。 创建的默认 I/O 协议取决于 C 参数的类型。 在使用块级协议启动块操作后,端口级 IO 协议用于对进出块的数据进行排序。
如果访问了全局变量,但所有读写操作都是设计本地的,则在设计中创建资源。RTL 中不需要 I/O 端口。 如果预期全局变量是外部源或目标,请以与标准函数参数类似的方式指定其接口。
当 INTERFACE pragma 用于子函数时,只能使用 register 选项。子函数不支持 选项。Vivado HLS 会自动确定任何子函数使用的 I/O 协议。 您无法控制这些端口,除非指定端口是否已注册。
语法
将编译指示放在函数的边界内。
#pragma HLS interface <mode> port=<name> bundle=<string> \ register register_mode=<mode> depth=<int> offset=<string> \ clock=<string> name=<string> \ num_read_outstanding=<int> num_write_outstanding=<int> \ max_read_burst_length=<int> max_write_burst_length=<int>
其中,
:指定函数参数、函数使用的全局变量或块级控制协议的接口协议模式。 模式可以指定为以下之一:
port=
:指定 INTERFACE pragma 应用的函数参数、函数返回或全局变量的名称。块级 I/O 协议(ap_ctrl_none、ap_ctrl_hs 或 ap_ctrl_chain)可以分配给函数返回值的端口。- bundle=:将函数参数分组到 AXI 接口端口中。 默认情况下,Vivado HLS 将所有指定为 AXI4-Lite (s_axilite) 接口的函数参数分组到单个 AXI4-Lite 端口中。 类似地,指定为 AXI4 (m_axi) 接口的所有函数参数都分组到单个 AXI4 端口中。 此选项将具有相同
bundle=
的所有接口端口显式分组到相同的 AXI 接口端口中,并将 RTL 端口命名为指定的值。
register
:注册信号和任何相关协议信号的可选关键字,并使信号至少持续到函数执行的最后一个周期。 此选项适用于以下接口模式:
○ ap_none ○ ap_ack ○ ap_vld ○ ap_ovld
○ ap_hs ○ ap_stable ○ axis ○ s_axilite
- register_mode= :与 register 关键字一起使用,此选项指定寄存器是否放置在正向路径(TDATA 和 TVALID)、反向路径 (TREADY)、两条路径(TDATA、TVALID、 和 TREADY),或者如果没有任何端口信号要寄存(关闭)。 默认 register_mode 是 both。 AXI-Stream(axis)侧通道信号被认为是数据信号,只要寄存TDATA 就会寄存。
depth=
:指定测试台要处理的最大样本数。 此设置指示 Vivado HLS 为 RTL 协同仿真创建的验证适配器中所需的 FIFO 的最大大小。虽然深度通常是一个选项,但只有m_axi 接口需要配置。offset=
:控制 AXI4-Lite (s_axilite) 和 AXI4 (m_axi) 接口中的地址偏移。- ○ 对于 s_axilite 接口,
指定寄存器映射中的地址。
○ 对于 m_axi 接口,指定以下值之一:
- direct:生成标量输入偏移端口。
- slave:生成偏移端口并自动将其映射到 AXI4-Lite 从接口。
- off:不生成偏移端口。
- config_interface 命令的 -m_axi_offset 选项全局控制设计中所有 M_AXI 接口的偏移端口。
clock=
:可选择仅为接口模式 s_axilite 指定。 这定义了用于接口的时钟信号。 默认情况下,AXI-Lite 接口时钟与系统时钟相同。 此选项用于为 AXI-Lite (s_axilite) 接口指定单独的时钟。如果捆绑选项用于将多个顶级函数参数分组到单个 AXI-Lite 接口中,则只需在捆绑成员之一上指定时钟选项。- num_read_outstanding=:对于 AXI4 (m_axi) 接口,此选项指定在设计停止之前可以向 AXI4 总线发出多少个读取请求,而没有响应。这意味着设计中的内部存储,大小为 FIFO:
num_read_outstanding*max_read_burst_length*word_size。
num_write_outstanding=
:对于 AXI4 (m_axi) 接口,此选项指定在设计停止之前可以向 AXI4 总线发出多少次写入请求而没有响应。 这意味着设计中的内部存储,大小为 FIFO:
num_write_outstanding*max_write_burst_length*word_size
max_read_burst_length=:对于 AXI4 (m_axi) 接口,此选项指定在突发传输期间读取的最大数据值数。
max_write_burst_length=:对于 AXI4 (m_axi) 接口,此选项指定在突发传输期间写入的最大数据值数。
name=:此选项用于根据您自己的规范重命名端口。生成的 RTL 端口将使用此名称。
示例
示例1
在此示例中,两个函数参数均使用 AXI4-Stream 接口实现:
void example(int A[50], int B[50]) { //Set the HLS native interface types #pragma HLS INTERFACE axis port=A #pragma HLS INTERFACE axis port=B int i; for(i = 0; i < 50; i++){ B[i] = A[i] + 5; } }
示例2
下面关闭块级 I/O 协议,并赋给函数返回值:
#pragma HLS interface ap_ctrl_none port=return
函数参数 InData 被指定为使用 ap_vld 接口,并且还指示应该寄存器输入:
#pragma HLS interface ap_vld register port=InData
这将全局变量 lookup_table 设置为 RTL 设计上的端口,具有 ap_memory 接口:
pragma HLS interface ap_memory port=lookup_table
示例3
本示例定义了顶级转置功能端口的 INTERFACE 标准。 请注意使用 bundle= 选项对信号进行分组。
// TOP LEVEL - TRANSPOSE void transpose(int* input, int* output) { #pragma HLS INTERFACE m_axi port=input offset=slave bundle=gmem0 #pragma HLS INTERFACE m_axi port=output offset=slave bundle=gmem1 #pragma HLS INTERFACE s_axilite port=input bundle=control #pragma HLS INTERFACE s_axilite port=output bundle=control #pragma HLS INTERFACE s_axilite port=return bundle=control #pragma HLS dataflow
pragma HLS protocol
说明
protocol pragma将代码的一个区域指定为协议区域,除非代码中明确指定,否则Vivado HLS不会在其中插入时钟操作。协议区域可用于手动指定接口协议,以确保最终设计可连接到具有相同I/O协议的其他硬件块。
Vivado HLS不会在操作之间插入任何时钟,包括从函数参数读取或写入的操作,除非在代码中明确指定。因此,RTL遵循读写顺序。
可以指定时钟操作:
- 在C语言中,使用ap_wait()语句(包括ap_utils.h)。
- 在C++和SystemC中使用WaIT()语句(包括System.h)进行设计。
ap_wait和wait语句对C和C++设计的仿真没有影响。它们仅由Vivado HLS进行解释。
要创建C代码的区域,请执行以下操作:
- 将区域用大括号括起来,{},
- 可以选择将其命名以提供标识符。
例如,下面定义了一个名为io_section的区域:
io_section:{ ... }
语法
将pragma放置在区域边界内,以定义该区域的协议。
#pragma HLS protocol <floating | fixed>
其中:
- floating:协议模式,允许协议区域外的语句与最终RTL中协议区域内的语句重叠。协议区域内的代码保持周期精确,但其他操作可能同时发生。这是默认的协议模式。
- fixed:协议模式,确保协议区域内外的语句没有重叠。
示例
本示例将区域 io_section 定义为固定协议区域。 将编译指示放在里面的区域:
io_section:{ #pragma HLS protocol fixed ... }
任务级流水
pragma HLS dataflow
说明
DATAFLOW pragma 支持任务级流水线,允许函数和循环在其操作中重叠,增加 RTL实现的并发性,并增加设计的整体吞吐量。
所有操作都在 C 描述中按顺序执行。 在没有任何限制资源的指令(例如 pragma HLS 分配)的情况下,Vivado HLS 寻求最小化延迟并提高并发性。 但是,数据依赖性可以限制这一点。 例如,访问数组的函数或循环必须在完成之前完成对数组的所有读/写访问。 这可以防止下一个消耗数据的函数或循环开始操作。 DATAFLOW 优化使函数或循环中的操作能够在前一个函数或循环完成其所有操作之前开始操作。
当指定 DATAFLOW pragma 时,Vivado HLS 分析顺序函数或循环之间的数据流并创建通道(基于乒乓 RAM 或 FIFO),允许消费者函数或循环在生产者函数或循环完成之前开始操作。
这允许函数或循环并行运行,从而减少延迟并提高 RTL 的吞吐量。如果未指定启动间隔(一个函数或循环开始与下一个循环之间的循环数),Vivado HLS 会尝试最小化启动间隔并在数据可用时立即开始操作。
config_dataflow 命令指定用于数据流优化的默认内存通道和 FIFO 深度。为了使 DATAFLOW 优化起作用,数据必须从一个任务流向下一个任务。 以下编码风格会阻止 Vivado HLS 执行 DATAFLOW 优化:
- 单一生产者-消费者违规。
- 绕过任务。
- 任务之间的反馈。
- 有条件地执行任务。
- 具有多个退出条件的循环。
如果存在这些编码风格中的任何一种,Vivado HLS 会发出一条消息并且不执行 DATAFLOW 优化。最后,DATAFLOW 优化没有分层实现。 如果子函数或循环包含可能受益于 DATAFLOW 优化的其他任务,则必须将优化应用于循环、子函数或内联子函数。
语法
将编译指示放在区域、函数或循环的边界内的 C 源代码中。
#pragma HLS dataflow
示例
在循环 wr_loop_j 中指定 DATAFLOW 优化。
wr_loop_j: for (int j = 0; j < TILE_PER_ROW; ++j) { #pragma HLS DATAFLOW wr_buf_loop_m: for (int m = 0; m < TILE_HEIGHT; ++m) { wr_buf_loop_n: for (int n = 0; n < TILE_WIDTH; ++n) { #pragma HLS PIPELINE // should burst TILE_WIDTH in WORD beat outFifo >> tile[m][n]; } } wr_loop_m: for (int m = 0; m < TILE_HEIGHT; ++m) { wr_loop_n: for (int n = 0; n < TILE_WIDTH; ++n) { #pragma HLS PIPELINE outx[TILE_HEIGHT*TILE_PER_ROW*TILE_WIDTH*i +TILE_PER_ROW*TILE_WIDTH*m+TILE_WIDTH*j+n] = tile[m][n]; } } }
pragma HLS stream
说明
- 默认情况下,数组变量实现为RAM。
- 顶层函数数组参数实现为RAM接口端口。
- 通用阵列作为RAM实现读写访问。
- 在涉及 DATAFLOW 优化的子函数中,实现了数组参数使用 RAM 乒乓缓冲通道。
- 基于循环的 DATAFLOW 优化中,涉及的数组作为 RAM 实现乒乓缓冲通道。
如果存储在阵列中的数据以顺序方式使用或生成,则更有效的通信机制是使用 STREAM pragma 指定的流数据,其中使用 FIFO 而不是 RAM。当顶层函数的参数指定为 INTERFACE 类型 ap_fifo 时,该数组将自动实现为流。
语法
将编译指示放在 C 源代码中所需位置的边界内。
#pragma HLS stream variable=<variable> depth=<int> dim=<int> off
其中:
variable=
:指定要实现为流接口的数组的名称。- depth=:仅与DATAFLOW 通道中的数据流相关。 默认情况下,RTL 中实现的 FIFO 深度与 C 代码中指定的数组大小相同。 此选项可让您修改 FIFO 的大小并指定不同的深度。当数组在 DATAFLOW 区域中实现时,通常使用 depth= 选项来减小 FIFO 的大小。 例如,在 DATAFLOW 区域中,当所有循环和函数都以 II=1 的速率处理数据时,不需要大的 FIFO,因为每个时钟周期都会产生和消耗数据。 在这种情况下,可以使用 depth= 选项将 FIFO 大小减少到 1,以显着减少 RTL 设计的面积。config_dataflow -depth 命令提供了流式传输 DATAFLOW 区域中的所有数组的能力。 此处指定的 depth= 选项会覆盖已分配变量的 config_dataflow 命令。
dim=
:指定要流式传输的数组的维度。 默认值为维度 1。对于具有 N 维的数组,指定为从 0 到 N 的整数。off
:禁用流数据。 仅与数据流通道中的数组流相关。- config_dataflow -default_channel fifo 命令全局表示设计中所有阵列上的 STREAM 编译指示。 此处指定的 off 选项会覆盖分配变量的 config_dataflow 命令,并恢复使用基于 RAM 乒乓缓冲区的通道的默认值。
示例
示例1
以下示例指定要流式传输的数组 A[10],并作为 FIFO 实现:
#pragma HLS STREAM variable=A
示例2
在此示例中,数组 B 设置为 FIFO 深度为 12 的流数据:
#pragma HLS STREAM variable=B depth=12
示例3
阵列 C 已禁用流式传输。
在本例中假定由 config_dataflow 启用:
#pragma HLS STREAM variable=C off