HLS新手入门教程

文章目录

  • HLS学习笔记
    • 1. 什么是HLS
    • 2. HLS开发流程
    • 3. HLS基本语法
      • 3.1. #pragma HLS
      • 3.2. HLS数据类型
      • 3.3. HLS模块定义
      • 3.4. 数组分区
      • 3.5. 流水线优化
      • 3.6. 组合逻辑优化
      • 3.7. 一些基本概念
      • 3.8. 完整示例
        • 3.8.1. 矩阵乘法
        • 3.8.2. 函数调用和循环
        • 3.8.3. 流水线和并行化指令
    • 4. HLS高级语法
      • 4.1. 定点数和浮点数支持
      • 4.2. 数据重用
      • 4.3. #pragma HLS interface
      • 4.4. #pragma HLS design
      • 4.5. 流式处理器
      • 4.6. #pragma HLS dataflow
      • 4.7. #pragma HLS reset variable
    • 5. HLS技巧
    • 6. 总结
    • 7. 资料来源

HLS学习笔记

1. 什么是HLS

HLS是一种高级综合技术,它允许开发人员使用高级语言(如C、C++和SystemC)来描述数字电路的行为和功能,然后将其转换为硬件电路实现。这种转换过程是自动完成的,因此开发人员无需手动编写硬件描述语言(HDL)。
HLS的主要目的是简化FPGA设计流程,提高设计效率和设计质量。通过使用高级语言来描述电路,开发人员可以更快速地进行设计和调试,同时也可以更容易地对电路进行修改和优化。此外,HLS还可以自动生成优化后的硬件电路,从而提高性能和资源利用率。

2. HLS开发流程

HLS开发流程大致可以分为以下几个步骤:

  • 设计和实现HLS模块的功能和行为。这一步通常使用C/C++等高级语言来描述电路行为,包括输入/输出端口、计算逻辑和状态机等。
  • 使用HLS工具将高级语言代码转换为硬件电路实现。这一步通常由HLS工具自动完成,但也需要开发人员进行一些配置和调整,以便获得所需的性能和资源利用率。
  • 对生成的硬件电路进行验证和调试。这一步通常需要使用仿真工具对硬件电路进行验证,以确保其与高级语言代码的行为一致,并进行调试以解决任何问题。
  • 将生成的硬件电路与其他硬件/软件组件进行集成和部署。这一步通常需要将生成的硬件电路与其他硬件/软件组件进行集成,以实现最终的系统功能。

3. HLS基本语法

HLS使用特定的语法和指令来描述电路行为和特性。下面是一些基本的HLS语法和指令:

3.1. #pragma HLS

#pragma HLS是HLS中最常用的指令之一,用于控制HLS工具生成的硬件电路的特性和行为。该指令的语法如下:

#pragma HLS directive_name [directive_options]

其中,directive_name是具体的指令名称,directive_options是指令的参数。
以下是一些常见的#pragma HLS指令:

  • #pragma HLS interface:定义模块的输入/输出端口类型和属性。
  • #pragma HLS array_partition:将数组分区为多个部分,以优化资源利用率和性能。
  • #pragma HLS pipeline:将循环展开为流水线以提高性能。
  • #pragma HLS inline:将指定的函数内联以提高性能。
  • #pragma HLS reset:定义复位信号的属性和行为。
  • #pragma HLS latency:定义操作的延迟,以便HLS工具可以更好地优化电路。

3.2. HLS数据类型

HLS支持C/C++语言中的大多数基本数据类型,例如int、float、double等。此外,HLS还支持以下数据类型:

  • ap_int和ap_uint:分别表示带符号整数和无符号整数。
  • ap_fixed和ap_ufixed:分别表示带符号和无符号的固定点数。
  • ap_vector和ap_matrix:分别表示向量和矩阵。

3.3. HLS模块定义

HLS模块定义类似于C/C++函数定义,包括模块名称、输入/输出端口、模块属性等。例如:

void my_module(int input1, int input2, int output1, int output2
) {#pragma HLS interface ap_ctrl_none port=return#pragma HLS interface ap_none port=input1#pragma HLS interface ap_none port=input2#pragma HLS interface ap_none port=output1#pragma HLS interface ap_none port=output2// 模块实现逻辑
}

上述代码中,my_module是一个HLS模块,有两个输入端口input1和input2,以及两个输出端口output1和output2。#pragma HLS interface指令用于定义端口的属性和类型,例如ap_ctrl_none表示该模块不包含任何控制信号,ap_none表示该端口不进行数据流控制。

3.4. 数组分区

HLS中的array_partition是一种用于在FPGA或ASIC中实现数组分区的指令。它允许将一个数组划分为多个小数组,每个小数组都可以独立地存储在FPGA或ASIC的不同部分,以实现并行化操作。这样可以提高设计的并行性,从而提高性能。使用 array_partition 可以帮助设计人员更好地利用硬件资源,提高设计效率。例如:

#define N 1024
int data[N];#pragma HLS array_partition variable=data complete dim=1

