Block RAM
进阶功能
本章节属于拓展功能,其主要目的是给大家一个做一个简单的进阶参考。进阶实验的目的是锻炼大家的自学能力。因此对本章节内容的答疑可能不会被接受。特此声明。
在实验过程中可能会遇到这样一个问题:由于 Wishbone 总线的介入,访问板子上提供的 SRAM 的速度较慢。但是寄存器的资源又较为有限。我们需要一些更快速的存储设备,同时也需要它能存一定量的数据,来完成 ICache, DCache, BTB, 显存等功能。这时候就需要使用 FPGA 上提供的 Block RAM 了。
有兴趣的同学也可以去参考一下 xpm 的相关内容,这个在编写代码时会更加方便。
引入 IP 核
-
在 IP 目录中找到
Block Memory Generator
-
在
Memory Type
处选择合适的 BRAM 种类- Single Port RAM: 只有 1 个口用于读写 RAM。
- Simple Dual Port RAM: 有两个端口,一个端口只写,另一个端口只读。
- True Dual Port RAM: 两个端口可读可写。
- ROM 为只读存储器
在
Write Enable
栏打开字节使能,并字节大小为 8 bit。如果不需要可以不打开。其他选项不要更改,这里以 True Dual Port RAM 为例
-
进入第二页,设置 1 号口的相关参数:
- Write Width: 写宽度,表示每次写入 data 的 bit 数 (即
DATA_WIDTH
)。 - Read Width: 读宽度,即读取宽度。若打开字节使能,则这两个参数必须为字节大小的倍数。为了方便,请设置读写宽度为相同的值。
- Write Depth: 写深度,即该 RAM 中有多少个长度为 Write Width 的项。如图中配置,则 RAM 的空间大小为
16 * 16 / 8 = 32 Bytes
。 - Read Depth 自动设置
- Operating Mode 的 3 个选项我们将在后面解释。
- Enable Port Type:若选择 Use ENA pin 则表示需要使能端 (类似于 SRAM 中的 ce 信号),Always Enabled 则表示一直开启。
- Primitive Output Register 和 Core Output Register 表示 输出数据是否需要寄存器,如输出需要寄存器,选择 Primitive 和 Core 中的一个即可,这会让输出延迟一拍,但会让信号更加稳定。 剩下的选项不用修改。
如选择双端口 RAM,则继续配置 2 端口的相关参数,注意这里只能改 Width 但是不能改 Depth,因为 RAM 的大小已经由 1 号口的参数确定。
- Write Width: 写宽度,表示每次写入 data 的 bit 数 (即
-
Other Options 页中可以设置 BRAM 的初始化文件,从而让 BRAM 预载对应的值。这会使用到 coe 文件,这部分的内容请自行学习。
-
Summary 页中可以看到你设置的相关参数,以及实际 BRAM 的使用数量等信息。注意 FPGA 上的 BRAM 资源有限,不要开太大(一般 KB 级别已经相当够用了)。同时记得给你的 BRAM 改一个好名字,默认的名字比较奇怪。
信号和时序
这里以一个 Simple Dual Port RAM 为例讲解信号:
写口有以下信号:
- addr(a/b): 地址,输入,编址方式为按照 Write Width 为单位编址。即 addr 为 0 对应第一个 Write Width bits,addr 为 1 对应第二个 Write Width bits,以此类推。
- clk(a/b): 时钟信号,输入
- din(a/b): 即 data in , 输入,表示写的数据
- en(a/b): 对应的使能端,不开启则没有这个信号
- we(a/b): 对应的字节使能端,不开启则没有这个信号
读口有以下信号:
- addr(a/b)
- clk(a/b)
- dout(a/b): 输出,表示读出的数据
- en(a/b)
如果是读写口,不开启字节使能时,仍会有一位的 we(a/b) 信号,此时,这个信号表示整体的写使能:当 en & we
时写 BRAM, 当 en & ~we
时读 BRAM。
Read First , 不开启 Output Register 时的时序图如下:
用 Verilog 代码描述如下:
always_ff @(posedge clk) begin
if(en) begin
if(we) begin
ram[addr] <= din;
dout <= ram[addr];
end else begin
dout <= ram[addr];
end
end
end
Write First , 不开启 Output Register 时的时序图如下:
用 Verilog 代码描述如下:
always_ff @(posedge clk) begin
if(en) begin
if(we) begin
ram[addr] <= din;
dout <= din;
end else begin
dout <= ram[addr];
end
end
end
No Change , 不开启 Output Register 时的时序图如下:
用 Verilog 代码描述如下:
always_ff @(posedge clk) begin
if(en) begin
if(we) begin
ram[addr] <= din;
end else begin
dout <= ram[addr];
end
end
end
总结如下:
- addr 需要 setup 时间,即这周期给地址和信号,下周期拿结果。而且 BRAM 支持连续的读写。
- Read First, Write First, No Change 会影响写入时读出的数据。注意这会影响 Dual Port RAM 在读写地址相同时的读取情况。
更详细的说明和使用方法可以去 Xilinx 官方文档网站查看: Block RAM 文档