Vitis HLS 学习笔记--矢量数据类型

2024-06-12 08:04

本文主要是介绍Vitis HLS 学习笔记--矢量数据类型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1. 简介

2. 用法详解

2.1 存储器布局

2.2 示例展示 

2.3 综合报告

3. 总结


1. 简介

在 Vitis HLS 中,矢量数据类型是一种特殊的数据类型,它允许你一次处理多个数据元素,就像一排并排的盒子,每个盒子里都装着一个数据元素。这种方式非常适合于同时执行多个相同的操作,这就是所谓的 SIMD(单指令多数据)操作。

矢量数据类型用法

#include <hls_vector.h>
hls::vector<T,N> aVec;

在代码中,#include <hls_vector.h> 这行告诉程序,我们要使用 Vitis HLS 提供的矢量数据类型。hls::vector<T,N> aVec; 这行代码声明了一个矢量变量 aVec。这里的 T 表示数据的类型,比如整数或浮点数,而 N 表示这个矢量中有多少个元素。

当 T 的位宽(即每个数据元素占用的位数)和 N(矢量中元素的数量)都是 2 的幂(比如 2, 4, 8, 16…)时,这个矢量数据类型就能以最高效的方式运行,因为计算机处理这样的数字时更加高效。

打个简单的比方,就像你在超市买东西时,如果你有一个足够大的购物车,你可以一次性把所有东西放进去,然后一起结账,这样就比每次只买一个东西要快得多。在 Vitis HLS 中,矢量数据类型就像是一个大购物车,让你能够一次性处理很多数据,提高效率。

2. 用法详解

2.1 存储器布局

hls::vector<T,N> aVec;

这个数据结构被定义为 hls::vector<T,N>,其中 T 表示矢量中元素的类型,而 N 表示矢量中元素的数量。

存储器连续性:矢量中的元素在内存中是连续存储的。这意味着,如果你知道了矢量中第一个元素的内存地址,就可以通过这个地址和元素的索引(乘以元素的大小)来计算出任何一个元素的内存地址。

存储大小:矢量的总大小(以字节为单位)是元素类型大小 sizeof(T) 与元素数量 N 的乘积。这是因为所有元素都紧密地排列在一起,没有任何间隙。

对齐要求:矢量的对齐要求是其总大小的最大2的幂值。对齐是指数据的起始内存地址是某个数(通常是2的幂)的倍数。这有助于提高内存访问的效率。特别地,当 N 和 sizeof(T) 都是2的幂时,矢量应该对齐到其总大小。这意味着如果你有一个类型大小为4字节(2的2次幂),包含8个元素(2的3次幂)的矢量,那么这个矢量的总大小是32字节(2的5次幂),它应该对齐到32字节。

这种设计与许多计算机架构上的矢量实现相匹配,因为它们通常也有类似的连续存储和对齐要求。这样的设计可以使得数据结构在这些架构上运行得更有效率。

2.2 示例展示 

#include "hls_vector.h"
#include <ap_int.h>// Each vector will be 64 bytes (16 x 4 bytes)
typedef hls::vector<float, 16> float16;template <int N, typename T> void load(T (&out)[N], const T* in) {
#pragma HLS INLINE offfor (int i = 0; i < N; ++i) {
#pragma HLS pipelineout[i] = in[i];}
}template <int N, typename T> void store(T* out, const T (&in)[N]) {
#pragma HLS INLINE offfor (int i = 0; i < N; ++i) {
#pragma HLS pipelineout[i] = in[i];}
}template <int N, typename T, typename S>
void compute(T (&res)[N], const S (&lhs)[N], const S (&rhs)[N]) {
#pragma HLS INLINE offfor (int i = 0; i < N; ++i) {
#pragma HLS pipelineres[i] = lhs[i] + rhs[i];}
}extern "C" void example(float16* res, const float16* lhs, const float16* rhs,int n) {
#pragma HLS INTERFACE m_axi port = lhs offset = slave bundle = gmem0 depth = 32
#pragma HLS INTERFACE m_axi port = rhs offset = slave bundle = gmem1 depth = 32
#pragma HLS INTERFACE m_axi port = res offset = slave bundle = gmem0 depth = 32for (int i = 0; i < n; ++i) {float16 lhs_buf[32];float16 rhs_buf[32];float16 res_buf[32];#pragma HLS DATAFLOWload(lhs_buf, lhs);load(rhs_buf, rhs);compute(res_buf, lhs_buf, rhs_buf);store(res, res_buf);}
}

这段代码中,定义了一种特定的向量类型 float16(由16个浮点数组成,总共64字节),并实现了几个基本操作:从内存加载数据 (load)、将数据存储回内存 (store) 以及执行向量之间的加法 (compute)。

