基于ZYNQ-7000系列的FPGA学习笔记11------IP核之单端RAM读写
- [1. 实验要求](#1. 实验要求)
- [2. 功能分析](#2. 功能分析)
-
- [2.1 RAM简介](#2.1 RAM简介)
- [2.2 BMG IP核的调用](#2.2 BMG IP核的调用)
- [2.3 RAM的三个工作模式](#2.3 RAM的三个工作模式)
- [3. 模块设计](#3. 模块设计)
- [4. 波形图](#4. 波形图)
- 5.代码编写
- [6. 代码仿真](#6. 代码仿真)
- [7. 通过在线逻辑分析仪观察结果](#7. 通过在线逻辑分析仪观察结果)
上一期内容,我们学习了VAVADO软件IP核中的锁相环,这一期我们IP核中的单端RAM实验
1. 实验要求
通过例化VAVADO软件中的BMG(Block Memory Generator)模块,配置成一个单端口的RAM,并对其实现读写操作,然后通过仿真观察波形是否争取,最后下载到FPGA开发板中,通过在线调试工具对实验结果进行验证。
2. 功能分析
本次实验中要求我们实现一个简单的单端口RAM,在此之前,我们有必要来了解一下什么是RAM。
2.1 RAM简介
RAM(Random Access Memory,随机存取存储器)是计算机中的一种存储设备,用于暂时存储和访问数据。
目前的存储器分为随机存储器和只读存储器,随机存储器包括静态 RAM 和动态 RAM。静态 RAM只要有供电,它保存的数据就不会丢失;而动态 RAM 在供电的情况下,还需要根据其要求的时间来对存储的数据进行刷新,才能保持存储的数据不会丢失。同时静态RAM的读写速度也比动态RAM要快,但是由于静态RAM的实现、成本和功耗方面都比动态RAM要高,所以静态RAM的大小一般不会很大,相反动态RAM的大小一般就很大。
静态RAM又可以分为单端RAM、伪双端RAM、真双端RAM三种,它们之间的区别如下:
本次实验内容,我们要使用的是单端RAM,单端口 RAM 只有一个端口进行读写,即读/写只能通过这一个端口来进行。
2.2 BMG IP核的调用
Vivado 软件自带的 Block Memory Generator IP 核(缩写为 BMG,中文名为块 RAM 生成器),可以用来配置生成 RAM 或者 ROM。RAM 是一种随机存取存储器,不仅可以读出存储的数据,同时还支持对存储的数据进行修改,而 ROM 是一种只读存储器,也就是说,在工作时只能读出数据,而不能写入数据。需要注意的是,配置生成的 RAM 或者 ROM 使用的都是 FPGA 内部的 BRAM 资源(Block RAM,即块随机存储器,是 FPGA 厂商在逻辑资源之外,给 FPGA 加入的专用 RAM 块资源),只不过配置成 ROM 时只用到了嵌入式 BRAM 的读数据端口。本章我们主要介绍如何将 BMG IP 核配置成 RAM。
【注】:Xilinx 7 系列器件内部的 BRAM 全部是真双端口 RAM,但是通过BMG IP 核,我们还可以将其配置为伪双端口 RAM 或者单端口 RAM。
下面我们来简单的介绍一下VIVADO软件中BMG IP核的调用步骤:
- 新建一个vivado工程,命名为 " ip_1port_ram " ,然后进入IP核创建界面,搜索 " Block Memory Generator " 。
- 进行BMG模块的基础配置
- 端口A的选项
至此,BMG的基本配置就已经完成了,其他的选项保存默认即可,如果有其他的需要,可以自行查找对应的手册,这里不做介绍了。
2.3 RAM的三个工作模式
在2.2 配置的过程中,有一个选项控制RAM的工作模式,分为优先模式、读优先模式和不变模式,我们这里介绍一下他们之间的区别:
- 写优先模式
在写优先模式下,当 ENA(使能信号)为高后,第一个时钟上升沿 WEA(读/写使能信号)为低电平,表示读数据,此时的地址为 aa,所以读出的就是 aa 内的数据;第二个时钟上升沿 WEA 为高电平,表示写数据,此时的地址为 bb,即先将数据(DAIN)"1111"写入地址 bb 后再读出,所以读出的数据也是"1111"。
也就是说,在写优先模式下,如果是写字节,会将 DINA 数据写入存储器后再将更新后的数据送到 DOUTA 上进行输出
- 读优先模式
在读优先模式下,当 ENA(使能信号)为高后,第一个时钟上升沿 WEA 为低电平,表示读数据,此时的地址为 aa,所以读出的就是 aa 内的数据;第二个时钟上升沿 WEA 为高电平,表示写数据,此时的地址为 bb,即先读出地址 bb 中的旧数据,然后再将数据(DAIN)"1111"写入地址 bb。
也就是说,在读优先模式下,如果是写字节,会将当前存储器中的旧数据送到 DOUTA 上进行输出,然后再将 DINA 数据写入当前存储地址
- 不变模式
在不变模式下,当 ENA 和 WEA 同时为高时,只进行写操作,读数据保持不变,也就是说 DOUTA 保持前一拍数据,直到 WEA 为低。
以上就是本次实验的所有功能分析,主要是BMG IP核的使用和一些RAM的知识补充,下面我们开始绘制整体的功能框图和波形图。
3. 模块设计
本次实验一共分为了三个模块,分别是ip_1port_ram、RAM读写模块和RAM IP核,其中ip_1port_ram是顶层模块,下面是对应的波形图
4. 波形图
- RAM IP核读写时序
这里的RAM IP核读写时序是根据vivado提供的文档绘制的
- RAM读写模块
这里的RAM读写模块波形图是用来驱动RAM IP核的,所以波形图上和RAM IP核很像,不过增加了复位引脚和rw_cnt计数值来控制读还是写。
- ip_1port_ram顶层模块
由于ip_1port_ram顶层模块中只需要例化RAM IP核 和 RAM读写模块,逻辑很简单,所以这里就不绘制波形图了。
5.代码编写
- RAM IP核
由于RAM IP核是我们通过vivado调用产生的,因此不需要相关的rtl代码,故省略。
- RAM读写模块
vbnet
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/10 21:53:58
// Design Name:
// Module Name: ram_rw
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
//RAM模块的定义,深度为16,宽度为8
module ram_rw(
input clk,
input rst_n,
input [7:0] rd_data,
output reg ram_en,
output reg ram_we,
output reg [3:0] ram_addr,
output reg [7:0] wr_data
);
//计数变量定义
reg [5:0] wr_cnt;
//使能端口控制
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_en <= 1'b0;
else
ram_en <= 1'b1;
end
////控制wr_cnt的值
//always @(posedge clk or negedge rst_n) begin
// if(rst_n == 1'b0) //复位,wr_cnt = 0
// wr_cnt <= 5'd0;
// else if(wr_cnt == 5'd31 && ram_en == 1'b1) //使能了RAM模块,且wr_cnt到达最大值,wr_cnt清零
// wr_cnt <= 1'b0;
// else if(ram_en == 1'b1) //如果使能了RAM模块,wr_cnt加一
// wr_cnt <= wr_cnt + 5'd1;
// else
// wr_cnt <= 5'd0; //其他情况下,清零
//end
//控制wr_cnt的值方法2
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) //复位,wr_cnt = 0
wr_cnt <= 5'd0;
else if(ram_en == 1'b1) begin
if(wr_cnt == 5'd31)
wr_cnt <= 5'd0;
else
wr_cnt <= wr_cnt + 5'd1;
end
else
wr_cnt <= 5'd0;
end
//控制ram_we的值
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) //复位,ram_we = 1
ram_we <= 1'd1;
else if(wr_cnt < 5'd15)
ram_we <= 1'd1;
else
ram_we <= 1'd0;
end
//读写地址,在0-15之间变化
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) //复位,默认地址从0开始
ram_addr <= 4'd0;
else if(ram_addr == 5'd15 && ram_en)
ram_addr <= 4'd0;
else if(ram_en)
ram_addr <= ram_addr + 5'd1;
else
ram_addr <= 1'd0;
end
//写入的数据:从1到16
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) //复位,默认地址从0开始
wr_data <= 8'd1;
else if(wr_cnt < 5'd15 && ram_we && ram_en)
wr_data <= wr_data + 8'd1;
else
wr_data <= 8'd1;
end
endmodule
- ip_1port_ram顶层模块
vbnet
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/10 20:09:07
// Design Name:
// Module Name: ip_1port_ram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ip_1port_ram(
input sys_clk,
input sys_rst_n
);
//wire define
wire ram_en ; //RAM 使能
wire ram_we ; //ram 读写使能信号,高电平写入,低电平读出
wire [3:0] ram_addr ; //ram 读写地址
wire [7:0] ram_wr_data ; //ram 写数据
wire [7:0] ram_rd_data ; //ram 读数据
ram_rw u_ram_rw(
.clk (sys_clk),
.rst_n (sys_rst_n),
.rd_data (ram_rd_data),
.ram_en (ram_en),
.ram_we (ram_we),
.ram_addr (ram_addr),
.wr_data (ram_wr_data)
);
//ram ip 核
blk_mem_gen_0 blk_mem_gen_0 (
.clka (sys_clk ), // input wire clka
.ena (ram_en ), // input wire ena
.wea (ram_we ), // input wire [0 : 0] wea
.addra (ram_addr ), // input wire [3 : 0] addra
.dina (ram_wr_data ), // input wire [7 : 0] dina
.douta (ram_rd_data ) // output wire [7 : 0] douta
);
endmodule
- 测试代码
vbnet
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/11 12:15:06
// Design Name:
// Module Name: tb_ip_1port_ram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module tb_ip_1port_ram();
//端口定义
reg sys_rst_n;
reg sys_clk;
//时钟周期20ns
parameter CLK_PERIOD = 20;
//初始化时序
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//产生时钟
always #(CLK_PERIOD / 2) sys_clk <= ~sys_clk;
ip_1port_ram u_ip_1port_ram(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n)
);
endmodule
以上就是本次实验的所有相关代码,下面我们开始仿真,这里仿真使用的是vivado自带的仿真软件。
6. 代码仿真
-
写时序
-
读时序
与我们绘制的波形图一致,很不错!!!
7. 通过在线逻辑分析仪观察结果
仿真完成之后,下一步生成约束文件,然后分析综合,最后生成比特流文件,过程中会发生报错,这是因为我们这个系统中没有输出,在vivado中不允许这样的结果,所以我们只能通过添加在线逻辑分析仪进行在线调试,关于在线调试的方法,可以参考: 基于ZYNQ-7000系列的FPGA学习笔记9------VIVADO的在线调试
进行在线调试,需要改写ip_1port_ram顶层模块,下面是修改完之后的文件
vbnet
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/10 20:09:07
// Design Name:
// Module Name: ip_1port_ram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ip_1port_ram(
input sys_clk,
(* mark_debug = "true" *)input sys_rst_n
);
//wire define
(* mark_debug = "true" *)wire ram_en ; //RAM 使能
(* mark_debug = "true" *)wire ram_we ; //ram 读写使能信号,高电平写入,低电平读出
(* mark_debug = "true" *)wire [3:0] ram_addr ; //ram 读写地址
(* mark_debug = "true" *)wire [7:0] ram_wr_data ; //ram 写数据
(* mark_debug = "true" *)wire [7:0] ram_rd_data ; //ram 读数据
ram_rw u_ram_rw(
.clk (sys_clk),
.rst_n (sys_rst_n),
.rd_data (ram_rd_data),
.ram_en (ram_en),
.ram_we (ram_we),
.ram_addr (ram_addr),
.wr_data (ram_wr_data)
);
//ram ip 核
blk_mem_gen_0 blk_mem_gen_0 (
.clka (sys_clk ), // input wire clka
.ena (ram_en ), // input wire ena
.wea (ram_we ), // input wire [0 : 0] wea
.addra (ram_addr ), // input wire [3 : 0] addra
.dina (ram_wr_data ), // input wire [7 : 0] dina
.douta (ram_rd_data ) // output wire [7 : 0] douta
);
endmodule
下面是运行的结果
- 写时序
- 读时序
可以看到,和我们仿真的结果是基本一致的,也即是说我们前面的步骤都是没有问题的。
以上就是本期的所有内容,创造不易,点个关注再走呗。