HLS学习笔记——实现卷积层的加速计算

软件实现卷积的原理

在这里插入图片描述
上图是卷积计算的原理图

  • 首先说明一下图中的各个参数:
    • CHin:输入特征图的通道数(channel depth)
    • CHout:输出特征图的通道数
    • R:输出特征图的行数(Row)
    • C:输出特征图的列数(Column)
    • K:卷积核的大小(kennel size)
    • S:步进步数(stride)
  • 对于输出的特征图,其计算公式如下图所示

在这里插入图片描述
注意当卷积核在输入的特征图上进行滑动时,需要乘上步进步数

  • 将上图中的计算公式转换成C语言代码

在这里插入图片描述

常用时间术语总结

在对卷积核运算进行硬件加速前,需要对HLS中的一些常用的时间术语进行解释

  • Area:实现该C代码多用的资源量,该资源包括LUT、registers、Block RAM、DSP48等等。
  • Latency:C函数完成所有的一次输出所需要的周期数。
  • Initiation interval(II):C函数需要多少时间才可以重新接受新的数据,也就是C函数本次开始到下一次开始所需要的周期数。
  • Loop iteration latency:C函数中的for循环每迭代一次需要多少时钟周期。
  • Loop initiation interval:本次循环开始到下一次循环开始所需要的周期数。
  • Loop latency:完成整个循环需要多少个时钟周期。
  • Trip Count:for循环的循环迭代的次数。

通过下方两个图可以更好理解上方的这些术语

在这里插入图片描述

在这里插入图片描述

HLS实现

代码实现(未优化)

卷积运算的各参数取值如下图所示(其中步进步数S为1)

在这里插入图片描述

  • 首先定义三个多维数组
    在这里插入图片描述

  • 分别代表输入特征图输出特征图以及卷积核的权值。其中卷积核中的CHout表示卷积核的个数,每个卷积核都是一个三维数组,并且通道数CHin都和输入特征图一致

  • 然后编写卷积运算的循环体代码
    在这里插入图片描述

  • 其中循环体的顺序为:
    Output_Channel --> Input_Channel --> Row --> Column --> Kernel_Row --> kernel_Column

  • 仿真后的性能报告如下图所示
    在这里插入图片描述

加速器架构

在这里插入图片描述

  • 本文的加速方案是从Channel层面进行展开的。也就是当卷积核在对输入的特征图进行乘法操作时,对每一层Channel的计算作为一个处理元件( Processing Elements,简称PE),这些PE将进行并行运算。而对每一个单独的PE在进行Pipeline展开。同时对每个待操作的多维数组数组也要在Channel维度上进行展开(Partition),以适应PE的并行计算。

添加约束条件(Directive)

循环展开

  • 首先要对循环体中的Output_Channel以及Input_Channel循环进行Unroll展开,具体代码如下:

在这里插入图片描述

  • 同时,对多维数组也要进行Channel维度上的展开

在这里插入图片描述

  • 其中数组In和数组Out分别在CHin和CHout维度展开,而数组W(卷积核权重)同时包含两个维度,需要同时在两个维度都展开。
  • 最后经过仿真的报告如下:

在这里插入图片描述

  • 从图中可以看到,经过Unroll的并行优化后,程序的Latency明显下降了。

循环体流水化处理

  • 由上一步Unroll后的结果报告,我们可以观察到,循环体并没有进行Pipeline展开,因此还有进一步优化的余地。下面我们对Outout_Channel和Input_Channel循环体进行Pipeline展开。

在这里插入图片描述

  • 由于Pipeline的特性,Pipeline内部的循环自动进行Unroll展开,所以我们不需要在额外的添加展开的Directive。
  • 下面是仿真后的结果报告:

在这里插入图片描述

  • 从报告中我们可以看出,经过流水化处理之后,程序的Latency得到了进一步的减少。
  • 但是,同时我们也发现,Pipeline结果中的Initiation Interval并没有达到理想中的1个时钟周期。

循环体顺序问题

  • 上次的仿真报告中II不为1的原因,其实是和循环体的顺序有关。
  • 由于最后的输出out [ cho ] [ r ] [ c ]其实只与输出的Channel输入特征图的行r以及输入特征图的列c有关。
  • 如果将rc放在循环体的最外侧的话,那么程序的运行顺序就如下图所示。

在这里插入图片描述

  • 可以看到相邻两次循环之间,Iteration 0的输出与Iteration 1的输入存在一定的读写关系。也就是说下一次循环必须等到上一次循环完成,并将结果写入到RAM中,下一次循环才能读取到上一次的结果,开始下一次的循环。正因为存在这一层的读写关系,所以才不能实现Pipeline来对循环体进行有效的加速。
  • 想要解决这一问题,其实方法也很简单,只需要将循环体的顺序调整一下。将krkc的循环与rc的循环位置调换一下,使得对rc的循环在整个循环体的内测,就可以避免两次循环之间的读写关系。

在这里插入图片描述
在这里插入图片描述

  • 上图即为循环体顺序调换过后程序的运行时序图,可以看到相邻的循环之间没有了依赖关系,相应的II也变为了1。
  • 11
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
通过引用可以得知,对于HLS加速YOLO卷积层,可以使用一种高效普适性的加速器IP核。这个IP核可以用于硬件加速,并根据YOLO v2算法的不同网络层的运算功能进行硬件设计优化。在这个加速器IP核中,卷积层、池化层和重排序层的运算都会使用到同一IP核。该加速器IP核会从DRAM中持续读取数据并存储在不同的缓存单元中,在输出特征图数据复用的方式下完成不同的运算,并将最终的结果写回DRAM。对于数据复用策略,可以通过加载每个输出特征映射的权重,在计算开始之前进行数据复用,以减少访存降低时延。此外,还可以对输入特征图进行数据复用,即重复使用同一块输入特征图进行计算,并在所有卷积计算完成后再更新输入数据。层融合是将YOLO v2的主要网络层融合到一起,将占据大部分运算量的卷积层和池化层都看作卷积运算,将路由层和重排序层视为存储重新整理,并在PL侧实现这些网络层。最后,通过高层次综合(High-level Synthesis,HLS)工具,可以将电路设计规范的算法级或行为级描述转化为电路结构描述的方法和过程,实现软件算法设计的硬件加速硬件电路设计的抽象级设计和验证。使用Xilinx公司的Vivado HLS工具,可以将C、C++或SystemC语言编写的算法综合成VHDL或Verilog HDL语言的RTL代码,从而更高效地完成设计和仿真。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [目标检测YOLO实战应用案例100讲-基于FPGA的目标检测硬件加速技术及其应用研究](https://blog.csdn.net/qq_36130719/article/details/130768007)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值