IP核RAM学习

news2024/11/17 8:54:38

        RAM 的英文全称是 Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的。RAM 主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。
1、简介
        Xilinx 7 系列器件具有嵌入式存储器结构,满足了设计对片上存储器的需求。嵌入式存储器结构由一列列 BRAM(块 RAM)存储器模块组成,通过对这些 BRAM 存储器模块进行配置,可以实现各种存储器的功能,例如:RAM、移位寄存器、ROM 以及 FIFO 缓冲器。
        Vivado 软件自带了 BMG IP 核(Block Memory Generator,块 RAM 生成器),可以配置成 RAM 或者ROM。这两者的区别是 RAM 是一种随机存取存储器,不仅仅可以存储数据,同时支持对存储的数据进行修改;而 ROM 是一种只读存储器,也就是说,在正常工作时只能读出数据,而不能写入数据。需要注意的是,配置成 RAM 或者 ROM 使用的资源都是 FPGA 内部的 BRAM,只不过配置成 ROM 时只用到了嵌入式BRAM 的读数据端口。
        Xilinx 7 系列器件内部的 BRAM 全部是真双端口 RAM(True Dual-Port ram,TDP),这两个端口都可以独立地对 BRAM 进行读/写。但也可以被配置成伪双端口 RAM(Simple Dual-Port ram,SDP)(有两个端口,但是其中一个只能读,另一个只能写)或单端口 RAM(只有一个端口,读/写只能通过这一个端口来进行)。单端口 RAM 只有一组数据总线、地址总线、时钟信号以及其他控制信号,而双端口 RAM 具有两组数据总线、地址总线、时钟信号以及其他控制信号。有关 BRAM 的更详细的介绍,请读者参阅 Xilinx 官方的手册文档“UG473,7 Series FPGAs Memory Resources User Guide”。
        单端口 RAM 类型和双端口 RAM 类型在操作上都是一样的,我们只要学会了单端口 RAM 的使用,那么学习双端口 RAM 的读写操作也是非常容易的。本章我们以配置成单端口 RAM 为例进行讲解。
        BMG IP 核配置成单端口 RAM 的框图如下图所示。

        各个端口的功能描述如下:
        DINA:RAM 端口 A 写数据信号。
        ADDRA:RAM 端口 A 读写地址信号,对于单端口 RAM 来说,读地址和写地址共用同该地址线。
        WEA:RAM 端口 A 写使能信号,高电平表示向 RAM 中写入数据,低电平表示从 RAM 中读出数据。
        ENA:端口 A 的使能信号,高电平表示使能端口 A,低电平表示端口 A 被禁止,禁止后端口 A 上的读写操作都会变成无效。另外 ENA 信号是可选的,当取消该使能信号后,RAM 会一直处于有效状态。
        RSTA:RAM 端口 A 复位信号,可配置成高电平或者低电平复位,该复位信号是一个可选信号。
        REGCEA:RAM 端口 A 输出寄存器使能信号,当 REGCEA 为高电平时,DOUTA 保持最后一次输出的数据,REGCEA 同样是一个可选信号。
        CLKA:RAM 端口 A 的时钟信号。
        DOUTA:RAM 端口 A 读出的数据。
