文章目录
- 前言
- 一、RAM简介
- 1、随机存储器IP核分类
- 1、RAM IP核
- 2、ROM IP核
- 2、RAM IP核
- 二、IP核配置步骤
- 三、源码
- 1、ram_rw驱动文件
- 2、ip_1port_ram顶层文件
- 3、仿真文件
- 4、仿真波形
- 四、SignalTap II在线调试
- 五、总结
- 六、参考资料
前言
环境:
1、Quartus18.1
2、vscode
3、板子型号:原子哥开拓者2(EP4CE10F17C8)
要求:
调用RAM IP核进行单端口RAM的读写数据。
一、RAM简介
RAM(Random Access Memory),即随机存储器。它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的。RAM 主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。
1、随机存储器IP核分类
在Cyclone IV 器件中,具有嵌入式存储器结构,嵌入式存储器结构由一系列M9K存储器模块进行配置,可以实现各种存储器功能,例如:RAM、移位寄存器、ROM以及FIFO缓冲器。
1、RAM IP核
RAM 是一种随机存取存储器,不仅仅可以存储数据,同时支持对存储的数据进行修改。
2、ROM IP核
是一种只读存储器,也就是说,在正常工作时只能读出数据,而不能写入数据。
- 注意:
这两种存储器使用的资源都是 FPGA 的内部嵌入式 RAM 块,只不过 ROM IP 核只用到了嵌入式 RAM 块的读数据端口。
2、RAM IP核
Altera 推出的 RAM IP 核分为两种类型:单端口 RAM 和双端口 RAM。单端口 RAM 只有一组地址线,这组地址线控制着写数据端口和读数据端口,而双端口 RAM 具有两组地址线,这两组地址线分别控制着写数据端口和读数据端口。
- 端口描述:
data:RAM 写数据端口;
address:RAM 读写地址端口,对于单口 RAM 来说,读地址和写地址共用同一组地址;
wren:写使能信号,高电平有效;
byteena:字节使能控制,该功能屏蔽了输入数据,这样仅写入数据中指定字节,未被写入的字节保留之前写入的值。当写入数据的位宽为 16 位、18 位、32 位和 36 位时,M9K 模块将支持字节使能,wren 信号以及字节 byteena 信号一起控制 RAM 模块的写操作。byteena 信号在 RAM IP 核创建过程中是可选的,可选择是否使用字节使能控制功能。
addressstall:地址时钟使能控制,当 addressstall 信号为高电平时,有效地址时钟使能就会保持之前的地址。addressstall 信号在 RAM IP 核创建过程中是可选的,可选择是否使用地址使能控制功能。
clockena:时钟使能控制,高电平有效;
rden:读使能信号,高电平有效;
aclr:异步复位信号,高电平有效;
q:从 RAM 中读出的数据;
在输入与输出时钟模式下,输入时钟控制存储器模块的所有输入寄存器,输出时钟控制存储器模块的所有输出寄存器。只有一个时钟信号的情况下,一个时钟控制所有寄存器。
二、IP核配置步骤
- 添加IP核:
- 双击后弹出:
写入IP名、路径、语言等信息。
- 参数配置:
1、“How wide should the ‘q’ output bus be?”:用于指定输出数据端口的位宽,我们这里保持默认,选择 8bit;
2、“How many 8-bit words of memory?”:用于指定存储器的容量大小,我们这里选择存储容量为32words;
3、“What should the memory block type be?”:用于指定实现存储器使用的存储块类型,具体可选值与使用的 FPGA 芯片型号有关,一般选择默认 AUTO 就可以了;
4、“What clocking method would you like to use?”:用于指定使用的时钟模式,可选择单时钟和双时钟,一般对于单口 ram 选择单时钟就可以了。点击next。
- next过后:
- next:
- next:
- next:
- next:
- 点击finish过后点击yes生成IP并加入到工程:
- 效果:
IP核简介及PLL_IP核的调用这是我的第一篇有关IP核的写的比较详细,还写了如何重新对IP核进行修改的操作。
三、源码
我们虽然配置了IP核但我们仍然需要写驱动文件、顶层文件对IP核进行调用。
1、ram_rw驱动文件
module ram_rw (
input clk,
input rst_n,
input [7:0] ram_rd_data,//读数据
output ram_rw_en,//写使能
output ram_rd_en,//读使能
output reg [4:0] ram_addr,//读写地址
output reg [7:0] ram_wr_data//写数据
);
reg [5:0] rw_cnt;//读写控制器
//高电平操作
//rw_cnt 计数范围在 0~31,ram_wr_en 为高电平;32~63 时,ram_wr_en 为低电平
assign ram_rw_en = ((rw_cnt >= 6'd0) && (rw_cnt <= 6'd31) && rst_n) ? 1'b1 :1'b0;
//rw_cnt 计数范围在 32~63,ram_rd_en 为高电平;0~31 时,ram_rd_en 为低电平
assign ram_rd_en = ((rw_cnt >= 6'd32) && (rw_cnt <= 6'd63)) ? 1'b1 :1'b0;
//0~63的计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rw_cnt <= 6'd0;
else if(rw_cnt == 6'd63)
rw_cnt <= 6'd0;
else
rw_cnt <= rw_cnt +1'b1;
end
//读写控制器计数范围:0~31 产生 ram 写使能信号和写数据信号,
//读使能信号也已产生,读数据信号由input信号引入
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
ram_wr_data <= 8'd0;
else if(rw_cnt >= 6'd0 && rw_cnt <= 6'd31)
ram_wr_data <= ram_wr_data +8'd1;
else
ram_wr_data <= 8'd0;
end
//控制读写地址范围
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
ram_addr <= 5'd0;
else if(ram_addr == 5'd31)
ram_addr <= 5'd0;
else
ram_addr <= ram_addr + 1'd1;
end
endmodule
2、ip_1port_ram顶层文件
module ip_1port_ram(
input sys_clk ,
input sys_rst_n
);
wire ram_wr_en ;//写使能
wire ram_rd_en ;//读使能
wire [4:0] ram_addr ;//读写地址
wire [7:0] ram_wr_data ;//ram写数据
wire [7:0] ram_rd_data ;//ram读数据
//ram读写控制模块
ram_rw ram_rw_inst(
.clk (sys_clk),
.rst_n (sys_rst_n),
.ram_rd_data (ram_rd_data),//读数据
.ram_rw_en (ram_wr_en ),//写使能
.ram_rd_en (ram_rd_en ),//读使能
.ram_addr (ram_addr ),//读写地址
.ram_wr_data (ram_wr_data)//写数据
);
ram ram_inst(
.address (ram_addr),
.clock (sys_clk),
.data (ram_wr_data),
.rden (ram_rd_en),
.wren (ram_wr_en),
.q (ram_rd_data)
);
endmodule
3、仿真文件
`timescale 1ns/1ns
module ip_1port_ram_tb();
parameter T = 20;
reg sys_clk;
reg sys_rst_n;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#(T+1)
sys_rst_n = 1'b1;
#(3000)
$stop;
end
always #(T/2) sys_clk = ~sys_clk;
ip_1port_ram ip_1port_ram_tb_inst(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n)
);
endmodule
4、仿真波形
- 写状态的波形:
- 分析:
ram_wr_en 信号拉高,ram_rd_en 信号拉低,说明此时是对 ram 进行写操作。
ram_wr_en 信号拉高之后,地址和数据都是从 0 开始累加,也就说当 ram 地址为 0 >时,写入的数据也是 0;当 ram 地址为 1 时,写入的数据也是 1,我们总共向 ram >中写入 32 个数据。
- 读状态波形:
- 分析:
ram_rd_en 信号拉高,ram_wr_en 信号拉低,说明此时是对 ram 进行读操作。
ram_rd_en(读使能)信号拉高之后,ram_addr 从 0 开始增加,也就是说从 ram的>地址 0 开始读数据;ram中读出的数据 ram_rd_data 在延时一个时钟周期之后,开始输出数据,输出的数据为 0,1,2……结果正确
四、SignalTap II在线调试
具体调试流程看:SignalTap II 软件使用步骤
- 读状态:
在读数据的状态下,读的数据都是延后一个时钟周期的数据,与仿真一致.
- 写状态:
写状态下,ram_wr_en 信号拉高之后,地址和数据都是从 0 开始累加,也就说当 ram 地址为 0 >时,写入的数据也是 0;当 ram 地址为 1 时,写入的数据也是 1.与仿真一致.实验结果正确.
五、总结
IP核的配置总体来说并不难,但需要我们一步步的细心配置,代码部分只需要对IP的操作为主。就是读数据哪里的波形,为什么我们的写数据没有时钟周期的延时,反而在读数据的时候会有一个时钟的延时。有没有懂佬解惑下?
六、参考资料
以上资料均来自正点原子的教学视频或开拓者2开发教程:
原子官方