11111111111111# 前言
ram分为分布式ram(distributed ram)以及块ram(block ram),
RAM分类 | 实现方法 |
---|---|
ram(distributed ram) | 用寄存器写出来的储存单元,仅仅用于储存比较少量的数据。优点是dram不需要时钟线来控制,可以直接用组合逻辑进行控制。 |
块ram(block ram) | 定制的ram资源,控制起来需要用时钟线,以及其他UI界面设定的io接口进行配置。 |
可以选择的类型有如下五个,其中两种是ROM,三种是RAM。
- single - port RAM(简单单口RAM)
端口 | 说明 |
---|---|
ADDRA | 地址线 |
DINA | 数据输入端 |
ENA | 为可选端口,在其为高电平时,才使能。 |
WEA | 写使能,当其为高电平时,DINA数据才会被写入对应的地址,这里[0:0],仿真的时候会发现,这里的数据不同于时钟线,而是数据。” |
DOUTA | A输出端 |
CLKA | 时钟线 |
此文章先介绍这种RAM的使用,其他RAM的使用方法会在接下来的文章陆续介绍。
一、IP的UI配置
这里我选择使用最简单的单端口配置。数据的位宽为8bit,数据存储的深度为1024。也就是在FPGA的内部申请了1KB的RAM。
二、实现功能
1、往RAM中写入数据,同时读出数据
- 复位信号拉高以后,WEA拉高(写数据使能打开)。
- 在每一个时钟的上升沿,写入数据与写入数据的地址保持一致从0开始累加,一直累加到200,然后开始新一轮的循环。
`timescale 1ns / 1ps
module my_ram_tst(
input sys_rst_n,
input sys_clk
);
reg [ 0: 0 ] ram_wr_en ; //ram写使能
reg [ 9: 0 ] ram_addr ; //ram读写地址
reg [ 7: 0 ] ram_wr_data; //ram写数据
wire [ 7: 0 ] ram_rd_data; //ram读数据
/* block_ram例化 */
block_ram my_block_ram (
.clka( sys_clk ), // input clka
.wea( ram_wr_en ), // input [0 : 0] wea
.addra( ram_addr ), // input [9 : 0] addra
.dina( ram_wr_data ), // input [7 : 0] dina
.douta( ram_rd_data ) // output [7 : 0] douta
);
/* 写使能信号 */
always@( posedge sys_clk or negedge sys_rst_n )
begin
if ( !sys_rst_n )
ram_wr_en <= 0;
else
ram_wr_en <= 1;
end
/* RAM的写地址和写数据改变 */
always@( posedge sys_clk or negedge sys_rst_n )
begin
if ( !sys_rst_n )
begin
ram_addr <= 0;
ram_wr_data <= 0;
end
else if ( ram_addr < 200 )
begin
ram_addr <= ram_addr + 1;
ram_wr_data <= ram_wr_data + 1;
end
else
begin
ram_addr <= 0;
ram_wr_data <= 0;
end
end
endmodule
截取的一部分的仿真图如图所示
可以发现DOUTA(输出数据线输出的数据)总是比ADDRA(输入的地址)的时钟滞后一个时钟周期。为了验证这一猜想,开始接下来的验证。
2、读取RAM验证
先往RAM中的地址【1:200】中写入与地址一样的数据,然后关闭写使能信号线,改变地址,看输出的数据是否滞后一个clk。
代码
`timescale 1ns / 1ps
module my_ram_tst(
input sys_rst_n,
input sys_clk
);
reg [ 0: 0 ] ram_wr_en ; //ram写使能
reg [ 9: 0 ] ram_addr ; //ram读写地址
reg [ 7: 0 ] ram_wr_data; //ram写数据
wire [ 7: 0 ] ram_rd_data; //ram读数据
reg W_R_EN;
/* block_ram例化 */
block_ram my_block_ram (
.clka( sys_clk ), // input clka
.wea( ram_wr_en ), // input [0 : 0] wea
.addra( ram_addr ), // input [9 : 0] addra
.dina( ram_wr_data ), // input [7 : 0] dina
.douta( ram_rd_data ) // output [7 : 0] douta
);
/* 写使能信号 */
always@( posedge sys_clk or negedge sys_rst_n )
begin
if ( !sys_rst_n )
ram_wr_en <= 0;
else
if ( W_R_EN )
ram_wr_en <= 1;
else
ram_wr_en <= 0;
end
/* RAM的写地址和写数据改变 */
always@( posedge sys_clk or negedge sys_rst_n )
begin
if ( !sys_rst_n )
begin
ram_addr <= 0;
ram_wr_data <= 0;
W_R_EN <= 0;
end
else if ( ram_addr < 200 )
begin
ram_addr <= ram_addr + 1;
ram_wr_data <= ram_wr_data + 1;
end
else
begin
ram_addr <= 0;
ram_wr_data <= 0;
W_R_EN <= ~W_R_EN;
end
end
endmodule
- 阶段1:此时WEA信号为低电平,此时虽然DINA(数据输入)和ADDRA(地址)都在变化,但是DOUTA并没有发生变化,说明此时数据没有被写进去。
- 阶段2:此时WEA信号为高电平,此时DINA(数据输入)和ADDRA(地址)都在变化,DOUTA也在变化,说明此时数据被写进去。
- 阶段3:此时WEA信号为低电平,此时虽然DINA(数据输入)和ADDRA(地址)都在变化,但是由阶段一得到的结论我们可以知道,数据并没有被写进去,而数据输出依然在随着地址的变化而变化。 放大阶段3如下图
由上边的实验可以看到无论写信号是否有效,地址线对应的数据总是在下一个时钟周期出现在读数据总线上。