使用软件: Vivado
开发板: EGO1采用Xilinx Artix-7系列XC7A35T-1CSG324C FPGA

BRAM介绍

BRAM 即块 RAM, 是 FPGA 的固有硬件资源。 另一种形式的 RAM 是分布RAM(Distribution RAM), 是由 FPGA 逻辑资源查找表 LUT 拼起来的 。这两种 RAM 最本质的区别是块RAM 默认输入有寄存器,所以它在读、写使能信号后的下个时钟边沿返回数据,而分布式RAM就没有,就是个组合逻辑,读、写使能的同一时刻返回数据。从时序的角度上来说,块RAM更好,唯一不足的是,它是珍贵的硬件资源。一般来说,芯片越高级,块RAM资源越多。

Artix- - 7 FPGA 的最高型号具有可分配的 13Mbit双端口BRAM, 而实验电路板选型的XC7A35T具备 18Kbit 的BRAM100 个 ,36Kbit 的BRAM 50 个,共1800Kbit 。 Xilinx 7系列 FPGA 可将 BRAM 配置为同步双端口 RAM或单端口 RAM。

同步双端口BRAM

方便通过一个端口写的同时可以从另一个端口进行读取
每个真正的双端口的36Kbit BRAM 包含 36Kbit 个存储单元以及2个完全独立的访问接口A 和B 。每个 18Kbit BRAM 双端口内存包含18Kbit 个存储单元以及2个完全独立的访问接口A和B 。内存的结构完全对称, 双端口可互换。
在这里插入图片描述
双端口接口

BRAM读写操作

(1)读操作

读操作是在一个时钟边沿完成读取RAM指定单元(读地址)内容的操作。首先将读取地址寄存在读端口,并在RAM读取时间之后将存储的数据加载到输出锁存器中。当使用输出寄存器时,读操作需要一个额外的等待周期

(2)写操作

写操作是一个时钟边沿写入RAM的操作。写地址寄存在写入端口,数据输入存储在内存中。

(3)写模式

写模式有三种,决定了写入时钟边沿后,有效数据出现在输出锁存器的时间。
三种模式是: 写优先模式WRITE_FIRST读优先模式READ_FIRST不变模式。每个端口的写模式可以通过配置过程单独配置。
默认模式是写优先模式,在这种模式总是将最新写入的数据送到输出总线上。
在读优先模式,当新的数据被写入时,仍然输出先前存储的数据。
在不变模式,输出总线上保持先前的输出。

写优先模式

在第一个时钟的上升沿,将地址aa的数据MEM(aa)读出放在数据总线DO上。在第二个时钟的上升沿,写有效,因此将DI上的1111写入地址bb。因为写优先,所以出现在数据总线上的是新写入的1111。
写优先

读优先模式

在第一个时钟的上升沿,将地址aa的数据MEM(aa)读出放在数据总线DO上。在第二
个时钟的上升沿,写有效,因此将DI上的1111写入地址bb。因为读优先,所以出现在数据总线上的不是新写入的1111,而是原来地址bb的内存的值old MEM(bb)。
读优先

不变模式

在第一个时钟的上升沿,将地址aa的数据MEM(aa)读出放在数据总线DO上。在第二个时钟的上升沿,写有效,因此将DI上的1111写入地址bb。因为不变模式,所以在写有效的时候出现在数据总线上的数据是不变的,仍然是MEM(aa),直到写无效后的第一个时钟上升沿才变为MEM(dd)。
不变模式

双端口块内存接口

在这里插入图片描述

BRAM IP的使用及仿真验证

IP核使用

coe文件

使用matlab生成.coe文件:
.coe文件
l.coe文件内容如下,注意前两行要添加~
在这里插入图片描述

将IP核添加到工程
  1. 在工程管理栏下点击[IP Catalog],然后搜索RAM,找到块RAM生成器[Block Memory Generator],如下图所示:
    在这里插入图片描述
  2. 将组件名称改为“ram”,其他设置如下图所示:
    读写模式改为读优先“Read First”
    在这里插入图片描述

取消勾选Primitives Output Register,不然输出会滞后一个时钟周期
在这里插入图片描述

  1. 在[Other Options]选项中勾选Load Init File,点击Browse添加前面用matlab生成的要存入RAM的初始数据。
    添加初始数据

代码

verilog代码
`timescale 1ns / 1ps
//
// Module Name: BRAM_test
// Revision 0.01 - File Created
// Additional Comments:
// 
//
module BRAM_test(clk,ena,wea,addra,dina,douta);
input clk,ena,wea;
input[9:0] addra;
input[14:0] dina;
output[14:0] douta;

ram ram(clk,ena,wea,addra,dina,douta);

endmodule
仿真代码
`timescale 1ns / 1ps
//
// Create Date: 2022/10/20 10:59:14
// Design Name: 
// Module Name: sim_bram
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module sim_bram();
reg clk;//时钟输入
reg wea;//写使能
reg[9:0] addra;//地址输入
reg[14:0] dina;//数据输入
wire[14:0] douta;//数据输出
reg[9:0] cnt1,cnt2;
initial begin
    clk=0;
    wea=0;
    cnt1=0;
    cnt2=0;
end
BRAM_test uut(//调用被仿真的模块完成内存读写
.clk(clk),
.ena(1),
.wea(wea),
.addra(addra),
.dina(dina),
.douta(douta)
);
always #10 clk=~clk;//周期20ns,模拟50M时钟
always@(posedge clk)
begin
    if(cnt1==8)
    begin
        cnt1=0;
        cnt2=cnt2+1;//cnt2低位每9个周期翻转一次
    end
    else
        cnt1=cnt1+1;
end
//负边沿写地址,写数据输入,写使能信号,保证时钟上升沿时这些值是稳定的
always@(negedge clk)
begin
    dina=cnt1;//数据输入总线是哪个的值是计数值cnt1
    addra=cnt1;
    if(cnt2[0]==0) wea=0;//每9个周期写使能翻转
    else wea=1;
end
endmodule

仿真结果

仿真结果如下图所示:
仿真结果
初始时,在时钟上升沿读取RAM中addra地址(000 ~ 008)下存储的值(100 ~ 108)。
在写使能wea=1之后,因为是读优先,所以先读出该地址的值,再将dina内容写入,所以看到douta的值还是(100 ~ 108)。
写使能结束之后,再读取ram对应地址中的内容,可以看出douta结果为写入的数值(0 ~ 8)。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