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 = LUTRAM

    A[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
#(parameter
    DataWidth    = 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) begin
    if (reset)
        written <= 1'b0;
    else begin
        if (ce1 & we1) begin
            written[address1] <= 1'b1;
        end
    end
end

always @(posedge clk) begin
    if (ce0) begin
        sel0_sr[0] <= written[address0];
    end
end

endmodule

语句说明:

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设计中的内存管理至关重要。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日5题Day18 - LeetCode 86 - 90

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;86. 分隔链表 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;…

为下一波创新做准备:人工智能和元宇宙

人工智能和元宇宙的发展带来了独特的可能性和挑战。随着这些技术的发展&#xff0c;我们进入了一个沉浸式虚拟体验和智能系统的时代&#xff0c;我们正站在一个历史性的时刻。为迎接下一波创新&#xff0c;采取必要的措施是很重要的。 我们正在见证两项变革性技术的激动人心的发…

【TB作品】msp430f5529单片机,dht22,温湿度传感器,OLED显示屏

使用DHT22温湿度传感器和OLED显示屏的单片机项目 博客名称 利用MSP430单片机读取DHT22并显示温湿度 作品功能 本项目利用MSP430单片机读取DHT22温湿度传感器的数据&#xff0c;并将温湿度信息显示在OLED显示屏上。通过这个项目&#xff0c;您可以学习如何使用单片机与传感器…

计算机二级Access操作题总结——简单应用

查询设计 创建一个查询&#xff0c;能够在客人每次结账时根据客人的姓名提示统计这个客人已住天数和应交金额&#xff0c;并显示“姓名”、“房间号”、“已住天数”和“应交金额”&#xff0c;所建查询命名为“qT2”。 注&#xff1a;输入姓名时应提示“请输入姓名”。已住天…

vscode输出控制台中文显示乱码最有效解决办法

当VSCode的输出控制台中文显示乱码时&#xff0c;一个有效的解决办法是通过设置环境变量来确保编码的正确性。以下是解决方式&#xff1a; 首先&#xff0c;设置环境变量以修正乱码问题&#xff1a; 如果上述方法没有解决乱码问题&#xff0c;请继续以下步骤&#xff1a; 右键…

编译原理-语法分析(实验 C语言)

语法分析 1. 实验目的 编制一个递归下降分析程序&#xff0c;实现对词法分析程序所提供的单词序列的语法检查和结构分析 2. 实验要求 利用C语言编制递归下降分析程序&#xff0c;并对简单语言进行语法分析 2.1 待分析的简单语言的语法 用扩充的BNF表示如下&#xff1a; …

Spring AOP(实现,动态原理)详解版

Spring AOP 1.什么是AOP&#xff1f;1.1引入AOP依赖1.2编写AOP程序 2.Spring AOP核⼼概念2.1 切点(Pointcut)2.2连接点(Join Point)2.3通知(Advice)2.4 切⾯(Aspect) 3.通知类型3.1顺序3.2切⾯优先级 Order3.3 ⾃定义注解 MyAspect 4. Spring AOP 原理5 动态代理怎么实现5.1 JD…

BeagleBone Black入门总结

文章目录 参考连接重要路径系统镜像下载访问 BeagleBone 参考连接 镜像下载启动系统制作&#xff1a;SD卡烧录工具入门书籍推荐&#xff1a;BeagleBone cookbookBeagleBone概况&#xff1f; 重要路径 官方例程及脚本路径&#xff1a;/var/lib/cloud9BeagleBone Cookbook例程…

03-3.5.1~4 特殊矩阵的压缩存储

&#x1f44b; Hi, I’m Beast Cheng&#x1f440; I’m interested in photography, hiking, landscape…&#x1f331; I’m currently learning python, javascript, kotlin…&#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以订…

最新区块链论文速读--CCF A会议 CCS 2023 共25篇 附pdf下载(3/4)

Conference&#xff1a;ACM Conference on Computer and Communications Security (CCS) CCF level&#xff1a;CCF A Categories&#xff1a;network and information security Year&#xff1a;2023 Num&#xff1a;25 第1~7篇区块链文章请点击此处查看 第8~13篇区块链文…

【介绍下什么是Kubernetes编排系统】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

java线程池介绍

在Java中&#xff0c;线程池是用来管理和复用线程的一种机制&#xff0c;它可以显著提升程序性能&#xff0c;特别是在大量短期异步任务的场景下。以下是创建和使用线程池的基本步骤&#xff1a; 1.创建线程池: 使用java.util.concurrent.Executors类的静态工厂方法创建线程池&…

【c语言】自定义类型----结构体

结构体是c语言的一种自定义类型&#xff0c;自定义类型对于开发者及其重要的类型&#xff0c;它可以随意由开发者进行谱写功能&#xff0c;而今天的结构体可以用来表示一种变量的单个或多种具体属性&#xff0c;再编写代码时有着不可替代的作用&#xff01;&#xff01;&#x…

编译原理-词法分析(实验 C语言)

编译原理-词法分析 1. 实验目的 设计、编写并调试一个词法分析程序&#xff0c;加深对词法分析原理的理解 2. 实验要求 2.1 待分析的简单语言的词法 关键字&#xff1a;begin&#xff0c;if&#xff0c;then&#xff0c;while&#xff0c;do&#xff0c;end 所有关键字都是…

Nginx(openresty) 查看连接数和并发送

1 通过浏览器查看 #修改nginx配置文件 location /status {stub_status on;access_log off;allow 192.168.50.0/24;deny all;} #重新加载 sudo /usr/local/openresty/nginx/sbin/nginx -s reloadActive connections //当前 Nginx 当前处理的活动连接数。 server accepts handl…

HPC: perf入门

如果你想查看你的程序在cpu上运行时&#xff0c;耗时时如何分布的&#xff0c;那么perf是一个合理的选择。 准备工作 为了支持使用perf&#xff0c;首先你要安装相关的库 sudo apt install linux-tools-5.15.0-67-generic此外&#xff0c;因为使用perf进行benchmark&#xf…

四种跨域解决方案

文章目录 1.引出跨域1.基本介绍2.具体演示1.启动之前学习过的springboot-furn项目2.浏览器直接访问 [localhost:8081/furns](http://localhost:8081/furns) 可以显示信息3.启动前端项目&#xff0c;取消请求拦截器&#xff0c;这样设置&#xff0c;就会出现跨域4.跨域原因 2.跨…

linux指令--sed

sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。 语法解析 sed [选项] 编辑命令 文件 选项&#xff1a; -n&#xff1a;只显示匹配处理的行-e&#xff1a;执行多个编辑命令时-i&#xff1a;在原文件中进行修改&#xff0c;不输出到屏幕-…

零基础入门学用Arduino 第二部分(一)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

系统思考—决策

为‮么什‬模型及‮式公‬通常比人‮决类‬策更有效&#xff1f;究‮是竟‬什么让‮些某‬模型和公‮表式‬现出色&#xff1f;事实上&#xff0c;我‮应们‬该探究的‮人是‬类在决策‮程过‬中的不足。关‮在键‬于人类‮策决‬中存在的“噪声”。尽‮模管‬型或公‮不式‬完…