上述代码中,data是一个长度为1024的数组。#pragma HLS array_partition指令用于将该数组分为多个部分,并指定每个部分的类型。complete表示将数组完全分区,dim=1表示将数组按行进行分区。
在HLS的array_partition指令中,dim参数是用于指定分区的维度的。它指示分区应该应用于哪个维度。对于一维数组,dim参数应该设置为1,而对于二维数组,dim参数应该设置为0或1。如果设置为0,则分区将应用于第一维,而如果设置为1,则分区将应用于第二维。例如,考虑以下代码段:

int A[4][8];
#pragma HLS array_partition variable=A dim=1 complete

这将A数组的第二维(即列)分为8个小数组,每个小数组包含4个元素。因此,每个小数组都可以并行地存储在FPGA或ASIC的不同部分,以实现并行化操作。
除了dim参数之外,array_partition指令还支持以下参数:

  • variable: 指定要分区的数组变量名称。
  • factor: 指定要分区的因子数。例如,#pragma HLS array_partition variable=A factor=2 将数组A分为两个块。
  • type: 指定分区的类型。可选的类型包括block、cyclic、complete和none。block类型指定分区为块状分区,其中每个块包含连续的元素。
  • cyclic类型指定分区为循环分区,其中每个块包含间隔相等的元素。
  • complete类型指定分区为完全分区,其中所有分区包含相等数量的元素。
  • none类型指定不进行分区。
  • dim: 指定要应用分区的维度,可以是0或1,具体含义在前面回答中已经介绍。
    这些参数可以组合使用以实现各种不同的分区方案,以便更好地利用FPGA或ASIC上的并行计算资源。

3.5. 流水线优化

HLS支持将循环展开为流水线,以提高性能。例如:

#define N 1024
int data[N];void my_module(int *input, int *output) {#pragma HLS interface ap_ctrl_none port=return#pragma HLS interface ap_none port=input#pragma HLS interface ap_none port=output#pragma HLS pipeline II=1for (int i = 0; i < N; i++) {output[i] = input[i] * 2;}
}

II=1表示该循环的迭代间隔为1,即将该循环展开为一个流水线。HLS工具将在流水线上并行执行多个操作,以提高性能。
#pragma HLS pipeline是在HLS(高层次综合)中使用的一种指令,用于指示编译器将循环展开为流水线。通过使用#pragma HLS pipeline,可以将循环分为多个阶段,从而允许每个阶段在不同的时钟周期内并行执行,从而提高系统的性能。
使用#pragma HLS pipeline指令可以有效地利用FPGA或ASIC上的并行计算资源,从而实现更高的时钟频率和更快的数据处理速度。它通常用于加速循环密集型的计算和数据处理任务,如图像处理、信号处理和矩阵计算等。
#pragma HLS pipeline指令的语法如下:

#pragma HLS pipeline [N]

其中N是可选参数,指示流水线的并行度。默认情况下,流水线的并行度等于1,即每个阶段在一个时钟周期内执行。如果指定了N,则表示将流水线分为N个阶段,从而允许每个阶段在不同的时钟周期内并行执行。通常,N的值应该是2的幂,以便更好地利用硬件并行计算资源。
除了N参数之外,#pragma HLS pipeline还支持一些其他的可选参数,包括:

  • II:II代表“间隔间隔”(Iteration Interval)的缩写,是在HLS(高层次综合)中使用的一个指令参数,用于指示循环迭代之间的最小间隔。通过指定II参数,可以帮助编译器更好地理解循环的数据依赖关系,从而生成更高效的硬件设计。
    • 在流水线化的循环中,每个迭代可以分为多个阶段,这些阶段可以在不同的时钟周期内并行执行。如果循环迭代之间的间隔太小,就可能会导致不同的阶段之间存在数据依赖关系,从而导致流水线暂停等性能问题。因此,可以使用II参数指定循环迭代之间的最小间隔,以便在硬件设计中解决这些问题。
    • II的值应该是一个正整数,表示相邻两次迭代之间的最小间隔,通常以时钟周期为单位。例如,如果II=2,则表示每两次迭代之间必须间隔两个时钟周期,即第一个迭代在时钟周期1执行,第二个迭代在时钟周期3执行,第三个迭代在时钟周期5执行,以此类推。
  • enable_flush:指示是否应该在输入结束时强制冲洗流水线,以避免在下一个输入时出现数据冲突。
  • ii_reduction:指示是否应该将内部循环展开为累加器,从而进一步减少延迟和提高时钟频率。
    下面是一个使用#pragma HLS pipeline指令的例子:
#pragma HLS pipeline II=1
for (int i = 0; i < N; i++) {// ...
}

在上面的例子中,#pragma HLS pipeline指令将循环展开为流水线,并将流水线分为II=1个阶段,每个阶段在一个时钟周期内执行。通过流水线化,该循环可以更快地执行,并且可以在不同的时钟周期内并行执行。

3.6. 组合逻辑优化

HLS支持将指定的函数内联以提高性能。例如:

int add(int a, int b) {return a + b;
}int sub(int a, int b) {return a - b;
}int calc(int a, int b) {return add(a, b) * sub(a, b);
}void my_module(int *input, int *output) {#pragma HLS interface ap_ctrl_none port=return#pragma HLS interface ap_none port=input#pragma HLS interface ap_none port=output#pragma HLS inlinefor (int i = 0; i < N; i++) {output[i] = calc(input[i], input[i+1]);}
}

上述代码中,calc函数内部调用了add和sub函数。#pragma HLS inline指令用于将calc函数内联,即将其实现逻辑直接嵌入到my_module中,以避免在运行时频繁调用函数。
在HLS(高层次综合)中,inline是一种指令,用于告诉编译器在编译时将函数调用内联展开,而不是生成实际的函数调用。
使用inline指令可以有效地减少函数调用带来的开销,因为它可以将函数体的代码直接插入到调用该函数的代码中,从而避免了函数调用的开销(如函数调用和返回操作以及参数的复制等)。这在一些高频率的数据通路设计中非常有用。
下面是一个使用inline指令的例子:

inline int add(int a, int b) {return a + b;
}int main() {int x = 10, y = 20;int z = add(x, y);  // 实际上是将add函数的代码插入到这里return 0;
}

在上面的例子中,inline指令告诉编译器将add函数的代码内联到调用它的地方,而不是生成实际的函数调用。这样,当程序运行时,将不会存在真正的函数调用,而是在main函数中直接执行add函数的代码,从而避免了函数调用的开销。
#pragma HLS inline是在HLS中使用的指令,用于指示编译器在编译时对特定函数进行内联展开优化。使用#pragma HLS inline可以告诉编译器将指定的函数内联展开,从而加速系统的性能。
#pragma HLS inline指令的语法如下:

#pragma HLS inline
int add(int a, int b) {return a + b;
}int main() {int x = 10, y = 20;int z = add(x, y);  // 实际上是将add函数的代码插入到这里return 0;
}

在上面的例子中,#pragma HLS inline指令告诉编译器将add函数内联展开,从而加速系统的性能。

3.7. 一些基本概念

  • iteration latency: 从第一个输入到第一个输出的时间
  • iteration interval(II): 每两次输出/输入之间的时间间隔
  • total latency: 总时间,total latency = iteration latency + II * (number of items - 1),II可以改善total latency但不影响iteration latency
    在这里插入图片描述

3.8. 完整示例

3.8.1. 矩阵乘法

以下是一个完整的HLS示例,展示了如何使用HLS实现一个矩阵相乘的功能。

#include <hls_stream.h>
#include <ap_axi_sdata.h>#define N 32
#define M 32
#define P 32typedef ap_axiu<32,1,1,1> data_t;void matrix_multiply(data_t A[N*M], data_t B[M*P], data_t C[N*P]
) {#pragma HLS interface ap_ctrl_none port=return#pragma HLS interface axis port=A#pragma HLS interface axis port=B#pragma HLS interface axis port=Cdata_t a;data_t b;data_t c;hls::stream<data_t> stream_A;hls::stream<data_t> stream_B;hls::stream<data_t> stream_C;// 将输入数据流转换为流对象for (int i = 0; i < N*M; i++) {#pragma HLS pipeline II=1a = A[i];stream_A.write(a);}for (int i = 0; i < M*P; i++) {#pragma HLS pipeline II=1b = B[i];stream_B.write(b);}// 矩阵乘法计算逻辑for (int i = 0; i < N; i++) {for (int j = 0; j < P; j++) {#pragma HLS pipeline II=1int sum = 0;for (int k = 0; k < M; k++) {a = stream_A.read();b = stream_B.read();sum += a.data * b.data;}c.data = sum;c.keep = 15;c.strb = 15;c.last = (i == N-1 && j == P-1) ? 1 : 0;stream_C.write(c)}}// 将输出流转换为输出数据流for (int i = 0; i < N*P; i++) {#pragma HLS pipeline II=1c = stream_C.read();C[i] = c;}
}

在上述示例中,我们定义了一个名为matrix_multiply的函数,该函数用于计算两个矩阵的乘积,并将结果存储在输出矩阵中。我们使用了#pragma HLS interface指令指定了函数的输入输出接口,以及数据类型。然后我们定义了一些流对象,用于在HLS中处理数据流。
在函数实现中,我们首先将输入的数据流转换为流对象,然后执行矩阵乘法计算。在矩阵乘法计算中,我们使用三重嵌套循环遍历矩阵,以计算矩阵乘积的每个元素。内部的循环使用#pragma HLS pipeline指令进行流水线操作。最后,我们将输出流转换为输出数据流,并将其返回。
这个示例演示了HLS如何使用流水线和流对象来高效处理数据流,以及如何使用#pragma HLS interface指令来指定HLS代码的接口和数据类型。对于HLS的新手来说,这个示例可以作为一个非常好的学习范例。

3.8.2. 函数调用和循环

