RAM 的英文全称是 Random Access Memory, 即随机存取存储器
, 它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度由时钟频率决定
Xilinx 7 系列器件具有嵌入式存储器结构,嵌入式存储器结构由一列列 BRAM(块 RAM)存储器模块
组成,通过对这些 BRAM 存储器模块进行配置,可以实现各种存储器的功能,例如:RAM、移位寄存器、ROM 以及 FIFO 缓冲器
Vivado 软件自带了 BMG IP 核(Block Memory Generator,块 RAM 生成器),可以配置成 RAM 或者 ROM
实验任务是使用 Xilinx BMG IP 核,配置成一个单端口的 RAM
,然后对 RAM 进行读写操作。BMG IP 核配置成单端口 RAM 如图所示:
图片来自《领航者ZYNQ之FPGA开发指南》
高电平
表示向 RAM 中写入数据,低电平
表示从 RAM 中读出数据高电平
表示使能端口 A,低电平
表示端口 A 被禁止,禁止后端口 A 上的读写操作都会变成无效。另外 ENA 信号是可选的,当取消该使能信号后,RAM 会一直处于有效状态新建工程,操作如图所示:
输入工程名和工程路径,如图所示:
选择创建RTL工程,如图所示:
直接点击Next:
继续点击Next:
添加芯片型号,操作如图所示:
完成工程创建:
点击IP Catalog,搜索Block Memory,如图所示:
双击Block Memory Generator,弹出如下窗口:
单端口 RAM
设置端口A的参数,如图所示:
Other Options 选项界面用于设置 RAM 的初始值:
Summary选项界面显示了存储器的类型,消耗的 BRAM 资源等,如图所示:
弹出如下窗口,直接点击Generate:
点击OK即可:
IP 核自动生成的只读的 verilog 例化模板文件,双击打开,如图所示:
创建工程顶层文件,操作如图所示:
创建文件,输入文件名ip_ram:
创建完成:
双击打开,输入代码如下:
module ip_ram(
input sys_clk , //系统时钟
input sys_rst_n, //系统复位,低电平有效
output ram // 任意定义
);
//wire define
wire ram_en ; //RAM 使能
wire ram_wea ; //ram 读写使能信号,高电平写入,低电平读出
wire [4:0] ram_addr ; //ram 读写地址
wire [7:0] ram_wr_data ; //ram 写数据
wire [7:0] ram_rd_data ; //ram 读数据
//ram 读写模块
ram_rw u_ram_rw(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
//RAM
.ram_en (ram_en ),
.ram_wea (ram_wea ),
.ram_addr (ram_addr ),
.ram_wr_data (ram_wr_data ),
.ram_rd_data (ram_rd_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_wea ), // input wire [0 : 0] wea
.addra (ram_addr ), // input wire [4 : 0] addra
.dina (ram_wr_data ), // input wire [7 : 0] dina
.douta (ram_rd_data ) // output wire [7 : 0] douta
);
endmodule
继续创建文件ram_rw,如图所示:
双击打开,输入代码:
module ram_rw(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
output ram_en , //ram 使能信号
output ram_wea , //ram 读写选择
output reg [4:0] ram_addr , //ram 读写地址
output reg [7:0] ram_wr_data, //ram 写数据
input [7:0] ram_rd_data //ram 读数据
);
//reg define
reg [5:0] rw_cnt ; //读写控制计数器
//控制 RAM 使能信号
assign ram_en = rst_n;
//rw_cnt 计数范围在 0~31,写入数据;32~63 时,读出数据
assign ram_wea = (rw_cnt <= 6'd31 && ram_en == 1'b1) ? 1'b1 : 1'b0;
//读写控制计数器,计数器范围 0~63
always @( posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
rw_cnt <= 1'b0;
else if(rw_cnt == 6'd63)
rw_cnt <= 1'b0;
else
rw_cnt <= rw_cnt + 1'b1;
end
//产生 RAM 写数据
always @( posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_wr_data <= 1'b0;
else if(rw_cnt <= 6'd31) //在计数器的 0-31 范围内,RAM 写地址累加
ram_wr_data <= ram_wr_data + 1'b1;
else
ram_wr_data <= 1'b0 ;
end
//读写地址信号 范围:0~31
always @( posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_addr <= 1'b0;
else if(ram_addr == 5'd31)
ram_addr <= 1'b0;
else
ram_addr <= ram_addr + 1'b1;
end
endmodule
如图所示:
对设计进行分析,操作如图所示:
分析后的设计,Vivado自动生成顶层原理图,如图所示:
对设计进行综合,操作如图所示:
综合完成后,弹出窗口如下,直接关闭:
创建约束文件,操作如图所示:
创建文件,输入文件名:
双击打开,输入约束代码:
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
如图所示:
点击 Flow Navigator 窗口中的 Run Implementation,如图所示:
点击OK:
报错,如图所示:
错误解决方案:Vivado 设计实现时报错The design is empty的解决方案(亲测有效)
创建TestBench,操作如图所示:
创建文件,输入文件名:
创建完成:
双击打开,输入TestBench(激励)代码:
`timescale 1ns / 1ps
module tb_ip_ram ();
reg sys_clk;
reg sys_rst_n;
always #10 sys_clk = ~sys_clk;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200
sys_rst_n = 1'b1;
end
ip_ram u_ip_ram(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n )
);
endmodule
如图所示:
开始进行仿真,操作如下:
选择HDL仿真对象,这里把我们解决错误时,任意添加的输出ram也选择了(不需要选择):
选择HDL仿真对象:
点击Restart,波形窗口中的当前仿真时刻点回归到0ns:
删掉误添加的ram,开始仿真,ram进行写操作:
ram进行读操作:
关闭仿真:
点击OK即可:
由于疫情,一直无法去实验室,故ZYNQ开发板不在身边,该步骤内容待更新
致谢领航者ZYNQ开发板,开启FPGA学习之路!
希望本文对大家有帮助,上文若有不妥之处,欢迎指正
分享决定高度,学习拉开差距