HLS开发学习-13- 数组优化

简介: HLS开发学习-13- 数组优化

写在前面


本文是本系列专题的第十三篇,参考高亚军老师的视频教程以及课程的ppt,主要介绍了vivado HLS对数组优化。

数组分割


resource指令可以指定使用哪种类型的RAM,从而创建哪些RAM端口(单端口或双端口)。如果没有指定资源,Vivado HLS默认使用为单端口RAM。如果它减少启动间隔或减少延迟,则会使用双端口RAM。

数组通常在综合后被实现为存储器(RAM, ROM或FIFO)

  • 顶层函数接口上的数组被合成为访问外部内存的RTL接口。
  • 内部设计的数组被合成为内部块RAM, LUTRAM, UltraRAM,或寄存器,取决于优化设置。

性能瓶颈


这里把men进行综合成为了双端口RAM,性能上进行了很大程度的优化。

image.png

三种数组分割的方法


进行数组分割的三种方法如下,

Block:可以进行设置Block的方式进行分割,该分组方式是顺序分割的。假设循环次数为6 ,这里按3组分割,每两个一组。

Cycllc:可以进行设置Cycllc的方式进行分割,该分组方式是轮序分割的。假设循环次数为6 ,这里按3组分割,每两个一组。

Register:设置为register发方式,循环将会展开成完全并行。指令的参数设置为complete

image.png

性能评估对比:

Block、Cycllc的性能基本一致,而Register(complete)的方式是最佳的。

image.png

分割的块越多越好吗?


分割的块数可根据实际数据流进行设置。通过这种方式,我们可以得到延迟和资源利用率的最佳结果,但更多的块将意味着更多的端口将生成。

从下图可以看到,分块的数量会适度影响到资源的使用情况。

image.png

多维数组


对多维数组的分割,定义维度为按顺序的1、2、3…如果在维度为3时进行分割,该实例会得到4个二维数组,如果在维度为1时进行分割,该实例会得到10个二维数组。

image.png

下面这个例子,展示了对不同维度分割成不同个数的块的情况:

在指定分割个数为4时,这里的二维数组分割成了4个block;在指定分割个数为2时,这里的二维数组分割成了2个block。

image.png

从仿真结果中也可以看出两种情况的读取数据的状态的区别。

image.png

数组分割小结


  1. HLS可以使用三种方法进行数据的分割。
  2. 数组分割可以提升数据的吞吐率。

数组的映射和重组


多个数组映射成一个大数组


当C代码中有许多小数组时,将它们映射到一个更大的数组通常会减少所需的块RAM数量。当设备支持时,每个阵列被映射到块RAM或UltraRAM。HLS使用ARRAY_MAP指令进行map操作,提供了两种MAP的方式,一个是横向的一个是纵向。

水平映射


**水平映射:**获取一个包含更多元素的数组。使用这种方法可以不用关注数据的位宽,最后的数组的位宽是N+M。(下图示例)

image.png

可以使用offset进行数据偏移。但是尽管水平映射可以导致使用更少的块RAM组件,从而“改善区域”,但它确实对吞吐量和性能有影响,因为现在有更少的块RAM端口。

image.png

**垂直映射:**获取具有较大位宽的单个数组。

和水平映射相反,这里不需要关注数组的长度,长度将会选择拼接数组的最大值。

image.png

数据分割和合并可以同时联合使用


这里的先使用分割,然后把分割的模块进行部分合并,这样做的目的是在减少资源的同时提高一定的数据吞吐率。

image.png

数据重组


ARRAY_RESHAPE数组重组指令结合了ARRAY PARTITION和ARRAY MAP的垂直模式。-使用数据重组减少块RAM的数量,仍然允许分区的有益属性:并行访问数据。

image.png

示例:

image.png

对上述例子分别使用水平映射、垂直映射、还有重组的方式。

image.png

从上图结果中可以看出,对于使用MAP指令,减少了RAM的使用量,但是不能提升数据的吞吐量。而使用RESHAPE指令,通过ARRAY PARTITION,提高数据吞吐量。通过ARRAY_MAP可以降低RAM的利用率。从而做到数据吞吐量和区域之间的权衡。