下面我们再来一个简单的示例,演示如何在HLS中使用函数调用和循环。
假设我们有一个函数compute,它的作用是对一个数组进行一系列计算,并返回计算结果。现在我们想在HLS中使用这个函数,以便可以在FPGA上进行硬件加速。下面是示例代码:

#define N 1024void compute(int A[N], int B[N], int C[N]) {for (int i = 0; i < N; i++) {B[i] = A[i] + 1;}for (int i = 0; i < N; i++) {C[i] = B[i] * 2;}
}void top_function(int A[N], int C[N]) {#pragma HLS interface ap_fifo port=A#pragma HLS interface ap_fifo port=Cint B[N];compute(A, B, C);
}

在上述示例中,我们定义了一个名为compute的函数,用于对一个数组进行一系列计算。然后我们定义了另一个函数top_function,它使用#pragma HLS interface指令指定了函数的输入输出接口和数据类型。在top_function中,我们首先声明一个数组B,用于存储compute函数的计算结果。然后我们调用compute函数,将输入数组A、输出数组C和中间数组B作为参数传递给该函数。最后,我们将C数组作为输出返回。
这个示例演示了如何在HLS中使用函数调用和循环。在HLS中,我们可以像在C语言中一样使用函数和循环来实现算法,从而更容易地将现有的软件算法移植到硬件中。同时,我们也可以使用#pragma HLS interface指令来指定函数的接口和数据类型,从而更方便地与其他硬件模块集成。

3.8.3. 流水线和并行化指令

接下来,我们将介绍如何在HLS中使用流水线和并行化指令来实现加速。
流水线是一种常见的并行化技术,它将一个任务分解为多个阶段,每个阶段可以并行执行。例如,在对一个长数组进行操作时,我们可以将其分为多个段,每个段可以并行处理。在HLS中,我们可以使用#pragma HLS pipeline指令来实现流水线加速。下面是一个示例代码:

#define N 1024void top_function(int A[N], int C[N]) {#pragma HLS interface ap_fifo port=A#pragma HLS interface ap_fifo port=C#pragma HLS pipelineint B[N];for (int i = 0; i < N; i++) {#pragma HLS unrollB[i] = A[i] + 1;}for (int i = 0; i < N; i++) {#pragma HLS unrollC[i] = B[i] * 2;}
}

在上述示例中,我们使用#pragma HLS pipeline指令来指定函数中的循环可以流水线并行执行。在每个循环中,我们还使用#pragma HLS unroll指令来指定循环可以展开为多个迭代,以进一步提高并行度。这个示例演示了如何使用流水线和并行化指令来实现加速。
最后,我们需要注意一些常见的优化技巧,以进一步提高HLS的性能。例如,我们可以使用#pragma HLS array_partition指令将数组分区,以充分利用FPGA的存储资源。我们还可以使用#pragma HLS dataflow指令来实现数据流架构,从而更好地利用FPGA的并行性能。此外,我们还可以使用#pragma HLS stream指令来指定数据流接口,以优化数据传输和缓存。这些优化技巧可以进一步提高HLS的性能和效率。

4. HLS高级语法

在HLS中,还有一些高级特性,如定点数和浮点数支持,数据重用和代码生成等。我们在下面继续介绍一些高级特性和应用示例。

4.1. 定点数和浮点数支持

在FPGA中,通常使用定点数来表示数字,因为它可以更有效地利用硬件资源和提高运算速度。在HLS中,我们可以使用ap_fixed和ap_ufixed类型来表示定点数,它们具有定点位和整数位。例如:

#include "ap_fixed.h"
typedef ap_fixed<16, 8> fixed_point;

上述代码定义了一个16位定点数,其中8位为小数位,另外8位为整数位。我们还可以使用ap_float类型来表示浮点数,它具有单精度和双精度浮点数。例如:

#include "ap_float.h"
typedef ap_float<32> single_float;
typedef ap_float<64> double_float;

4.2. 数据重用

在HLS中,我们可以使用数据重用技术来减少计算量和存储器带宽。数据重用指的是在计算过程中多次使用同一数据,而不是每次都从存储器中读取数据。在HLS中,我们可以使用#pragma HLS array_reshape指令和#pragma HLS data_pack指令来实现数据重用。例如:

#define N 1024void top_function(int A[N], int B[N]) {#pragma HLS interface ap_fifo port=A#pragma HLS interface ap_fifo port=Bint C[N];for (int i = 0; i < N; i++) {#pragma HLS pipeline II=1#pragma HLS unroll factor=4C[i] = A[i] + B[i];}#pragma HLS array_reshape variable=C complete dim=1#pragma HLS data_pack variable=Cfor (int i = 0; i < N/4; i++) {#pragma HLS pipeline II=1int sum = C[i*4] + C[i*4+1] + C[i*4+2] + C[i*4+3];B[i] = sum;}
}

在上述示例中,我们使用#pragma HLS array_reshape指令将数组C从一维数组重塑为二维数组,以利用数据重用。然后,我们使用#pragma HLS data_pack指令对数组C进行数据打包,以减少存储器带宽。最后,我们将数组C用于计算,并使用流水线和并行化指令来实现加速。

