假双端口RAM是虽然有两个端口,但其中一个端口只负责写,一个端口只负责读。
两个端口可以同时执行但是,不能发生读写冲突(同一时刻对于同一个地址读、写是不允许的)
(根据正点原子视频学习,图片来自于正点原子讲解视频)
一、实验任务
对0-63地址使用 A(只写)端口进行写入0-63的数据,并使用B端口(只读)读出这些数据。
二、实验思路
需要三个模块,1用于调用编写读取模块,2用于编写写入模块,3用于调用1.2模块以及IP核模块,即1,2为子模块,3为顶层模块。机构图如下:
三、实验步骤
1.配置RAM IP核
首先在左侧IP catalog中搜索RAM,双击block memory generator 进行配置,将RAM的类型调整为简单双核RAM(Simple Dual Port Ram)
再分别配置port A 核port B的数据宽度和深度(这里我们配置8,64)
确定后即可完成配置。
2.波形图分析
写模块
输入:
clk:系统时钟
rst:系统复位
输出:
ram_wr_en:ram使能信号
ram_wr_wr: 写优先信号,但在简单双端口RAM中,该信号为1即写,为0不工作
ram_wr_addr: 地址
ram_wr_data: 数据
rd_flag: 读信号,为防止读写冲突,我们这里让写到31位时候开始读操作
读模块
输入:
clk:系统时钟
rst:系统复位
rd_flag: 读信号,为防止读写冲突,我们这里让写到31位时候开始读操作
输出:
ram_rd_en: 读使能信号
ram_rd_addr: 读出数据的地址
ram_rd_data: 读出的数据值
3.代码编写
根据波形图对读写模块进行编程
写模块
module ip_2portram_write(
input clk,
input rst,
output reg ram_wr_en,//使能
output reg ram_wr_we,//1写0不动
output reg[5:0] ram_wr_addr,
output reg[7:0] ram_wr_data,
output reg rd_flag
);
//ram_wr_en
always @(posedge clk or negedge rst) begin
if (rst == 1'b0)
ram_wr_en <= 1'b0;
else
ram_wr_en <= 1'b1;
end
//ram_wr_we
always @(posedge clk or negedge rst) begin
if(rst == 1'b0)
ram_wr_we <= 1'b0;
else
ram_wr_we <= 1'b1;
end
//ram_wr_addr
always @(posedge clk or negedge rst ) begin
if(rst == 1'b0)
ram_wr_addr <= 6'b0;
else if (ram_wr_we==1'b1 && ram_wr_addr != 6'd63)
ram_wr_addr <= ram_wr_addr + 6'b1;
else
ram_wr_addr <= 6'b0;
end
//ram_wr_data
always @(posedge clk or negedge rst ) begin
if(rst == 1'b0)
ram_wr_data <= 8'b0;
else if (ram_wr_data != 8'd63 && ram_wr_we == 1'b1)
ram_wr_data <= ram_wr_data + 8'b1;
else
ram_wr_data <= 8'b0;
end
//rd_flag
always @(posedge clk or negedge rst ) begin
if(rst == 1'b0)
rd_flag <= 1'b0;
else if (ram_wr_en == 1'b1 && ram_wr_addr == 8'd31)//?
rd_flag <= 1'b1;
else
rd_flag <= rd_flag;
end
endmodule
读模块
module ip_2portram_rd(
input clk,
input rst,
input rd_flag,
input [7:0] ram_rd_data,
output reg [5:0] ram_rd_addr,
output reg ram_rd_en
);
//ram_rd_en
always @(posedge clk or negedge rst) begin
if(rst == 1'b0)
ram_rd_en <= 1'b0;
else if (rd_flag == 1'b1)
ram_rd_en <= 1'b1;
else
ram_rd_en <= 1'b0;
end
//ram_rd_addr
always @(posedge clk or negedge rst) begin
if(rst == 1'b0)
ram_rd_addr <= 6'b0;
else if (ram_rd_en == 1'b1 && ram_rd_addr != 6'd63 )
ram_rd_addr <= ram_rd_addr + 6'b1;
else
ram_rd_addr <= 6'b0;
end
endmodule
顶层调用封装模块
//顶层模块,调用 read write ip_ram
module ip_ram(
input sys_clk,
input sys_rst
);
wire rd_flag ;
wire [7:0] ram_rd_data ;
wire [5:0] ram_rd_addr ;
wire ram_rd_en ;
wire ram_wr_en ;
wire ram_wr_we ;
wire [5:0] ram_wr_addr ;
wire [7:0] ram_wr_data ;
//read
ip_2portram_rd u_ip_2portram_rd(
.clk (sys_clk ),
.rst (sys_rst ),
.rd_flag (rd_flag ),
.ram_rd_data (ram_rd_data ),
.ram_rd_addr (ram_rd_addr ),
.ram_rd_en (ram_rd_en )
);
//write
ip_2portram_write u_ip_2portram_write(
.clk ( sys_clk ),
.rst ( sys_rst ),
.ram_wr_en ( ram_wr_en ),
.ram_wr_we ( ram_wr_we ),
.ram_wr_addr ( ram_wr_addr ),
.ram_wr_data ( ram_wr_data ),
.rd_flag ( rd_flag )
);
//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
blk_mem_gen_0 u_blk_mem_gen_0 (
.clka (sys_clk ), // input wire clka
.ena (ram_wr_en ), // input wire ena
.wea (ram_wr_we ), // input wire [0 : 0] wea
.addra (ram_wr_addr ), // input wire [5 : 0] addra
.dina (ram_wr_data ), // input wire [7 : 0] dina
.clkb (sys_clk ), // input wire clkb
.enb (ram_rd_en ), // input wire enb
.addrb (ram_rd_addr ), // input wire [5 : 0] addrb
.doutb (ram_rd_data ) // output wire [7 : 0] doutb
);
endmodule
4.仿真分析
为检查结果我们采用仿真分析,因此需要编写仿真模块激励代码,由于仅有clk和rst输入,因此仿真模块代码也比较简单。
`timescale 1ns/1ns
module tb_ip_2port_ram();
parameter clk_period = 20;
reg sys_clk;
reg sys_rst;
//初始化复位
initial begin
sys_clk <= 1'b0;
sys_rst <= 1'b0;
#200
sys_rst <= 1'b1;
end
//初始化时钟
always #(clk_period/2) sys_clk =~sys_clk;
ip_ram u_ip_ram(
.sys_clk (sys_clk),
.sys_rst (sys_rst)
);
endmodule
进行仿真后波形图
写入数据成功
读取数据成功。