当前位置: 首页 > news >正文

FPGA学习笔记#5 Vitis HLS For循环的优化(1)

本笔记使用的Vitis HLS版本为2022.2,在windows11下运行,仿真part为xcku15p_CIV-ffva1156-2LV-e,主要根据教程:跟Xilinx SAE 学HLS系列视频讲座-高亚军进行学习

从这一篇开始正式进入HLS对C++代码的优化笔记


学习笔记:《FPGA学习笔记》索引
FPGA学习笔记#1 HLS简介及相关概念
FPGA学习笔记#2 基本组件——CLB、SLICE、LUT、MUX、进位链、DRAM、存储单元、BRAM
FPGA学习笔记#3 Vitis HLS编程规范、数据类型、基本运算
FPGA学习笔记#4 Vitis HLS 入门的第一个工程
FPGA学习笔记#5 Vitis HLS For循环的优化(1)
FPGA学习笔记#6 Vitis HLS For循环的优化(2)
FPGA学习笔记#7 Vitis HLS 数组优化和函数优化
FPGA学习笔记#8 Vitis HLS优化总结和案例程序的优化


目录

  • 1.循环优化中的基本参数
  • 2.PIPELINE & UNROLL
  •     2.1.PIPELINE
  •     2.2.UNROLL
  • 3.LOOP_MERGE

1.循环优化中的基本参数

以如下程序为例,top.h:

#include <ap_int.h>#define N 3#define XW 8
#define BW 16typedef ap_int<XW> dx_t;
typedef ap_int<BW> db_t;
typedef ap_int<BW+1> do_t;void func(dx_t xin[N], dx_t a, db_t b, db_t c, do_t yo[N]);

top.cpp:

#include "top.h"void func(dx_t xin[N], dx_t a, db_t b, db_t c, do_t yo[N])
{int i = 0;loop:for (i = 0; i < N; i++){yo[i] = a * xin[i] + b + c;}
}

从本篇开始,top函数更改为通用的func,这样只改其中的逻辑和参数就行,记得第一次运行要在设置中更改Top函数,更改方式请参考上一篇笔记。

从程序代码出发,对loop标签下的for循环进行分析,可以得到其全过程时序如下,其中每个属性的含义:

1 clock cycle:一个基本操作需要一个时钟周期
Loop Trip Count:循环次数,N==3,所以为3
Loop Iteration Latency:一轮循环执行所需时间
Loop Iteration Interval(Loop II):两轮循环相隔的时间,这里和循环执行时间相等的原因:1.HLS识别到循环次数为常数,省略了对变量i的操作,如果N是变量则这个时间会相应增加。2.在编程中并未让其流水线操作或展开(UNROLL),否则这个时间会减少或直接为0。
Loop Latency:循环总执行时间,等于Loop Iteration Latency * Loop Trip Count
Function Latency:函数的执行时间,这里应该是Rd a,b,c,这三个变量传进来之后不再改变,读一次就行
Function Initial Interval(II):两轮函数的执行间隔,这里应该放在Rd之前,函数放在一个循环内时:1.如果函数执行结束到下一轮该函数执行之间有操作的话,这一时间会增加2.如果对循环和函数进行优化,函数可能并发执行(多个函数实例,间隔为0)、流水线执行(间隔减小)

2.PIPELINE & UNROLL

2.1.PIPELINE

For循环最常见也是最常用的优化就是PIPELINE,可以右键Directive中循环的标签进行添加(所有循环都应添加标签,便于进行优化和DEBUG),也可以在循环的左大括号下一行添加#pragma HLS PIPELINE:

loop:for (int i = 0; i < N; i++){
#pragma HLS PIPELINE/* 函数体 */}

添加PIPELINE后,第一节提到的Loop Iteration Interval (Loop II)循环迭代间隔就变为了1,因为3次循环间没有数据依赖,所以间隔为一个cycle,循环耗时从9减少到5:


2.2.UNROLL

UNROLL会将循环全部展开,如循环次数为3,则将原本1组电路进行3次运算更改为3组电路分别进行1次计算,通过消耗N倍资源换取N倍效率,其添加方法与PIPELINE相同:

#pragma HLS UNROLL

有时,循环的次数实在过多,全部展开会用光板子的资源,那么我们可以将其部分展开,使用UNROLL的参数factor进行配置,如factor=3时,会将循环展开为3组电路,消耗3倍资源换取3倍效率:

// factor为复制份数(消耗资源倍数)
#pragma HLS UNROLL factor=3

我们之前提到过,将C++原本的数据类型换为HLS的任意精度数据类型可以增加资源利用率,那么我们复制了这么多循环,for()中i的类型是否要考虑更换呢?事实上,vitis会考虑i的最大值,也就是N,来对其实际占用的空间进行资源优化,int i = 0;和ap_int<4> i = 0;实际没有区别,如果i<N的N是变量,那么N的数据类型是很重要的,这一点在以后会讲到。

3.LOOP_MERGE

考虑如下图中的情况,add循环和sub循环都用到了a[i]和b[i],并且循环上限都为N,从硬件设计的角度上讲,这两个循环完全可以合并,如右图中的逻辑:

在这里我们引入loop region的概念,它实际上就是对一段大括号括起的代码块声明的标签,在这里用作LOOP_MERGE的作用域。

然后我们对loop region添加LOOP_MERGE约束,与for添加约束的逻辑相同,通过图形界面Directive右键添加,或者在代码块第一行编写#pragma HLS LOOP_MERGE,就可以实现两个循环的并发执行:

void func(data_t a[N], data_t b[N], data_t c[N], data_t d[N])
{int i;loop_region:{
#pragma HLS LOOP_MERGEadd:for (i = 0; i < N; i++){c[i] = a[i] + b[i];}sub:for (i = 0; i < N; i++){d[i] = a[i] - b[i];}}
}

这样不仅能提高效率,并且在代码编写的过程中可以更好的区分逻辑,让代码有更好的可读性和可维护性。

如果两个循环的边界不同,如下图:

合并时将根据更大的循环决定循环次数,这没有问题,较少的那个循环之后就不会获取这个循环模块的sub输出了。

需要注意的是,如果两个(或多个)循环的循环上限有常量也有变量,那么LOOP_MERGE会报错:

而如果循环上限有2个及以上的变量,那么即便变量的范围相同,LOOP_MERGE也会报错:

如何避免这样的问题?如果编程者可以通过设计,预先确定多个变量中哪个变量最小,那么就可以LOOP_MERGE最小变量次数的循环,然后在loop region外执行剩余的循环,如下是能确定K<=J的情况:


下一篇笔记将对For循环的DATAFLOW、嵌套和其他优化进行介绍。


http://www.mrgr.cn/news/70303.html

相关文章:

  • 【计算机网络】设备如何监听 ARP 请求广播
  • Ceph后端两种存储引擎介绍
  • 细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的另一种方法
  • Python 绘图工具详解:使用 Matplotlib、Seaborn 和 Pyecharts 绘制散点图
  • 使用VSCode远程连接服务器并解决Neo4j无法登陆问题
  • Unity3D ASTC贴图压缩格式详解
  • 健身中心健身管理系统的设计与实现(源码+数据脚本+论文+技术文档)
  • 剧本杀app开发,行业发展的新方向
  • qt ui设计案例--登录界面
  • uniapp—android原生插件开发(1环境准备)
  • PostgreSQL 计算两个时间之间的日期差
  • 因为我不停地“抄”爆款选题,终于月入5万
  • 叉车倒车防撞预警系统:保障生产效率和员工安全
  • 2-UML概念模型测试
  • Elasticsearch实战应用:从入门到精通
  • C++网络编程之IO多路复用(三)
  • 丹摩征文活动|快速上手 CogVideoX-2b:智谱清影 6 秒视频生成部署教程
  • Python和Geopandas进行地理数据可视化的实用指南
  • 如何对公司的打印进行记录?打印机打印记录的3个自查小妙招,手把手教会你!
  • MySQL面试必杀技!不会这些,面试官都要哭了,你还想找工作?
  • 【NOIP普及组】摆花
  • 【LeetCode】每日一题 2024_11_11 切棍子的最小成本(区间 DP,记忆化搜索)
  • 堆排序,学习笔记
  • EHOME视频平台EasyCVR宇视设备视频平台1000路监控ip地址如何规划?
  • 期权懂|国内期货期权交易有门槛吗?
  • mysql 配置文件 my.cnf 增加 lower_case_table_names = 1 服务启动不了的原因