4.3. #pragma HLS interface

#pragma HLS interface是在HLS(高层次综合)中使用的一个指令,用于定义模块的接口,包括输入、输出端口和数据类型等。通过使用#pragma HLS interface,可以使HLS编译器了解模块的接口和数据类型,并生成相应的硬件设计。
具体而言,#pragma HLS interface指令的作用是将模块的输入和输出端口映射到HLS的IO接口上,从而将模块与外部环境连接起来。此外,还可以使用该指令指定输入和输出端口的数据类型、数据位宽、信号是否阻塞等属性,以便在硬件设计中生成相应的电路。
以下是一个使用#pragma HLS interface指令的简单例子:

#pragma HLS interface ap_ctrl_none port=return
#pragma HLS interface axis port=input
#pragma HLS interface axis port=output
void my_module(stream<data_t> &input, stream<data_t> &output) {
// ...
}

在上面的例子中,#pragma HLS interface指令定义了一个名为my_module的模块,该模块有两个输入输出端口input和output,数据类型为stream<data_t>,其中data_t是一个自定义的数据类型。第一行的ap_ctrl_none指示该模块不需要控制器端口,port=return表示该模块的返回值映射到HLS的返回端口上。
需要注意的是,#pragma HLS interface指令只是定义模块的接口,不会生成模块的实现。模块的实现应该在其他地方定义,例如在C或C++源代码中。在模块实现的代码中,需要使用与接口定义中相同的数据类型和端口名,以便与HLS生成的电路连接起来。同时,还需要使用HLS指令来指示编译器如何将模块转换为硬件电路,例如使用#pragma HLS design和#pragma HLS pipeline等指令。
#pragma HLS interface指令用于定义模块的接口和属性,以下是常用的参数:

  • ap_none、ap_hs、ap_stable、ap_vld:指定输入/输出端口的数据类型。
  • port=:指定模块的返回值端口名,必须在模块的接口中指定。
  • axis、s_axilite、m_axi、s_axi等:指定输入/输出端口的接口类型,例如AXI总线、轴接口等。
  • register、noflip、flatten等:指定输入/输出端口的属性,例如数据是否寄存器存储、是否需要数据翻转等。
    以下是参数类型和接口协议对应图
    在这里插入图片描述

图中的“ D”表示“ default”,表示 Vivado HLS 工具默认综合出来的接口。“ S”表示“ support”,表示 Vivado HLS 工具支持综合出来的接口。

4.4. #pragma HLS design

#pragma HLS design指令用于指示编译器如何将模块转换为硬件电路。以下是常用的参数:

  • flatten:将模块展开为更简单的逻辑结构。
  • pipeline:使模块内部的循环/分支语句并行执行,提高性能。
  • inline:将模块内的函数调用展开为实际的函数代码。
  • dataflow:指示编译器使用数据流编程模型进行优化。
  • latency:指定模块的延迟目标。
  • parallel:指定模块的并行度目标。
    使用示例:
#pragma HLS design flatten
#pragma HLS design pipeline II=2

在使用#pragma HLS design时,需要将其放置在模块定义之前,并且模块定义必须在同一个文件中。在模块实现的代码中,可以使用该指令来指示编译器对模块进行优化,例如将模块展开为更简单的逻辑结构、并行执行循环/分支语句、调用函数等。同时,还可以使用其他HLS指令来进一步控制优化策略,例如#pragma HLS loop_tripcount、#pragma HLS dependence等。

4.5. 流式处理器

  • 流式处理器:流式存储器(streaming memory)是一种用于解决数据存储和读取延迟问题的技术。使用流式存储器可以减少在高级综合(HLS)中数据存储和读取的延迟,从而提高设计的吞吐量和性能。
    • 以下是使用流式存储器的一些技巧:
    • 数据缓冲区:在HLS设计中,使用流式存储器可以将数据存储在缓冲区中,以便稍后读取。这可以减少存储和读取数据的延迟,从而提高设计的性能。
    • 数据分块:使用流式存储器可以将数据分成块,每个块都可以作为单独的流进行处理。这可以帮助优化流水线设计,减少存储和读取数据的延迟。
    • 数据流合并:在某些情况下,可以将多个数据流合并为一个数据流。这可以减少存储和读取数据的延迟,同时提高设计的性能。
    • 数据流分裂:类似于数据流合并,数据流分裂可以将一个数据流分成多个数据流进行处理。这可以帮助优化流水线设计,减少存储和读取数据的延迟。
    • 内存分配:在HLS设计中,可以使用内存分配技术来管理流式存储器。这可以减少存储和读取数据的延迟,同时提高设计的性能。
    • 总之,使用流式存储器可以帮助优化HLS设计,减少数据存储和读取的延迟,从而提高设计的性能和吞吐量。

以下是一个简单的HLS流式处理器的例子,该流式处理器使用Pipelining和Loop Unrolling优化策略来提高性能。

