写在前面的话
在很多时候,我们需要将采集得到的数据先存储起来,等到了需要的时候再调用。如果是这种情况,那么就要求我们的存储器必须可读可写。本节,梦翼师兄就和大家一起学习FPGA可读写存储器IP核-RAM的使用。
项目需求
设计一个RAM控制器,该控制器负责对RAM 进行读写操作,首先将数据写入RAM,然后再将数据全部读出。如果读出的数据和写入的数据完全一致,说明我们的操作和设计正确。
操作步骤
在右侧的IP核搜索区,输入ram,在菜单栏找到并双击【RAM:1-PORT】
选择语言类型为Verilog,并命名,点击【OK】
设置ram的存储深度和每一个存储空间的比特位数,然后点击【NEXT】
把【q output port】前面的对钩取消掉(如果不取消掉就会在输出端口生成寄存器,输出就会慢一拍,这里我们不需要它慢一拍),然后点击【NEXT】
一直点击【NEXT】,直到如下界面,选择my_ram_inst.v,点击【Finish】,完成对ram的设置
顶层架构设计
RAM是可读写的存储器,我们用一个控制模块向ram中写入数据,然后读出。
模块功能介绍
模块名 |
功能描述 |
Ram_control |
Ram控制器,对my_ram进行读写 |
My_ram |
ram存储器IP核 |
ram |
系统顶层模块,负责子模块级联 |
5.3.6 端口和内部连线描述
顶层模块端口介绍
端口名 |
端口说明 |
Clk |
系统时钟输入 |
Rst_n |
系统复位 |
q |
数据输出 |
系统内部连线介绍
连线名 |
连线说明 |
addr |
Ram_control产生的地址信号 |
data |
Ram_control产生的数据 |
wren |
Ram_control产生的读写控制信号(高电平为写,低电平为读) |
5.3.7代码解释
Ram_control模块的代码
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function:产生控制信号以及数据 *****************************************************/ 00 module ram_control ( 01 clk, //系统时钟输入 02 rst_n, //系统复位 03 wren, //读写信号 04 addr, //地址信号 05 data //有效数据 06 ); 07 // 模块输入 08 input clk; //系统时钟输入 09 input rst_n;//系统复位 10 // 模块输出 11 output reg wren; //读写信号 12 output reg [7:0] addr;//地址信号 13 output reg [7:0] data;//有效数据 14 //定义中间寄存器 15 reg state; //定义状态寄存器 16 17 always @ (posedge clk or negedge rst_n) 18 begin 19 if (!rst_n) // 复位时,将所有的输出清零。 20 begin 21 wren <= 0; 22 addr <= 0; 23 data <= 0; 24 state <= 0; 25 end 26 else 27 begin 28 case (state) 29 0 : begin 30 if (addr < 255)//使地址在0到255之间,让写信号有效 31 begin 32 addr <= addr + 1; 33 wren <= 1; 34 end 35 else 36 begin 37 addr <= 0;//转到下一个状态,让地址清零,让读信号有效 38 state <= 1; 39 wren <= 0; 40 end 41 42 if (data < 255)// 给有效数据,使数据在0到255之间 43 data <= data + 1; 44 else 45 data <= 0; 46 47 end 48 49 1 : begin 50 if (addr < 255)//使地址在0到255之间,让读信号有效 51 begin 52 addr <= addr + 1; 53 wren <= 0; 54 end 55 else 56 begin 57 state <= 0; //转到0状态,地址清零 58 addr <= 0; 59 end 60 end 61 62 default : state <= 0; // 如果系统不稳定的时候直接进入0状态 63 64 endcase 65 end 66 end 67 68 endmodule |
在本模块中,数据和地址的数值大小是一样的。
Ram的顶层代码
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function:顶层模块,负责连接各子模块 *****************************************************/ 00 module ram ( 01 clk, //系统时钟输入 02 rst_n,//系统复位 03 q //输出数据 04 ); 05 // 系统输入 06 input clk; //系统时钟输入 07 input rst_n;//系统复位 08 // 系统输出 09 output [7:0] q;//输出数据 10 //定义中间连线信号 11 wire wren; //定义写信号 12 wire [7:0] addr;// 定义地址信号 13 wire [7:0] data;//定义中间数据 14 // 调用ram_control 15 ram_control ram_control ( 16 .clk(clk), //系统时钟输入 17 .rst_n(rst_n), //系统复位 18 .wren(wren), //读写信号 19 .addr(addr), //地址信号 20 .data(data) //有效数据 21 ); 22 //调用IP核--ram 23 my_ram my_ram_inst ( 24 .address ( addr ), // 地址信号 25 .clock ( clk ), // 系统时钟 26 .data ( data ), // 输入数据 27 .wren ( wren ), //读写信号 28 .q ( q ) //输出数据 29 ); 30 31 endmodule |
本模块只负责连接各个子模块,没有任何的逻辑。代码编写完毕以后,查看RTL视图如下:
由RTL视图可以看出,电路综合以后的结果和我们所设计的系统框图一致,接下来编写测试代码如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function:对ram进行测试 *****************************************************/ 00 `timescale 1ns/1ps //时间单位和精度定义 01 module ram_tb; 02 //系统输入 03 reg clk; //系统时钟输入 04 reg rst_n; //系统复位 05 //系统输出 06 wire [7:0] q; //输出数据 07 08 initial begin 09 clk = 1; 10 rst_n = 0; 11 # 200.1 12 rst_n = 1; 13 end 14 15 always # 10 clk = ~clk; //50MHz时钟 16 17 ram ram ( 18 .clk(clk), //系统时钟输入 19 .rst_n(rst_n), //系统复位 20 .q(q) //输出数据 21 ); 22 23 24 endmodule |
仿真分析
复位结束之后,写信号有效,同时给出数据和地址。Ram的q端在写的过程中也会进行慢一拍的输出,这不是错误,这是由Ram内部结构决定的。
注:写数据的过程中,q端也会输出,这是由altera RAM IP核的功能决定的,此处不需要深究。
当写完数据之后,进行读(写信号拉低),同时给出地址,ram在下一拍给出对应地址中的数据。
经过上述的分析,证明我们的ram应用正确。