2、程序设计
        首先在 Vivado 软件中创建一个名为 ip_ram 的工程,工程创建完成后,在 Vivado 软件的左侧“Flow Navigator”栏中单击“IP Catalog”,如下图所示。

        在“IP Catalog”窗口的搜索框中输入“Block Memory”,出现唯一匹配的“Block Memory Generator”,如下图所示(图中出现的两个 IP 核为同一个 BMG IP 核)。

        双击“Block Memory Generator”后弹出 IP 核的配置界面,接下来对 BMG IP 核进行配置,“Basic”选项页配置界面如下图所示。

        Component Name:设置该 IP 核的名称,这里保持默认即可。
        Interface Type:RAM 接口总线。这里保持默认,选择 Native 接口类型(标准 RAM 接口总线);
        Memory Type:存储器类型。可配置成 Single Port RAM(单端口 RAM)、Simple Dual Port         RAM(伪双端口 RAM)、True Dual Port RAM(真双端口 RAM)、Single Port ROM(单端口 ROM)和 Dual Port ROM(双端口 ROM),这里选择 Single Port RAM,即配置成单端口 RAM。
        ECC Options:Error Correction Capability,纠错能力选项,单端口 RAM 不支持 ECC。
        Write Enable:字节写使能选项,勾中后可以单独将数据的某个字节写入 RAM 中,这里不使能。
        Algorithm Options:算法选项。可选择 Minimum Area(最小面积)、Low Power(低功耗)和 Fixed Primitives(固定的原语),这里选择默认的 Minimum Area。
        接下来切换至“Port A”选项页,设置端口 A 的参数,该页面配置如下:

        Write Width:端口 A 写数据位宽,单位 Bit,这里设置成 8。
        Read Width:端口 A 读数据位宽,一般和写数据位宽保持一致,设置成 8。
        Write Depth:写深度,这里设置成 32,即 RAM 所能访问的地址范围为 0-31。
        Read Depth:读深度,默认和写深度保持一致。
        Operating Mode:RAM 读写操作模式。共分为三种模式,分别是 Write First(写优先模式)、Read First(读优先模式)和 No Change(不变模式)。写优先模式指数据先写入 RAM 中,然后在下一个时钟输出该数据;读优先模式指数据先写入 RAM 中,同时输出 RAM 中同地址的上一次数据;不变模式指读写分开操作,不能同时进行读写,这里选择 No Change 模式。
        Enable Port Type:使能端口类型。Use ENA pin(添加使能端口 A 信号);Always Enabled(取消使能信号,端口 A 一直处于使能状态),这里选择默认的 Use ENA pin。
        Port A Optional Output Register:端口 A 输出寄存器选项。其中“Primitives Output Register”默认是选中状态,作用是打开 BRAM 内部位于输出数据总线之后的输出流水线寄存器,虽然在一般设计中为了改善时序性能会保持此选项的默认勾选状态,但是这会使得 BRAM 输出的数据延迟一拍,这不利于我们在 Vivado的ILA调试窗口中直观清晰地观察信号;而且在本实验中我们仅仅是把BRAM的数据输出总线连接到了ILA的探针端口上来进行观察,除此之外数据输出总线没有别的负载,不会带来难以满足的时序路径,因此这里取消勾选。
        Port A Output Reset Options:RAM 复位信号选项,这里不添加复位信号,保持默认即可。
接下来的“Other Options”选项页用于设置 RAM 的初始值等,本次实验不需要设置,直接保持默认即可。
        最后一个是“Summary”选项页,该页面显示了存储器的类型,消耗的 BRAM 资源等,我们直接点击“OK”按钮完成 BMG IP 核的配置,如下图所示:

        接下来会弹出询问是否在工程目录下创建存放 IP 核的文件,我们点击“OK”按钮即可。
        紧接着会弹出“Genarate Output Products”窗口,我们直接点击“Generate”,如下图所示。

        之后我们就可以在“Design Run”窗口的“Out-of-Context Module Runs”一栏中出现了该 IP 核对应的run“blk_mem_gen_0_synth_1”,其综合过程独立于顶层设计的综合,所以在我们可以看到其正在综合,如下图所示。

        在其 Out-of-Context 综合的过程中,我们就可以进行 RTL 编码了。首先打开 IP 核的例化模板,在“Source”窗口中的“IP Sources”选项卡中,依次用鼠标单击展开“IP”-“blk_mem_gen_0”-“Instantitation Template”,我们可以看到“blk_mem_gen_0.veo”文件,它是由 IP 核自动生成的只读的 verilog 例化模板文件,双击就可以打开它,如下图所示。

        接下来我们创建一个新的设计文件,命名为 ram_rw.v,代码如下:

module ram_rw(
    input               clk        ,  //时钟信号
    input               rst_n      ,  //复位信号,低电平有效
    
    output              ram_en     ,  //ram使能信号
    output              ram_wea    ,  //ram读写选择
    output  reg  [4:0]  ram_addr   ,  //ram读写地址
    output  reg  [7:0]  ram_wr_data,  //ram写数据
    input        [7:0]  ram_rd_data   //ram读数据        
    );

//reg define
reg    [5:0]  rw_cnt ;                //读写控制计数器

//*****************************************************
//**                    main code
//*****************************************************

//控制RAM使能信号
assign ram_en = rst_n;
//rw_cnt计数范围在0~31,写入数据;32~63时,读出数据
assign ram_wea = (rw_cnt <= 6'd31 && ram_en == 1'b1) ? 1'b1 : 1'b0;

//读写控制计数器,计数器范围0~63
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        rw_cnt <= 1'b0;    
    else if(rw_cnt == 6'd63)
        rw_cnt <= 1'b0;
    else
        rw_cnt <= rw_cnt + 1'b1;    
end  

//产生RAM写数据
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        ram_wr_data <= 1'b0;  
    else if(rw_cnt <= 6'd31)  //在计数器的0-31范围内,RAM写地址累加
        ram_wr_data <= ram_wr_data + 1'b1;
    else
        ram_wr_data <= 1'b0 ;   
end  

//读写地址信号 范围:0~31
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        ram_addr <= 1'b0;
    else if(ram_addr == 5'd31)
        ram_addr <= 1'b0;
    else    
        ram_addr <= ram_addr + 1'b1;
end

ila_0 your_instance_name (
    .clk(clk),           // input wire clk

    .probe0(ram_en),     // input wire [0:0]  probe0  
    .probe1(ram_wea),    // input wire [0:0]  probe1 
    .probe2(ram_addr),   // input wire [0:0]  probe2 
    .probe3(ram_wr_data),// input wire [4:0]  probe3 
    .probe4(ram_rd_data) // input wire [7:0]  probe4 
);
endmodule

        模块中定义了一个读写控制计数器(rw_cnt),当计数范围在 0~31 之间时,向 ram 中写入数据;当计数范围在 32~63 之间时,从 ram 中读出数据。
        接下来我们设计一个 verilog 文件来实例化创建的 RAM IP 核以及 ram_rw 模块,文件名为 ip_ram.v,编写的 verilog 代码如下。

module ip_ram(
    input         sys_clk   //系统时钟
    //input         sys_rst_n  //系统复位,低电平有效
    );

//wire define
wire             ram_en      ;  //RAM使能    
wire             ram_wea     ;  //ram读写使能信号,高电平写入,低电平读出 
wire    [4:0]    ram_addr    ;  //ram读写地址 
wire    [7:0]    ram_wr_data ;  //ram写数据  
wire    [7:0]    ram_rd_data ;  //ram读数据  

//*****************************************************
//**                    main code
//*****************************************************
reg [9:0]iReset_cnt;
reg sys_rst_n;

//reset
always @(posedge sys_clk) begin
   if(iReset_cnt<=100)begin
        sys_rst_n<=1'b0;
        iReset_cnt<=iReset_cnt+1'b1;
   end
   else if(iReset_cnt>100)begin
        sys_rst_n<=1'b1;
   end
   else begin
        iReset_cnt<=1'b0;
   end
end

//ram读写模块
ram_rw  u_ram_rw(
    .clk            (sys_clk     ),
    .rst_n          (sys_rst_n   ),
	//RAM
	.ram_en         (ram_en      ),
    .ram_wea        (ram_wea     ),
    .ram_addr       (ram_addr    ),
    .ram_wr_data    (ram_wr_data ),
    .ram_rd_data    (ram_rd_data )
    );

//ram ip核
blk_mem_gen_0  blk_mem_gen_0 (
	.clka  (sys_clk      ),  // input wire clka
	.ena   (ram_en       ),  // input wire ena	
	.wea   (ram_wea      ),  // input wire [0 : 0] wea
	.addra (ram_addr     ),  // input wire [4 : 0] addra
	.dina  (ram_wr_data  ),  // input wire [7 : 0] dina
	.douta (ram_rd_data  )  // output wire [7 : 0] douta
);

endmodule

        程序中例化了 ram_rw 模块和 ram IP 核 blk_mem_gen_0,其中 ram_rw 模块负责产生对 ram IP 核读/写所需的所有数据、地址以和读写使能信号,同时从 ram IP 读出的数据也连接至 ram_rw 模块。
        接下来对 RAM IP 核进行仿真,来验证对 RAM 的读写操作是否正确。tb_ip_ram 仿真文件源代码如下:

module ram_tb();
reg sys_clk;
//reg sys_rst_n; 

always #10 sys_clk = ~sys_clk;

initial begin
    sys_clk = 1'b0;
    //sys_rst_n = 1'b0;
    //#200
    //sys_rst_n = 1'b1;
end

ip_ram u_ip_ram(
    .sys_clk (sys_clk )
    //.sys_rst_n (sys_rst_n )
);

endmodule

        接下来就可以开始仿真了,仿真过程这里不再赘述,仿真波形图如下图所示。

        由上图可知,由于PL端没有系统复位,这里写了一个复位信号,iReset_cnt计数到100后进行复位,之后ram_wea 信号拉高,说明此时是对 ram 进行写操作。ram_wea 信号拉高之后,地址和数据都是从 0 开始累加,也就说当 ram 地址为 0 时,写入的数据也是 0;当 ram 地址为 1 时,写入的数据也是 1,我们总共向 ram 中写入 32 个数据。
        RAM 读操作仿真波形图如下图所示:

        由上图可知,ram_wea 信号拉低,说明此时是对 ram 进行读操作。ram_wea 信号拉低之后,ram_addr从 0 开始增加,也就是说从 ram 的地址 0 开始读数据;ram 中读出的数据 ram_rd_data 在延时一个时钟周期之后(这里之所以有一个时钟周期的延迟,是因为输入端存在一个寄存器,输入地址寄存器,所以从你外部输入读地址到RAM内部锁存该地址需要一个时钟周期。在生成IP核的时候有显示),开始输出数据,输出的数据为 0,1,2……,和我们写入的值是相等的, 也就是说,我们创建的 RAM IP 核从仿真结果上来看是正确的。

        RAM 写操作在 ILA 中观察的波形如下图所示:

        ram_wea 信号拉高之后,地址和数据都是从 0 开始累加,也就说当 ram 地址为 0 时,写入的数据也是 0;当 ram 地址为 1 时,写入的数据也是 1。我们可以发现,上图中的数据变化和在 Vivado 仿真的波形是一致。
        RAM 读操作在 ILA 中观察的波形如下图所示:

        ram_wea(读使能)信号拉低之后,ram_addr 从 0 开始增加,也就是说从 ram 的地址 0 开始读数据;ram中读出的数据 ram_rd_data 在延时一个时钟周期之后,开始输出数据,输出的数据为 0,1,2……,和我们写入的值是相等的。我们可以发现,上图中的数据变化同样和 Vivado 仿真的波形是一致的。本次实验的 IP核之 RAM 读写实验验证成功。

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

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

相关文章

在实验室内里的无奈

洋洋洒洒&#xff0c;随口就来。 不知道什么时候起&#xff0c;我喜欢静谧的环境&#xff0c;身边环境太嘈杂&#xff0c;我就容易心烦意乱&#xff0c;记得以前是完全没有这样的毛病的&#xff0c;不过好在&#xff0c;总是能找到安静的地方&#xff0c;我甚至一度极端&#x…

Vue2 实现带输入的动态表格,限制el-input输入位数以及输入规则(负数、小数、整数)

Vue2 实现el-input带输入限制的动态表格&#xff0c;限制输入位数以及输入规则&#xff08;负数、小数、整数&#xff09; 在这个 Vue2 项目中&#xff0c;我们实现一个限制输入位数&#xff08;整数16位&#xff0c;小数10位&#xff09;以及输入规则&#xff08;负数、小数、…

计算机毕业设计-----Springboot ERP管理系统

项目介绍 系统主要功能包括&#xff1a; 首页 零售管理&#xff1a;零售出库、零售退货&#xff1b; 采购管理&#xff1a;采购订单、采购入库、采购退货&#xff1b; 销售管理&#xff1a;销售订单、销售出库、销售退货&#xff1b; 仓库管理&#xff1a;其它入库、其它出库、…

从零开始搭建一个个人博客并部署发布

1、为什么要自己搭建一个个人博客呢 首先&#xff0c;市场上主流的个人博客有CSDN、掘金、博客园等博客平台&#xff0c;这些平台方便了用户创作、记录的同时&#xff0c;也存在一些弊端&#xff0c;比如某些平台可能你的文章阅读量过高的话&#xff0c;会强制收费等问题已经是…

Linux 软连接ln -s

什么是文件软链接&#xff1f; Linux 的“软连接”与windos 的"快捷方式"相似。文件软链接&#xff08;Symbolic Link 或 SymLink&#xff09;是一种特殊的文件类型&#xff0c;它实际上是一个指向另一个文件或目录的引用。创建软链接时&#xff0c;系统会建立一个新…

线性代数:由矩阵 AB=A 可以推出 B=E 吗?

其实&#xff0c;类似的问题在十几年前的各种提问中就出现了&#xff0c;而且&#xff0c;根据 A B A A BA ABA 推出 B E BE BE 有时候也相当 "符合直觉”&#xff0c;但如果追根问底&#xff0c;矩阵 B B B 到底应该是什么样子的&#xff0c;却很少有详细的解答。 …

高斯数据库 Gauss

gauss DB OLTP 交易 保证数据和安全&#xff0c;主要是银行使用 gauss DB OLAP 分析 大部分是网络公司 gsql 使用gauss数据库的工具 $ gsql -d 数据库名 -p 端口号 -u 用户名 -w 密码 -h 客户端ipgsql 常用参数 -d选项&#xff1a; 指定gsql客户端连接的数据库-h选项&#xff1…

打字侠网站:为何致力于青少年的打字乐趣与成长

在这个数字化时代&#xff0c;计算机已经深刻地渗透到我们的生活中&#xff0c;成为沟通、学习和表达的主要方式。而对于青少年而言&#xff0c;打字作为一项基本技能&#xff0c;不仅仅是应对学业的需要&#xff0c;更是未来发展的必备素养。正是基于这一认知&#xff0c;我决…

Android perfetto memory开源工具分析

目录 原理 官网链接 下载heap_profile producer_support.cc 本地编译 push heapprofd 工具使用 pb文件获取 打开*.pb文件 trace文件 提高系统CPU性能 拆解特定函数内存占用 环境配置 工具使用 修改heap_profile 脚本 原理 Android perfetto memory分析工具和ma…

Netty通信中的粘包半包问题(一)

前言 我们在日常开发过程中&#xff0c;客户端和服务端的连接大多使用的是TCP协议,因为我们要保证数据的可靠传输&#xff0c; 当网络中出现丢包时要求&#xff0c;要求数据包的发送端重传给接收端。而TCP是一种面向连接的传输层协议&#xff0c; 当使用TCP进行传输时&#xf…

Mysql时间差8小时解决方案

目录 1. MySQL 本身的问题1-1. 验证MySQL时间1-2. 修改Mysql时区配置文件修改Mysql时区SQL修改Mysql时区 2.JDBC 连接的问题3. 返回 JSON 时间不对 在开发中&#xff0c;有可能会遇到这种情况&#xff1a; 插入数据库中的时间时正常。但是将时间传到前端页面上显示时&#xff…

【Linux】 nohup命令使用

nohup命令 nohup是Linux和Unix系统中的一个命令&#xff0c;其作用是在终端退出时&#xff0c;让进程在后台继续运行。它的全称为“no hang up”&#xff0c;意为“不挂起”。nohup命令可以让你在退出终端或关闭SSH连接后继续运行命令。 nohup 命令&#xff0c;在默认情况下&…

大数据StarRocks(五) :数据类型

StarRocks 支持数据类型&#xff1a;数值类型、字符串类型、日期类型、半结构化类型、其他类型。您在建表时可以指定以下类型的列&#xff0c;向表中导入该类型的数据并查询数据。 5.1 数值类型 SMALLINT2 字节有符号整数&#xff0c;范围 [-32768, 32767] INT4 字节有符号整…

探索SQL性能优化之道:实用技巧与最佳实践

SQL性能优化可能是每个数据库管理员和开发者在日常工作中必不可少的一个环节。在大数据时代&#xff0c;为确保数据库系统的响应速度和稳定性&#xff0c;掌握一些实用的SQL优化技巧至关重要。 本文将带着开发人员走进SQL性能优化的世界&#xff0c;深入剖析实用技巧和最佳实践…

彻底解决charles抓包https乱码的问题

最近做js逆向&#xff0c;听说charles比浏览器抓包更好用&#xff0c;结果发现全是乱码&#xff0c;根本没法用。 然后查询网上水文&#xff1a;全部都是装证书&#xff0c;根本没用&#xff01; 最后终于找到解决办法&#xff0c;在这里记录一下&#xff1a; 乱码的根本原因…

Paddle模型转ONNX

深度学习模型在硬件加速器上的部署常常要用到ONNX&#xff08;Open Neural Network Exchange&#xff0c;开放神经网络交换&#xff09;格式&#xff0c;也可以通过ONNX实现不同AI框架&#xff08;如Pytorch、TensorFlow、Caffe2、PaddlePaddle等&#xff09;之间的模型转换。 …

MySQL-外键等信息

38. 基础-多表查询-概述_哔哩哔哩_bilibili 1、流程函数 2、约束字段 删除外键 &#xff1a; alter table emp2 drop foreign key 外键名 //外键可以保持数据的一致性和完整性&#xff0c;外键的话&#xff0c;就是类似一个主表&#xff0c;一个从表&#xff0c;从表的其中一…

【grpc】利用protobuf实现java或kotlin调用python脚本,含实现过程和全部代码

前言 在一些特殊场景中&#xff0c;我们可能需要使用java或者其他任意语言调用python脚本或sdk等。本文的需求衍生也不例外于此&#xff0c;python端有sdk&#xff0c;但只能在python中调用&#xff0c;于是就有了本文章。 常见的调用方式如jython、python提供http rest接口、…

波动,热传导,扩散方程建立

数学物理方程是从自然科学的各个领域和工程技术领域中导出的偏微分方程和积分方程.在这些以偏微分方程为基础的数学模型中&#xff0c;二阶线性偏微分方程中的三个典型方程与定解条件的建立、解法及其应用&#xff0e;描述振动和波动过程的波动方程、描述输运过程的热传导&…

2024年MIA最新生成综述:基于深度学习的MRI/CT/PET合成【文献阅读】

2024年MIA最新生成综述&#xff1a;基于深度学习的MRI/CT/PET合成【文献阅读】 基本信息 标题&#xff1a;Deep learning based synthesis of MRI, CT and PET: Review and analysis发表年份: 2024期刊/会议: Medical Image Analysis分区&#xff1a; SCI 1区IF&#xff1a;1…