文章目录
一.RAM简介
- RAM(Random Access Memory),即随机存取存储器,可以将数据写入地址,可以随时从任意指定地址读取数据。时钟频率决定了读取速度。
1.单端口
- 只有一个地址线控制读数据端口和写数据端口
2.双端口
- 两个地址线分别控制读数据端口和写数据端口(双端口又分为伪双端口和真双端口)
2.1伪双端口
- 一组读数据和读地址线,一组写数据和写地址线,能同时进行读和写操作,但不能同时对同一地址进行读和写操作。
2.2真双端口
- 有两组读数据线与读地址线,两组写数据线与写地址线;能同时进行两个端口读,能同时进行两个端口写,也能一个端口读同时另一个端口写。
二.RAM配置(单端口)
-
右侧IP catelog中输入RAM后选择RAM:1-PORT
-
选择保存的位置和文件名
-
配置数据位大小和深度,以及选择单时钟
-
选中复位信号以及使能信号
-
Next后保持默认
-
Next后保持默认
-
Next后保持默认
-
勾选xxx_inst.v文件
-
完成后Quartus弹窗点击yes即可
三.RAM调用(单端口)
- 在工程文件夹下的子文件夹rtl中新建RAM_test1.v文件,在其中调用我们刚生成的_inst.v文件,其代码如下
module RAM_test1(
input clk ,
input rst_n ,
input rden ,
input wren ,
input [7:0] address ,
input [7:0] data ,
output [7:0] q
);
RAM_1port RAM_1port_inst (
.aclr ( ~rst_n ),
.address ( address ),
.clock ( clk ),
.data ( data ),
.rden ( rden ),
.wren ( wren ),
.q ( q )
);
endmodule
- 需要注意的是:和PLL一样,IP核异步复位高有效,系统复位低有效。因此例化时需要取反
四.RAM仿真(单端口)
4.1仿真代码
`timescale 1ns/1ns
module tb_RAM_test1();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
reg tb_rden ;
reg tb_wren ;
reg [7:0] tb_address ;
reg [7:0] tb_data ;
//输出信号定义
wire [7:0] tb_q ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
RAM_test1 u_RAM_test1(
.clk (tb_clk),
.rst_n (tb_rst_n),
.rden (tb_rden),
.wren (tb_wren),
.address (tb_address),
.data (tb_data),
.q (tb_q)
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
integer i,j;
//产生激励
initial begin
tb_rst_n = 1'b1;
#200.1;
tb_rst_n = 1'b0;
tb_wren = 1'b0;//赋初值
tb_rden = 1'b0;
tb_data = 0;
tb_address = 0;
#200;
tb_rst_n = 1'b1;
#200;
//写数据
for(i=0;i<256;i=i+1)begin
tb_wren = 1'b1;//高电平有效
tb_address = i;
tb_data = i * 3;
#20;
end
tb_wren = 1'b0;//写完拉低
#100;
//读数据
for(j=0;j<256;j=j+1)begin
tb_rden = 1'b1;
tb_address = j;
#20;
end
tb_rden = 1'b0;//读完拉低
#200;
$stop;
end
endmodule
4.2仿真效果
-
写数据
-
读数据
-
我们发现会有两个时钟周期的延时,我们可以通过RAM核配置界面的效果图得知,读出的数据q经历了两个触发器,因此会有两个时钟周期延时
五.RAM配置(双端口)
-
右侧IP catelog中输入RAM后选择RAM:2-PORT
-
选择保存的位置和文件名
-
选择一个读模块,一个写模块
-
配置数据位大小和深度
-
选择读写时钟分开
-
选中复位信号
-
Next后保持默认
-
Next后保持默认
-
勾选xxx_inst.v文件
-
完成后Quartus弹窗点击yes即可
六.RAM调用(双端口)
- 在工程文件夹下的子文件夹rtl中新建RAM_test2.v文件,在其中调用我们刚生成的_inst.v文件,其代码如下
module RAM_test2(
input clk ,
input rst_n ,
input [7:0] data ,
input [7:0] rdaddress ,
input rden ,
input [7:0] wraddress ,
input wrclock ,
input wren ,
output [7:0] q
);
RAM_2port RAM_2port_inst (
.data ( data ),
.rd_aclr ( ~rst_n ),
.rdaddress ( rdaddress ),
.rdclock ( clk ),
.rden ( rden ),
.wraddress ( wraddress ),
.wrclock ( wrclock ),
.wren ( wren ),
.q ( q )
);
endmodule
七.RAM仿真(双端口)
7.1仿真代码
`timescale 1ns/1ns
module tb_RAM_test2();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
reg tb_din0 ;
reg [7:0] tb_data ;
reg [7:0] tb_rdaddress;
reg [7:0] tb_wraddress;
reg tb_rden ;
reg tb_wren ;
reg tb_wrclock ;
//输出信号定义
wire [7:0] tb_q ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
RAM_test2 u_RAM_test2(
.clk (tb_clk),
.rst_n (tb_rst_n),
.data (tb_data),
.rdaddress (tb_rdaddress),
.rden (tb_rden),
.wraddress (tb_wraddress),
.wrclock (tb_wrclock),
.wren (tb_wren),
.q (tb_q)
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
initial tb_wrclock = 1'b0;
always #(CLOCK_CYCLE/2) tb_wrclock = ~tb_wrclock;
integer i,j;
//产生激励
initial begin
tb_rst_n = 1'b1;
#200.1;
tb_rst_n = 1'b0;
tb_wren = 1'b0;//赋初值
tb_rden = 1'b0;
tb_rdaddress = 0;
tb_wraddress = 0;
tb_data = 0;
#200;
tb_rst_n = 1'b1;
#200;
//写数据
for(i=0;i<256;i=i+1)begin
tb_wren = 1'b1;
tb_wraddress = i;
tb_data = i * 3;
#20;
end
tb_wren = 1'b0;
#100;
//读数据
for(j=0;j<256;j=j+1)begin
tb_rden = 1'b1;
tb_rdaddress = j;
#20;
end
tb_rden = 1'b0;
#200;
$stop;
end
endmodule
7.2仿真效果
-
写数据
-
读数据