#include <hls_stream.h>// 定义流式处理器
void stream_processor(hls::stream<int>& in_stream, hls::stream<int>& out_stream, int num_elements) {
#pragma HLS interface ap_ctrl_none port=return
#pragma HLS interface axis port=in_stream
#pragma HLS interface axis port=out_streamint buffer[4];// 循环展开初始化,实现4元素缓冲区
for (int i = 0; i < 4; i++) {#pragma HLS unrollbuffer[i] = 0;
}// 流式处理器核心部分
for (int i = 0; i < num_elements; i++) {#pragma HLS pipeline II=1int input_data = in_stream.read();// 向缓冲区添加新数据buffer[0] = buffer[1];buffer[1] = buffer[2];buffer[2] = buffer[3];buffer[3] = input_data;// 计算输出int output_data = buffer[0] + buffer[1] + buffer[2] + buffer[3];// 输出到流out_stream.write(output_data);
}
}

上述例子中,stream_processor函数实现了一个简单的流式处理器。它的输入是一个整数流,输出也是一个整数流。在函数内部,使用了Pipelining和Loop Unrolling优化策略,将处理逻辑拆分为多个阶段,并使每个阶段都可以并行执行。同时,通过循环展开的方式实现了一个4元素的缓冲区,以加速计算过程。在处理完所有输入数据之后,该函数会将输出流中的数据写回到主存中。
需要注意的是,在使用HLS开发流式处理器时,还需要考虑数据流的稳定性和流量控制等问题。为了确保稳定性,可以使用ap_stable等数据类型来声明输入/输出端口;而为了控制流量,可以使用ap_fifo等缓冲区类型来调整输入/输出流的大小。

4.6. #pragma HLS dataflow

#pragma HLS dataflow数据流指令: 将设计的功能划分成不同的阶段,让HLS能够对不同的功能阶段进行优化,提高综合后的效果。

#include "hls_stream.h"void adder(hls::stream<int> &in, hls::stream<int> &out, int val) {
for (int i = 0; i < 10; i++) {int x = in.read();out.write(x + val);
}
}void multiplier(hls::stream<int> &in, hls::stream<int> &out, int val) {
for (int i = 0; i < 10; i++) {int x = in.read();out.write(x * val);
}
}void top(hls::stream<int> &in, hls::stream<int> &out, int val) {
#pragma HLS dataflow
hls::stream<int> intermediate;
adder(in, intermediate, val);
multiplier(intermediate, out, val);
}

4.7. #pragma HLS reset variable

指定变量在每个时钟周期的初始值。

int counter = 0;
void my_counter(bool reset, int &count) {
#pragma HLS reset variable=counter
if (reset) {counter = 0;
} else {counter++;
}
count = counter;
}

5. HLS技巧

下面我将为你介绍一些HLS的最佳实践,帮助你更好地使用HLS进行硬件电路设计。

  • 了解HLS的限制和优化
    虽然HLS可以让你使用高级编程语言进行硬件电路设计,但是它仍然有一些限制。例如,HLS无法处理动态内存分配,因此你需要手动分配所有的内存。另外,HLS通常无法处理递归函数,因为递归需要使用栈来保存函数调用的状态,而栈需要使用大量的存储器资源。
    此外,为了获得最佳的性能和资源利用率,你需要使用HLS的一些优化选项,例如循环展开、分区、指令重排等。这些优化选项可以根据你的算法和目标设备进行调整,因此你需要了解HLS的优化选项和如何使用它们。
  • 将复杂的算法拆分成简单的模块
    HLS最适合处理简单的、高度并行的算法。因此,当你使用HLS进行硬件电路设计时,应该尽可能将复杂的算法拆分成简单的模块。每个模块都应该尽可能独立,以便并行化处理。
  • 优化内存访问
    在HLS中,内存访问通常是性能和资源利用率的瓶颈。因此,你需要优化内存访问以获得最佳的性能和资源利用率。一种常见的优化方法是使用局部变量和数组,这可以减少对存储器的访问次数。另外,你还可以使用流缓冲区来减少存储器的访问次数。
  • 使用硬件加速器IP核
    HLS提供了许多硬件加速器IP核,例如FFT、卷积等。这些IP核已经经过优化,可以在FPGA上实现高性能的硬件电路。当你需要实现这些算法时,应该首先查看HLS提供的IP核,以便快速实现高性能的硬件电路。
  • 进行性能分析和调试
    最后,当你使用HLS进行硬件电路设计时,需要进行性能分析和调试,以确保硬件电路的性能和正确性。HLS提供了许多调试和性能分析工具,例如波形查看器、性能分析器等。你可以使用这些工具来定位和解决问题,从而加快硬件电路设计的开发速度。
    希望这些最佳实践可以帮助你更好地使用HLS进行硬件电路设计。