数组优化的其他方法


定义ROM


使用const +initial value


定义ROM的一种实用方法是使用const +initial value。这种方法简单易行,但是也有弊端,在我们的初始值比较多的时候,不方便进行手动输入,并且提高了维护代码的难度。

image.png

使用头文件


可以使用头文件的方式进行定义ROM,这样操作简单易行,提高了实用性,降低了代码管理和工程维护的难度。

image.png

这里需要注意的是,这个#include需要单独占一行。rom内部的文件格式如下

image.png

如果数值由数学方程计算而未更新。Vivado HLS将推断一个ROM。

image.png

改变ROM的输出延迟


默认情况下,ROM输出时延为2。可以增加ROM输出延迟,意味着插入一级寄存器,这使得时序收敛更加容易。

image.png

latency改变后的生成代码对比:

image.png

数组的初始化


虽然不是必需的,但Xilinx建议指定数组,使用静态限定符将其实现为内存。这不仅确保Vivado HLS在RTL中使用内存实现数组,还允许使用静态类型的初始化行为。

如果不加static的关键字,每次执行函数时,数组coeff都被赋给这些值。每次设计执行时,实现coeff的RAM都被加载这些值。对于一个单端口RAM,这将花费8个时钟周期,在此期间不会发生有涉及到coeff的操作。

如果加static的关键字,数组在执行开始时已经用指定的值初始化。静态数组在C代码中的行为就像内存在RT中的行为一样。

此外,如果变量具有静态限定符,Vivado HLS将在RTL设计和FPGA位流中初始化该变量。这消除了初始化内存需要多个时钟周期。

数组优化小结


  • Xilinx强烈建议对希望成为内存的数组使用static限定符。正如在数组初始化中所指出的,静态类型的行为与RTL中的内存几乎相同。
  • 当只读取数组时,也建议使用const限定符,因为Vivado HLS不能总是通过对设计的分析来推断应该使用ROM。



目录
相关文章
|
存储 算法 异构计算
HLS开发学习-11- for 循环优化(一)
HLS开发学习-11- for 循环优化(一)
385 0
HLS开发学习-11- for 循环优化(一)
|
缓存 调度
HLS开发学习-12- for 循环优化(二)
HLS开发学习-12- for 循环优化(二)
357 0
HLS开发学习-12- for 循环优化(二)
|
缓存 数据处理 索引
HLS实践 - 06 - 优化设计(二)
HLS实践 - 06 - 优化设计
219 0
HLS实践 - 06 - 优化设计(二)
|
存储 人工智能 监控
HLS实践 - 06 - 优化设计(一)
HLS实践 - 06 - 优化设计
290 0
HLS实践 - 06 - 优化设计(一)
HLS开发学习-14- Vivado HLS 函数层面的优化
HLS开发学习-14- Vivado HLS 函数层面的优化
337 0
HLS开发学习-14- Vivado HLS 函数层面的优化
HLS开发学习-15- HLS优化指令汇总
HLS开发学习-15- HLS优化指令汇总
352 0
HLS开发学习-15- HLS优化指令汇总
HLS实践 - 03 - 接口优化设计(一)
HLS实践 - 03 - 接口优化设计
344 0
HLS实践 - 03 - 接口优化设计(一)
HLS实践 - 03 - 接口优化设计(二)
HLS实践 - 03 - 接口优化设计
391 0
HLS实践 - 03 - 接口优化设计(二)
|
存储
HLS实践 - 02 - 简单优化FIR设计
HLS实践 - 02 - 简单优化FIR设计
226 0
HLS实践 - 02 - 简单优化FIR设计
|
存储 人工智能 缓存
HLS介绍 - 02 - HLS原理与软件编译器的区别
HLS介绍 - 02 - HLS原理与软件编译器的区别
388 0
HLS介绍 - 02 - HLS原理与软件编译器的区别