类型定义

  • float16:定义了一个包含16个float元素的向量,每个float占用4字节,因此整个float16占用64字节内存。

函数模板

  • load:从指定的输入指针位置(in)加载N个元素到数组(out)中。这个函数通过循环实现,并使用#pragma HLS pipeline来指示HLS工具将循环的每次迭代实现为一个流水线步骤,以提高执行速度。
  • store:将数组(in)中的N个元素存储到指定的输出指针位置(out)。同样使用#pragma HLS pipeline来优化性能。
  • compute:对两个输入数组(lhs和rhs)进行逐元素加法,将结果存储在数组(res)中。再次使用#pragma HLS pipeline实现流水线加速。

主函数 example

  • 功能:这个函数执行一系列操作,对于给定数量n的float16类型向量(lhs和rhs),它逐个处理这些向量,执行加法运算,并将结果存储在res数组中。
  • 接口指令:#pragma HLS INTERFACE指令定义了函数参数与外部世界的接口方式,这里使用m_axi接口,它是一种适用于内存访问的通用接口。offset = slave指定这些接口作为从设备端口,bundle参数定义了不同的接口被分配到的AXI总线接口,depth参数指定了接口期望的数据深度。
  • 内部缓冲区:函数内部定义了三个float16类型的数组作为缓冲区(lhs_buf、rhs_buf、res_buf),用于存储加载的数据、临时计算结果和最终结果。
  • 数据流:通过#pragma HLS DATAFLOW指令,函数内部的操作被组织成一个数据流图,允许这些操作并行执行,从而提高整体性能。

2.3 综合报告

================================================================
== SW I/O Information
================================================================
* Top Function Arguments
+----------+-----------+---------------------------+
| Argument | Direction | Datatype                  |
+----------+-----------+---------------------------+
| res      | inout     | vector<float, 16>*        |
| lhs      | inout     | vector<float, 16> const * |
| rhs      | in        | vector<float, 16> const * |
| n        | in        | int                       |
+----------+-----------+---------------------------+

 通过 Top Function Arguments 报告,可以查看矢量数据类型的具体信息。

================================================================
== HW Interfaces
================================================================
* M_AXI
+-------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| Interface   | Data Width | Address Width | Latency | Offset | Register | Max Widen | Max Read     | Max Write    | Num Read    | Num Write   |
|             | (SW->HW)   |               |         |        |          | Bitwidth  | Burst Length | Burst Length | Outstanding | Outstanding |
+-------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| m_axi_gmem0 | 512 -> 512 | 64            | 64      | slave  | 0        | 512       | 16           | 16           | 16          | 16          |
| m_axi_gmem1 | 512 -> 512 | 64            | 64      | slave  | 0        | 512       | 16           | 16           | 16          | 16          |
+-------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+

向量类型 float16(由16个浮点数组成,总共64字节),64*8=512,符合综合报告。

3. 总结

在 Vitis HLS 中,矢量数据类型提供了一种高效的数据处理方式,允许开发者利用 SIMD 操作一次性处理多个数据元素。通过使用 hls::vector<T,N>,开发者可以创建一个由 N 个类型为 T 的元素组成的矢量。这种数据结构在内存中连续存储,且当元素类型和数量都是 2 的幂时,对齐到其总大小,可以实现最优的内存访问效率。

示例代码展示了如何定义矢量类型 float16,以及如何实现加载、存储和计算操作。这些操作通过 HLS 指令优化,以流水线的形式执行,从而提高性能。主函数 example 则展示了如何将这些操作组织成数据流,以并行方式执行,进一步提升效率。

综合报告部分突出了矢量数据类型在硬件接口中的配置,如 M_AXI 接口的数据宽度和地址宽度,确保了与向量类型的内存布局相匹配。这种设计使得 Vitis HLS 中的矢量数据类型不仅在软件层面上高效,也在硬件层面上与现代计算机架构紧密对接,实现了数据处理的高效率和高性能。

这篇关于Vitis HLS 学习笔记--矢量数据类型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1053643

相关文章

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

Redis中的常用的五种数据类型详解

《Redis中的常用的五种数据类型详解》:本文主要介绍Redis中的常用的五种数据类型详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis常用的五种数据类型一、字符串(String)简介常用命令应用场景二、哈希(Hash)简介常用命令应用场景三、列表(L

python多种数据类型输出为Excel文件

《python多种数据类型输出为Excel文件》本文主要介绍了将Python中的列表、元组、字典和集合等数据类型输出到Excel文件中,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一.列表List二.字典dict三.集合set四.元组tuplepython中的列表、元组、字典

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06