以下是一些在使用HLS进行高层次综合时可能有用的技巧:

  • 分区(partitioning):使用HLS时,将大型数组分成较小的块可以增加并行性,从而提高性能。
  • 流水线(pipelining):将操作流水线化可以减少操作的延迟,并在FPGA上实现更高的时钟频率。
  • 循环展开(loop unrolling):展开循环可以减少循环迭代的次数,并增加并行性。
  • 优化数据类型:HLS支持多种数据类型,包括固定点数和浮点数。选择合适的数据类型可以减少逻辑资源使用和运行时间。
  • 优化存储:使用合适的存储器类型可以减少存储器使用和延迟。例如,使用流式存储器(streaming memory)可以减少数据存储和读取的延迟。
  • 优化数据流:使用HLS时,将操作转换为数据流可以提高性能,因为它允许并行操作。
  • 使用pragma指令:HLS支持pragma指令,这些指令可以告诉HLS如何执行某些操作。pragma指令可以优化代码,从而提高性能。
    - 调整数据结构:合适的数据结构可以减少逻辑资源使用和运行时间。例如,使用链表(linked list)可以减少数据移动的延迟。
  • 使用代码重构:HLS生成的代码可能不是最优的。通过重构代码,可以手动优化代码,从而提高性能。
  • 合适的编译器指令:编译器指令可以告诉编译器如何优化代码。使用合适的编译器指令可以提高性能。

6. 总结

在本教程中,我们介绍了HLS的基本概念和编程模型,包括HLS的主要特性、编程模型和调优技巧。我们还提供了一些示例,展示了如何使用HLS实现不同的应用程序,并介绍了HLS中的一些高级特性,如定点数和浮点数支持、数据重用和代码生成等。通过学习本教程,您将能够掌握HLS的基本概念和编程技巧,以便使用HLS来实现高效的FPGA设计。
HLS是一个强大的工具,可以帮助我们将软件算法转换为硬件电路,从而在FPGA上获得高性能的加速效果。在学习HLS时,我们需要掌握C/C++编程语言和FPGA体系结构知识,并掌握HLS中常见的优化技巧和指令。同时,我们也需要有耐心和恒心,逐步积累经验和技能,从而成为一名优秀的HLS工程师。

7. 资料来源

ChatGPT,对没错哈哈哈哈,全部是ChatGPT生成的,我只是知识的搬运工 >。<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36066.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python入门基础教程

Python入门基础教程 Python是一种高级编程语言&#xff0c;开发者可以编写多种应用程序&#xff0c;从简单的脚本到复杂的应用程序。Python易学易用&#xff0c;是新手和专业人士的首选编程语言。本文将介绍Python入门基础教程&#xff0c;帮助您开始学习Python编程。 安装Py…

新手入门 Python 的不同方法

学习Python意味着练习Python。在本文中&#xff0c;我们将探讨一些最流行的方法来练习 Python 编程技能。 学习几乎任何新技能不仅需要获得知识&#xff0c;还需要经验。这就是我们通过实践获得的。 本文将帮助任何最近开始学习 Python 的人&#xff0c;或者已经了解 Python 基…

提高情商必读的10本书籍推荐

职场中&#xff0c;情商&#xff08;即情绪智慧&#xff09;往往被认为比智商更为重要。情商不仅有助于更好地理解自己和他人的情绪&#xff0c;还能帮助我们在职场中更好地沟通、协作与领导。下面我们为大家推荐10本提高情商必读的书籍&#xff0c;帮助你在职场中取得更好的成…

必读的一些书籍推荐

推荐一&#xff1a;《如雪如山》作者: 张天翼 本书以女性视角隐喻女性的生存故事。所有的女人都藏着一块相同的拼图。他们暗中的悲欢离合&#xff0c;爱恨情仇如此不同&#xff0c;却又相通。 作者张天翼通过七位心思敏锐、笔触细腻犀利的女主人公的人生片段&#xff0c;梳理出…

有哪些人际关系书籍值得推荐?看完这两本书你也能成为社交高手!

要想提高人际关系和社交能力&#xff0c;阅读确实不失为一个好办法。 但是问题是该看什么书呢&#xff1f; 有哪些人际关系书籍值得推荐&#xff1f; 笔者强烈推荐《情商必读12篇》和《沟通与说服必读12篇》这两本书。建议你能把这两本书都完完整整、一字不漏地看完&#xf…

关于社交的书籍推荐,这本书最实用

看过那么多关于社交方面的书籍&#xff0c;个人感觉最值得推荐、最实用的还是《情商必读12篇》。 &#xff08;注意&#xff0c;这本书传统平台无售&#xff0c;只能从官网获得&#xff0c;请自行百度&#xff09; 也正是在看过这本书之后&#xff0c;笔者才真正明白了所谓人际…

有用但用处不多的html的属性

前言 html的属性&#xff0c;瀚若星河&#xff0c;有些是平时经常用到的&#xff0c;有些是平时不常用的&#xff0c;还有一些基本用不到。 今天就总结一般不怎么用&#xff0c;但是有用&#xff0c;用处又不多的属性。 Geolocation&#xff08;地理位置&#xff09; 我实操…

【趣学算法】贪心算法、海盗古董装船问题

