HLS新手入门教程

这篇具有很好参考价值的文章主要介绍了HLS新手入门教程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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=1
   
   for (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 inline
   
   for (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
    c 2 hls,数字IC设计,IC工具,Verilog,fpga开发,HLS

3.8. 完整示例

3.8.1. 矩阵乘法

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

#include <hls_stream.h>
#include <ap_axi_sdata.h>

#define N 32
#define M 32
#define P 32

typedef 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=C
   
   data_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=1
       a = A[i];
       stream_A.write(a);
   }
   for (int i = 0; i < M*P; i++) {
       #pragma HLS pipeline II=1
       b = B[i];
       stream_B.write(b);
   }
   
   // 矩阵乘法计算逻辑
   for (int i = 0; i < N; i++) {
       for (int j = 0; j < P; j++) {
           #pragma HLS pipeline II=1
           int 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=1
       c = 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 1024

void 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=C

   int 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 1024

void top_function(int A[N], int C[N]) {
   #pragma HLS interface ap_fifo port=A
   #pragma HLS interface ap_fifo port=C
   #pragma HLS pipeline

   int B[N];

   for (int i = 0; i < N; i++) {
       #pragma HLS unroll
       B[i] = A[i] + 1;
   }
   for (int i = 0; i < N; i++) {
       #pragma HLS unroll
       C[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 1024

void top_function(int A[N], int B[N]) {
  #pragma HLS interface ap_fifo port=A
  #pragma HLS interface ap_fifo port=B

  int C[N];

  for (int i = 0; i < N; i++) {
      #pragma HLS pipeline II=1
      #pragma HLS unroll factor=4
      C[i] = A[i] + B[i];
  }

  #pragma HLS array_reshape variable=C complete dim=1
  #pragma HLS data_pack variable=C

  for (int i = 0; i < N/4; i++) {
      #pragma HLS pipeline II=1
      int 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等:指定输入/输出端口的属性,例如数据是否寄存器存储、是否需要数据翻转等。
    以下是参数类型和接口协议对应图
    c 2 hls,数字IC设计,IC工具,Verilog,fpga开发,HLS

图中的“ 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_stream

int buffer[4];

// 循环展开初始化,实现4元素缓冲区
for (int i = 0; i < 4; i++) {
  #pragma HLS unroll
  buffer[i] = 0;
}

// 流式处理器核心部分
for (int i = 0; i < num_elements; i++) {
  #pragma HLS pipeline II=1
  int 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生成的,我只是知识的搬运工 >。<文章来源地址https://www.toymoban.com/news/detail-782708.html

到了这里,关于HLS新手入门教程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 电脑黑客技术新手入门,自学黑客技术入门教程

    最近经常有小伙伴联系我说要学黑客技术,当然目的各种各样,有的就是觉得黑客很酷,单纯想要学技术,还有的就是想找人帮忙攻击赌博网站或者监听别人的电话(以女朋友的电话居多),对于想要单纯学技术的朋友我很欢迎他们问我问题,但对于那些想做违法事情的人我

    2024年02月10日
    浏览(35)
  • Midjourney入门教程,新手必看!

    1.1 Midjourney是什么? Midjourney是一款非常受欢迎的AI绘图工具,可根据用户输入的内容生成高质量图像,该工具于2022年3月首次亮相,虽然Midjourney在发展过程中遇到了技术、金钱等诸多压力,但最终给广大AI绘画爱好者带来极致的体验。 1.2 Midjourney有何优势? ●智能化绘图 Mi

    2024年01月18日
    浏览(44)
  • 你好,uv变换(新手入门向聊天教程)

    温馨提示:本文只是一篇入门聊天,不涉及代码教程,看不懂代码就跳过,没关系! 1、uv其实就是一个二维坐标系啊,就俩轴,就跟xy轴一样。 那为什么不叫xy,反而叫uv呢? 不知道,应该是为了跟空间坐标系xyz区别开来,以免在工作流程中产生误解吧吧吧。 2、uv坐标用于采

    2024年02月12日
    浏览(30)
  • MUI框架从新手入门【webapp开发教程】

    性能和体验的差距,一直是mobile app开发者放弃HTML5的首要原因。 浏览器天生的切页白屏、不忍直视的转页动画、浮动元素的抖动、无法流畅下拉刷新等问题,这些都让HTML5开发者倍感挫败,尤其拿到Android低端机运行,摔手机的心都有; 另一方面,浏览器默认控件样式又少又

    2024年02月04日
    浏览(33)
  • 微信小程序新手入门教程一:零基础上手

    小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。它提供了一个简单、高效的应用开发框架和丰富的组件及API,帮助开发者在微信中开发具有原生 APP 体验的服务。 1.开发环境不同。 网页运行在浏览器环境中,而小

    2024年03月18日
    浏览(42)
  • 小程序注册安装以及新手快速入门教程

    一、注册并安装微信小程序 1.打开  https://mp.weixin.qq.com/  网址,点击立即注册即可进入小程序开发账号的注册流程,注册的账号类型选择 小程序 。 2.根据注册要求注册,发送邮箱信息,接收到微信团队发送的邮箱信息后,点击链接进行激活,如果出现 红色感叹号 可参考下

    2024年02月09日
    浏览(43)
  • Python 安装教程,新手入门(超详细)含Pycharm开发环境安装教程

    目录 一、Python介绍 二、Python安装教程 (一)Python的下载 (二)Python的安装 三、Pycharm开发工具的安装 (一)Pycharm介绍 (二)Pycharm的下载 (三)Pycharm的安装 ​        Python由荷兰数学和计算机科学研究学会的吉多·范罗苏姆于1990年代初设计,作为一门叫做ABC语言的替

    2024年01月20日
    浏览(70)
  • 注册Github账号详细教程【超详细篇--适合新手入门】

    目录 一、GitHub的简介 二、如何注册自己的GitHub账户 1、进入github的官网 2、点击右上角注册按钮sign up,来到注册页面  4、点击Continue,继续在光标处创建密码,继续创建用户名

    2024年02月10日
    浏览(31)
  • Unity2022新手入门教程(P4-P100)

    本笔记根据B站同名Unity教程同步记录,课程资料见此链接。 博主已购买,支持正版资源,尊重每个人的劳动成果。 本教程适合毫不知情的小白 内容非常简单 建议先看目录再决定要不要学习 unity下载地址: https://unity.cn/ 点击下载Unity 下载Unity Hub(需要注册一个账号,博主是用

    2024年02月15日
    浏览(64)
  • 微信小程序新手入门教程二:认识JSON配置文件

    在上一篇我们介绍了微信小程序的注册和基本使用方式,并且写出了一个简单的页面,但是依然没有解释目录中的各种.json文件是做什么的。这篇我们就来认识一下各种JSON配置文件及其配置项。 首先先来认识一下JSON是什么。 JSON 指的是 JavaScript 对象表示法(JavaScript Object 

    2024年04月17日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包