一、RAM介绍
RAM为随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的。RAM主要用来存放程序及程序执行过程中产生的中间数据、运行结果等。其特点适合双向交换数据。
RAM的端口有单端口和双端口:
其他类型的存储器还有:
(1)ROM,只读存储器,系统上电后数据被写入ROM,运行过程中只能从ROM读取数据,而不能改变ROM中的数值;
(2)FIFO(不寻址,操作简单):适合数据缓冲和跨时钟域数据同步处理
RAM、ROM、FIFO都是FPGA提供的存储单元。
二、创建IP核
以上一篇文章为参考,创建RAM IP 核
找到图中RAM IP 的设置位置:
在ip工程文件夹下创建ram_1port.v文件,并导入,进入ip核参数设置。
输入输出数据位宽设置为8bits,64个字节(64个存储单元,一个字节8bits),可以看到左下角可以存储1M9K大小的数据,最大显示64字节所用的地址位宽为6位;存储的样式设为自动,一般默认M9K;输出输出共用同一个时钟,即设为单时钟。
进入next,选择输出作为寄存器(插入了一个寄存器),勾选清零,即进行异步复位;勾选使能信号:(注意,此处的输出时钟,只是针对于输出端的寄存器,而不针对于输出端口,即‘q’output port被设置为寄存器,所以数据丢失是在引脚和输出端口之间的寄存器的位置)
点击next:
无需选择初始化:
接下来就是同样的步骤:
添加进工程里:
三、硬件语言描述
(1)顶层文件:
module test_ip (
input rst_n,
input clk,
input [5:0] address,
input [7:0] data,
input wren,
input rden,
output [7:0] q
);
//在调用ram时,此时pll中的时钟信号就变为了中间信号
wire c0 ;//25M
wire c1 ;//100M,90°相移
wire c2 ;//100M
wire c3 ;//25M,25%占空比
wire c4 ;//70M
wire locked ;
wire inclk ;//添加时钟中间信号,便于更改时钟
assign inclk = c2 & locked;//locked观察时钟是否锁定,为了不影响ram所以&locked
//能看到输出稳定的时钟信号,如果不&,c2输出时钟就为不定态
pll pll_inst (
.areset ( ~rst_n ),//根据手册,pll复位高电平有效,而设计的rst_n为低电平有效
.inclk0 ( clk ),
.c0 ( c0 ),
.c1 ( c1 ),
.c2 ( c2 ),
.c3 ( c3 ),
.c4 ( c4 ),
.locked ( locked )
);
ram_1port ram_1port_inst (
.aclr ( ~rst_n ),//复位信号低电平有效,手册ram高电平有效
.address ( address ),
.clock ( inclk ),//选用100M时钟
.data ( data ),
.rden ( rden ),
.wren ( wren ),
.q ( q )
);
endmodule
(2) 测试文件:
`timescale 1ns/1ps
module test_tb();
reg rst_n ;
reg clk ;
reg [5:0] address ;
reg [7:0] data ;
reg wren ;
reg rden ;
wire[7:0] q ;
parameter CYCLE = 20;
test_ip u_test_ip (
.rst_n ( rst_n ),
.clk ( clk ),
.address ( address ),
.data ( data ),
.wren ( wren ),
.rden ( rden ),
.q ( q )
);
integer i ;//加入i个存储单位(字节),ram中设置的64个字节
initial begin
clk = 1'b1;
rst_n = 1'b1;
#(2*CYCLE);
rst_n = 1'b0;
//时钟复位后给所有信号一个初值
address = 0 ;
data = 0 ;
wren = 0 ;
rden = 0 ;
//延迟5个周期释放
#(5*CYCLE);
rst_n = 1'b1;
#(CYCLE*10);//延迟10个时钟是为了打开时钟锁得到稳定的时钟信号
//写数据
for(i=0;i<64;i=i+1) begin
wren = 1'b1 ;
data = i + 1 ;//data给1到64
address = i ;//地址给0到63刚好与data错开
#(CYCLE/2) ;//保证一个时钟周期写写完一个数据
end
wren = 1'b0 ;//写完数据拉低
#(200*CYCLE) ;
//读数据
for (i=0;i<64;i=i+1) begin
rden = 1'b1 ;
address = i ;//写数据读地址
#(CYCLE/2) ;//保证一个是时钟周期读完一个数据
end
rden = 1'b0 ;//读完数据拉低
#(200*CYCLE) ;
$stop;
end
always #(CYCLE/2) clk = ~clk ;//50M
endmodule
四、仿真波形
修改test.do文件,在modelsim中仿真。
仿真分析:
全局分析:当wren信号拉高,rden信号拉低时,数据data读0到64,地址address读0到63,而q无数据;待到rden信号拉高时,输出q才有数据0到64。
局部分析:
locked拉高时时钟c2稳定输入,复位信号arest有效时,开始有数据
经过200个时钟周期,读数据,可以看到,读使能拉高,并在时钟c2上升沿时数据进行变化,当address为0时,输出q应该等于data对应的数据1,但在这里并不是立马读出数据,而是在下一个c2时钟上升沿才有数据1 ,同理,在结束时候也要在下一个时钟上升沿输出64.
五、补充
如果例化IP核时用了双时钟,对比单时钟来说,双时钟只是对输入和输出分别用了两个时钟来各自控制,但是在编写测试文件时完全不用改变代码,因为测试文件不能对输出q进行操作,只能对输入端进行激励,即只与输入时钟有关,与输出时钟无关。
对于复位清零,双时钟会变为输出端的复位清零,但是实际上和单时钟的效果是一样的,都是对寄存器进行清零,而寄存器位于输出端。
文章来源:https://www.toymoban.com/news/detail-604184.html
文章来源地址https://www.toymoban.com/news/detail-604184.html
到了这里,关于FPGA-基本IP核的应用之RAM的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!