14天阅读挑战赛 努力是为了不平庸~ 算法学习有些时候是枯燥的&#xff0c;这一次&#xff0c;让我们先人一步&#xff0c;趣学算法&#xff01; 文章目录 贪心本质贪心选择最优子结构 最优装载问题sort函数总结 贪心本质 一个贪心算法总是做出当前最好的选择&#xff0c;也就是…

《趣学算法》读书笔记

内容摘要 主要介绍我对本书的一些自我感觉比较亮点地方的总结。 第一章 算法 算法有两条线索&#xff0c;数据结构、算法策略。 算法特性 时间复杂度 常见算法时间复杂度 时间复杂度的渐进上界 渐进精确界 用渐进上界和渐进下界逼近&#xff0c; 空间复杂度 递归 递归包…

【趣学算法】一棋盘的麦子

14天阅读挑战赛努力是为了不平庸~ 算法学习有些时候是枯燥的&#xff0c;这一次&#xff0c;让我们先人一步&#xff0c;趣学算法&#xff01; 案例背景 有一个古老的传说&#xff0c;一位国王的女儿不幸落水&#xff0c;水中有很多鳄鱼&#xff0c;国王情急之下下令&#xff…

【算法】看看《趣学算法》里面介绍如何学习算法的

14天阅读挑战赛 如何学习算法的 算法为什么难学算法面临的困难是什么&#xff1f;趣学算法告诉我们如何学习算法 最近入手一本《趣学算法》这本书&#xff0c;感觉收获颇多。里面有这样的一则类容给大家介绍一下&#xff1a; 地址的链接&#xff1a;趣学算法&#xff08;第2版…

趣学算法(2)

14天阅读挑战赛 目录 前言一 几类时间复杂度二 兔子数列1.问题分析2.方法13.方法24.方法3 最后 前言 这篇文章是《趣学算法》的读书笔记&#xff0c;也对数据结构与算法的初步介绍&#xff0c;阅读这篇文章&#xff0c;我会带你改进一个算法。 一 几类时间复杂度 常见的算法时…

趣学算法14天阅读|Day2

14天阅读挑战赛 文章目录 前言什么是算法&#xff1f;算法复杂度如何评定好算法案例案例一&#xff1a;棋盘的麦子案例二&#xff1a;兔子数列 总结 前言 &#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端中级…

Go C 编程 第9课 放飞汽球(魔法学院的奇幻之旅 Go C编程绘图)

Goc编程第八课 Goc编程第八课_哔哩哔哩_bilibili Goc编程第九课 Goc编程第九课_哔哩哔哩_bilibili 59.实心椭圆 (魔法学院第9课) 难度&#xff1a;1 登录 60.双色椭圆 (魔法学院第9课) 难度&#xff1a;1 登录 61.气球串 (魔法学院第9课) 登录 62.同心圆环 (魔法学院第9课…

趣学算法14天阅读|Day1

14天阅读挑战赛 文章目录 前言编写博文背景学习算法的好处常见的招聘要求如何高效学习算法学习算法方式如何进行刷题训练如何进行算法面试总结 前言 &#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端中级工程…

青少年趣味编程社区

近年来&#xff0c;在政策推动和市场需求增长下&#xff0c;STEAM教育与科技的结合应用正如火如荼地进行&#xff0c;无论是新型的科技元件、教育机器人或3D打印技术等&#xff0c;格物斯坦表示&#xff1a;无人机同样也是。根据相关机构预测&#xff0c;国内STEAM教育行业未来…

趣学算法:贪心算法

14天阅读挑战赛 一、算法知识点 贪心算法是“活在当下&#xff0c;看清楚眼前”的方法。贪心算法从问题的初始解开始&#xff0c;一步一步地做出当前的最好选择&#xff0c;逐步逼近最优解&#xff0c;从而尽可能地得到最优解&#xff0c;即使达不到最优解&#xff0c;也可以得…

湖南码趣教育python怎么样,湖南码趣教育python接单

湖南码趣教育科技有限公司怎么样&#xff1f;Python编程课6888值不值得报&#xff1f; 湖南码趣教育科技有限公司还可以&#xff0c;学习少儿编程更推荐选择童程童美&#xff0c;该机构线上开设小班直播课&#xff0c;真人老师互动教学&#xff0c;激发孩子兴趣&#xff0c;培…

带你趣学算法

14天阅读挑战赛 目录 前言一 什么是好算法&#xff1f;1.1算法对比1.2算法的特性1.3好算法的标准 二 复杂度2.1时间复杂度&#xff08;1&#xff09;定义&#xff08;2&#xff09;如何计算 2.2空间复杂度&#xff08;1&#xff09;定义&#xff08;2&#xff09;如何计算 最后…

畅聊趣坊项目测试报告

文章目录 项目背景项目功能测试计划与设计功能测试自动化测试 测试结果功能测试结果UI自动化测试结果 项目背景 在浏览网站时&#xff0c;发现好多网站开放出聊天的窗口&#xff0c;我们一发送消息就会收到一条消息&#xff0c;好奇这个功能是怎么实现的&#xff0c;最后查阅资…