前言
随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。它可以随时读写(刷新时除外),而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。RAM工作时可以随时从任何一个指定的地址写入(存入)或读出(取出)信息。它与ROM的最大区别是数据的易失性,即一旦断电所存储的数据将随之丢失。RAM在计算机和数字系统中用来暂时存储程序、数据和中间结果。
本章节将练习如何使用FPGA设计并使用RAM。
正文
一、IP核ram调用及验证
1.项目需求
ram称为随机存储器,能够控制往里面写入数据,也可以控制将里面写入的数据读取出来,对于一个存储位置,在写入时可以一直覆盖写入,能够控制读取的数据最多只有两个。可以随时随地更新存储的数据,也可以随时随地将写入的数据读取出来;ram也分为单端口ram和双端口ram,单端口ram可以用来对数据进行缓存,数据在写入和读取时需要在同一个时钟域下;双端口主要应用在跨时钟域下的数据信号出来。
2.技术介绍
在写入时需要根据写地址控制数据写入到该地址上,地址和数据需要保持同拍,在同一个时钟周期下,同时也需要写使能信号wren进行控制写入。在读取时需要根据读地址读取当前地址上的数据,在对读使能信号rden没有进行设置时,默认读使能信号一直有效。
单端口ram只有一个地址变量,那么地址变量就包含两层含义:写地址含义,读地址含义;在写使能和读使能同时有效时,地址变量既是写地址,同时也表示读地址。将Ip核rom中的数据写入到单端口ram中,并全部读取出来,在写入时不进行数据的读取。
调用单端口RAM,配置总线宽度8,数据深度256
添加读使能信号,后面设置保持默认
生成inst.v文件
3.顶层架构
这里调用rom将rom中数据写入到ram中,并通过ram读出数据。
4.端口描述
clk | 时钟(50Mhz) |
rst_n | 复位按键(低电平有效) |
[7:0] data | 数据读出 |
二、代码验证
wr_ram_ctrl模块:,读取rom数据,写入到RAM中,读取RAM中的数据
module wr_ram_ctrl(
input clk,
input rst_n,
input [7:0] data,//从rom读取的数据
output reg [7:0] addr,//写/读RAM的地址
output [7:0] rom_addr,//传递给rom的地址
output reg wren,//写使能信号
output rden,//读使能信号
output [7:0] wr_data//写入RAM的数据
);
reg en;
reg [7:0] cnt;
assign wr_data = data;//写入RAM的数据=从rom读取的数据
assign rden = ~wren;//读完写,写完读,什么时候读完?
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
begin
en <= 1'b1;
cnt <= 8'd0;
end
else
if(cnt == 255)//0~255,读/写到第255位数据时,rom的数据读/写完
begin
en <= ~en;
cnt <= 8'd0;
end
else
begin
en <= en;
cnt <= cnt + 8'd1;
end
end
assign rom_addr = (en == 1)?cnt:8'd0;//传递给rom的地址,组合逻辑赋值,立即传递
reg wren_r;
wire pos_flag;
wire neg_flag;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
begin
wren <= 1'b1;
wren_r <= 1'b1;
end
else
begin
wren <= en;/en为写信号
wren_r <= wren;
end
end
assign pos_flag = ~wren_r & wren;
assign neg_flag = ~wren & wren_r;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
addr <= 8'd0;
else
addr <= cnt;//读/写ram的地址与读rom的地址相同,
//但是赋值的更新是在时钟边沿之后的一个时钟周期内发生的
end
endmodule
ram_test模块:顶层连线
module ram_test(
input clk,
input rst_n,
output [7:0] data
);
wire wren;
wire rden;
wire [7:0] addr;
wire [7:0] wr_data;
wire [7:0] rom_addr;
wire [7:0] rom_data;
wr_ram_ctrl wr_ram_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.data(rom_data),
.addr(addr),
.rom_addr(rom_addr),
.wren(wren),
.rden(rden),
.wr_data(wr_data)
);
my_rom my_rom_inst (
.address ( rom_addr ),
.clock ( clk ),
.q ( rom_data )
);
single_ram single_ram_inst (
.address ( addr ),
.clock ( clk ),
.data ( wr_data ),
.rden ( rden ),
.wren ( wren ),
.q ( data )
);
endmodule
仿真代码
`timescale 1ns/1ps
module ram_test_tb;
reg clk;
reg rst_n;
wire [7:0] data;
double_ram_test ram_test_inst(
.clk(clk),
.rst_n(rst_n),
.data(data)
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst_n = 0;
#200
rst_n = 1;
#10000
$stop;
end
endmodule
三、仿真验证
数据都可正常读出,下面验证数据是否正确
ROM的数据读取会延迟地址一个时钟
读出的数据在同一时钟下写入到RAM中,
当cnt记到255时,即写地址到255时,数据从ROM中读完,RAM中写完
大多数RAM IP核,特别是同步RAM IP核,设计时会考虑时钟边沿对数据稳定性的要求。为了确保数据读取操作的稳定性,RAM通常会在读取操作时引入延迟。这个延迟通常表现为两个时钟周期:
第一个时钟周期:地址和读信号被传递到RAM。在这一周期内,RAM完成地址的解码,并开始内部读取操作。
第二个时钟周期:在地址稳定的情况下,RAM内部完成数据准备并将数据输出到数据总线。这通常意味着数据将在下一个时钟周期变为有效。
读出地址到255后,数据从RAM读完毕,由于RAM的特性,数据需要延后2个时钟才能完全读出,读完毕后立刻进入写模式。