Vitis HLS 学习笔记--global_array_RAM初始化及复位

目录

1. 简介

2. 示例代分析

2.1 源代码

2.2 URAM 不可用 

2.3 代码功能解释

2.4 综合报告

2.4.1 顶层控制接口

2.4.2  软件 IO 信息

2.4.3 存储绑定

3. 对比两种 solution

3.1 solution_A

3.2 solution_B

4. 总结


1. 简介

在C++程序中,数组是一种基本的数据结构,用来存储一系列的数据。程序员可以动态地分配和释放数组的内存空间。

但是在将程序综合到硬件上时,就不能再动态地分配内存了,因为硬件需要提前知道需要多少内存来存储数组的数据。在FPGA上,有一个本地存储器,相比于全局存储器(比如DDR或HBM存储器),本地存储器的访问速度更快,通常只需要一个或多个周期。

本文例子展示了如何将全局数组映射到具有不同实现的RAM,并展示了它们如何初始化以及如何重置。

2. 示例代分析

2.1 源代码

#include <ap_int.h>ap_int<10> A[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
ap_int<10> B[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
ap_int<10> C[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};int example(int i) {
#pragma HLS BIND_STORAGE variable = A type = RAM_2P impl = BRAM
#pragma HLS BIND_STORAGE variable = B type = RAM_2P impl = LUTRAMA[i] += B[i] + C[i];B[i] += 5;C[i] += 10;int result = (A[i] + B[i] + C[i]).to_int();return result;
}

2.2 URAM 不可用 

// #pragma HLS BIND_STORAGE variable=C type=RAM_2P impl=URAM 

该指令不可用!

有用户提出了类似的问题,发现即使使用了 #pragma HLS BIND_STORAGE 指令指定将数组存储到 URAM 中,综合报告显示每个 URAM 单元只存储了一个数组元素,而没有充分利用 URAM 的容量。解决方法是在代码中手动对数据进行打包,将多个较小位宽的数打包成一个更大位宽的数后存入 URAM。

这表明,虽然 URAM 可以通过手动优化来使用,但 Vitis HLS 可能没有直接支持全局数组使用 URAM 的内置机制。

2.3 代码功能解释

首先定义三个数组:A、B 和 C,每个数组都包含了 10 个 ap_int<10> 类型的元素。这些数组在 HLS 中被绑定到不同的存储实现类型上。

#pragma HLS BIND_STORAGE 是用于指定存储实现类型的指令。在这里,我们有两个绑定:

  • A 使用 BRAM(Block RAM)作为存储实现类型。
  • B 使用 LUTRAM(Look-Up Table RAM)作为存储实现类型。

然后,对这些数组进行了一系列操作:

  • A[i] += B[i] + C[i]:将 B[i] 和 C[i] 的值相加,并将结果加到 A[i] 上。
  • B[i] += 5:将 B[i] 的值增加 5。
  • C[i] += 10:将 C[i] 的值增加 10。
  • 最后,计算了 A[i] + B[i] + C[i] 的和,并将其转换为整数类型,然后返回该结果。 

2.4 综合报告

2.4.1 顶层控制接口

包含:块级控制协议、时钟、复位、中断;

* TOP LEVEL CONTROL
+-----------+---------------+-----------+
| Interface | Type          | Ports     |
+-----------+---------------+-----------+
| ap_clk    | clock         | ap_clk    |
| ap_rst_n  | reset         | ap_rst_n  |
| interrupt | interrupt     | interrupt |
| ap_ctrl   | ap_ctrl_chain |           |
+-----------+---------------+-----------+

2.4.2  软件 IO 信息

================================================================
== SW I/O Information
================================================================
* Top Function Arguments
+----------+-----------+----------+
| Argument | Direction | Datatype |
+----------+-----------+----------+
| i        | in        | int      |
| return   | out       | int      |
+----------+-----------+----------+* SW-to-HW Mapping
+----------+---------------+----------+-------------------------------------+
| Argument | HW Interface  | HW Type  | HW Info                             |
+----------+---------------+----------+-------------------------------------+
| i        | s_axi_control | register | name=i offset=0x18 range=32         |
| return   | s_axi_control | register | name=ap_return offset=0x10 range=32 |
+----------+---------------+----------+-------------------------------------+

2.4.3 存储绑定

变量A、B、C和编译指令期望的输出是一致的:

================================================================
== Bind Storage Report
================================================================
+-----------+------+------+--------+----------+---------+--------+---------+
| Name      | BRAM | URAM | Pragma | Variable | Storage | Impl   | Latency |
+-----------+------+------+--------+----------+---------+--------+---------+
| + example | 1    | 0    |        |          |         |        |         |
|   B_V_U   | -    | -    | pragma | B_V      | ram_2p  | lutram | 1       |
|   C_V_U   | -    | -    |        | C_V      | ram_1p  | auto   | 1       |
|   A_V_U   | 1    | -    | pragma | A_V      | ram_2p  | bram   | 1       |
+-----------+------+------+--------+----------+---------+--------+---------+

3. 对比两种 solution

3.1 solution_A

对 kernel 代码不做复位相关编译指令,进行综合后,查看图示文件:

在 HLS 设计中,为了初始化全局 RAM 数组(不包括URAM),会生成一种特定的结构,这种结构专门用于配置 BRAM/LUTRAM:

---
变量A
---
(* ram_style = "block" *)reg [DataWidth-1:0] ram[0:AddressRange-1];initial begin$readmemh("./example_A_V_RAM_2P_BRAM_1R1W.dat", ram);
end---
变量B
---
(* ram_style = "distributed" *)reg [DataWidth-1:0] ram[0:AddressRange-1];initial begin$readmemh("./example_B_V_RAM_2P_LUTRAM_1R1W.dat", ram);
end

*.dat文件包含相应数组的初始值。

3.2 solution_B

对 kernel 代码添加优化指令:

#pragma HLS reset variable=A
#pragma HLS reset variable=B
#pragma HLS reset variable=C

进行综合后,可以看到当对 BRAM/LUTRAM 使用复位指令生成的代码结构:

解释:

当在静态数组/全局数组(这里的A、B、C)上使用复位指令后,生成的RTL代码中,每个数组都会通过ROM和RAM来实现。数组的初始值只会被加载到ROM里面,这一点和solution_A是一样的。

但是,每当复位信号被激活,从数组读取的数据就默认会来自ROM。当然,如果有新数据写入了某个地址,该地址将被记录,滞后读取的数据就会转到RAM中。这样做的结果是,每次重置之后,数组都会回到它的初始状态,就像重新启动一样。

三个数组 A/B/C 的相同结构如下所示:

module example_A_V_RAM_2P_BRAM_1R1W
#(parameterDataWidth    = 10,AddressWidth = 4,AddressRange = 10
)(input  wire                    clk,input  wire                    reset,input  wire [AddressWidth-1:0] address0,input  wire                    ce0,output wire [DataWidth-1:0]    q0,input  wire [AddressWidth-1:0] address1,input  wire                    ce1,input  wire                    we1,input  wire [DataWidth-1:0]    d1
);
//------------------------Local signal-------------------
reg  [AddressRange-1:0] written = {AddressRange{1'b0}};
wire [DataWidth-1:0]    q0_ram;
wire [DataWidth-1:0]    q0_rom;
wire                    q0_sel;
reg  [0:0]              sel0_sr;
//------------------------Instantiation------------------
example_A_V_RAM_2P_BRAM_1R1W_ram #(.DataWidth(DataWidth),.AddressWidth(AddressWidth),.AddressRange(AddressRange))
example_A_V_RAM_2P_BRAM_1R1W_ram_u(.clk      ( clk ),.reset    ( reset ),.ce0      ( ce0 ),.address0 ( address0 ),.q0       ( q0_ram ),.ce1      ( ce1 ),.address1 ( address1 ),.we1      ( we1 ),.d1       ( d1 )
);example_A_V_RAM_2P_BRAM_1R1W_rom #(.DataWidth(DataWidth),.AddressWidth(AddressWidth),.AddressRange(AddressRange))
example_A_V_RAM_2P_BRAM_1R1W_rom_u(.clk      ( clk ),.ce0      ( ce0 ),.address0 ( address0 ),.q0       ( q0_rom )
);
//------------------------Body---------------------------
assign q0     = q0_sel? q0_ram : q0_rom;
assign q0_sel = sel0_sr[0];always @(posedge clk) beginif (reset)written <= 1'b0;else beginif (ce1 & we1) beginwritten[address1] <= 1'b1;endend
endalways @(posedge clk) beginif (ce0) beginsel0_sr[0] <= written[address0];end
endendmodule

语句说明:

assign q0     = q0_sel? q0_ram : q0_rom;

用于选择输出数据来源于 RAM 或者 ROM。而 q0 来自于 written[address0]。

reg  [AddressRange-1:0] written = {AddressRange{1'b0}};

用于记录某个地址是否被写入过,写入过的地址会被标记,下次读取时,就会从 RAM 中读取。

4. 总结

在本文中,我们探讨了如何在Vitis HLS中处理FPGA的全局数组映射和初始化问题。通过示例代码,我们了解了如何将C++数组映射到不同类型的RAM,并使用#pragma HLS BIND_STORAGE指令来指定存储实现。我们还讨论了URAM的使用限制和手动数据打包的解决方案。最后,我们比较了两种解决方案:一种是不使用复位指令的solution_A,另一种是使用复位指令的solution_B。solution_B通过ROM和RAM的结合,提供了一种在复位信号激活时能够将数组恢复到初始状态的方法。这些知识对于理解和优化FPGA设计中的内存管理至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/25377.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLM的基础模型8:深入注意力机制

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

在线按模板批量生成文本工具

具体请前往&#xff1a;在线按模板批量生成文本工具

URL的编码解码(一),仅针对ASCII码字符

用十六进制对特定字符编码&#xff0c;利用百分号标识搜索字符串解码十六进制字符。 (笔记模板由python脚本于2024年06月09日 18:05:25创建&#xff0c;本篇笔记适合喜好探寻URL的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free…

Java Set系列集合的使用规则和场景(HashSet,LinkedHashSet,TreeSet)

Set集合 package SetDemo;import java.util.HashSet; import java.util.Iterator; import java.util.Set;public class SetDemo {public static void main(String[] args) {/*Set集合的特点&#xff1a;1.Set系列集合的特点&#xff1a;Set集合是一个存储元素不能重复的集合方…

Vue13-计算属性的简写

一、计算属性的简写 注意&#xff1a; 当计算属性只有get&#xff0c;没有set的时候&#xff0c;才能用简写形式&#xff01;&#xff01;&#xff01;

svn的使用

【图文详解】入职必备——SVN使用教程-CSDN博客 使用SVNBucket作为服务端,来辅助学习. 什么时候会产生冲突呢? 原本A,B,服务器的版本都一致,都是最新版. A修改文件m,向服务器提交 B修改文件m,向服务器提交,这时候出现了冲突 双击冲突的文件,手动修改

---java 抽象类 和 接口---

抽象类 再面向对对象的语言中&#xff0c;所以的对象都是通过类来描述的&#xff0c;但如果这个类无法准确的描述对象的 话&#xff0c;那么就可以把这个类设置为抽象类。 实例 这里用到abstract修饰&#xff0c;表示这个类或方法是抽象方法 因为会重写motifs里的show方法…

【爬虫实战项目一】Python爬取豆瓣电影榜单数据

目录 一、环境准备 二、编写代码 2.1 分页分析 2.2 编码 一、环境准备 安装requests和lxml pip install requests pip install lxml 二、编写代码 2.1 分页分析 编写代码前我们先看看榜单的url 我们假如要爬取五页的数据&#xff0c;那么五个url分别是&#xff1a; htt…

再读高考作文题

新课标I卷&#xff1a;讨论了随着互联网和人工智能的普及&#xff0c;问题是否会变得越来越少&#xff0c;要求考生写一篇文章&#xff0c;表达自己对于这一现象的联想和思考。 从来就没有什么救世主 AI也不是​​​​​ 一直不会写作文&#xff0c;直到高中&#xff0c;才堪堪…

Java Web学习笔记30——打包部署

打包&#xff1a; 到资源管理器中再看下&#xff1a; 将这些文件压缩成一个zip文件&#xff0c;然后到nginx的html目录中执行unzip 解压即可。 部署&#xff1a; Nginx&#xff1a;Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代…

使用JMeter软件压测接口配置说明

1、下载完该软件https://blog.csdn.net/wust_lh/article/details/86095924 2.点击bin文件中jmeter.bat脚本https://blog.csdn.net/wust_lh/article/details/86095924 3.官网地址https://jmeter.apache.org/download_jmeter.cgi 通过 【Options】->【Choose Language】变更为…

双列集合底层源码

tips: 竖着的箭头&#xff1a;重写 横着的箭头&#xff1a;继承

张大哥笔记:经济下行,这5大行业反而越来越好

现在人们由于生活压力大&#xff0c;于是就干脆降低自己的欲望&#xff0c;只要不是必需品就不买了&#xff0c;自然而然消费也就降低了&#xff0c;消费降级未必是不好的现象&#xff01; 人的生物本能是趋利避害&#xff0c;追求更好的生存和发展空间&#xff0c;回避对自己有…

在线渲染3d怎么用?3d快速渲染步骤设置

在线渲染3D模型是一种高效的技术&#xff0c;它允许艺术家和设计师通过互联网访问远程服务器的强大计算能力&#xff0c;从而加速渲染过程。无论是复杂的场景还是高质量的视觉效果&#xff0c;在线渲染服务都能帮助您节省宝贵的时间。 在线渲染3D一般选择的是&#xff1a;云渲染…

生成式人工智能 - 本地windows 11 + PyCharm运行stable diffusion流程简述

一、环境说明 硬件:本地电脑windows11、32.0 GB内存、2060的6G的卡。 软件:本地有一个python环境,主要是torch 2.2.2+cu118 二、准备工作 1、下载模型 https://huggingface.co/CompVishttps://huggingface.co/CompVis 进入上面的网址,我这里下载的是这个里面的 …

雷电模拟器中控实现,直通源码

目录 前言 开发 需求 初始环境 UI搭建 功能实现 前言 本篇为易语言雷电模拟器中控项目实现操作&#xff0c;一般用于&#xff1a;脚本开发多线程模拟操作等起始模板框架&#xff0c;使用易语言原因为其前后端一体化&#xff0c;对于脚本开发而言更为方便。 开发 需求 以…

C语言详解(联合和枚举)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习笔记&#xff0c;在这里撰写…

论文阅读KAN: Kolmogorov–Arnold Networks

学习了最近大热的KAN网络 论文地址&#xff1a;https://arxiv.org/pdf/2404.19756 按我个人读论文的习惯总结了如下几点&#xff1a; 1&#xff0c;背景&#xff1a; 1&#xff09;灵感来源&#xff1a;于Kolmogorov-Arnold表示定理&#xff0c;也就是多变量连续函数可以表…

【人工智能】第七部分:ChatGPT的未来展望

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

LeetCode | 1624.两个相同字符之间的最长子字符串

这道题拿到手想法就是去双重遍历暴力解&#xff0c;对于每个字符&#xff0c;从后往前遍历字符串&#xff0c;找到从后往前一直到本次遍历的这个字符串这段子串中和这个字符串相同的字符位置&#xff0c;然后得到子字符串的长度&#xff0c;和ans存储的值做一个比较&#xff0c…