本帖最后由 1nnocent 于 2023-2-4 13:01 编辑
1、简介
双端口 RAM 又分为简单双端口 RAM 和真双端口 RAM,顾名思义,简单双端口 RAM 虽然有两个端口,但是一个端口只能用来写,另一个端口只能用来读,所以简单双端口 RAM 也称为伪双端口 RAM。而真双端口 RAM 是指两个端口都可以用来写或者读,可以理解成具有两个独立的单端口的 RAM,一般用于需要多路写入和读出的情况,而不用例化两个单端口的 RAM,在使用上更为方便。
2、实验任务
本节实验任务是使用 PDS 的 IP Compiler 配置一个简单双端口 RAM IP 并对该简单双端口 RAM 进行读写操作, 通过 PDS 与 Modelsim 联合仿真观察波形是否正确,最后将设计下载到 ATK-DFPGL22G 开发板中,并使用 Inserter 对其进行在线调试观察。
3、程序设计
该程序需要5个模块:简单双端口 RAM 模块、写 RAM模块、读 RAM 模块、 PLL IP 核模块以及顶层模块,顶层模块例化其余模块实现前四个模块的数据交互。由于双端口 RAM 多用于跨时钟域信号的处理,所以本实验我们使用两个不同的时钟分别作为 RAM 的写时钟和读时钟,这两个时钟由 PLL IP 核生成,输出的时钟分别是 50Mhz 和 25Mhz。系统功能框图:
ram_wr.v:模块中定义了一个写计数器(wr_cnt),从 0 累加至 63,此后一直保持在数值 63。当计数范围在0~31 之间时,向 ram 中写入数据,而其它计数值时停止写入。实现的功能是向 RAM 的地址 0 写入数据0,地址 1 写入数据 1,一直到地址 31 写入数据 31,只在上电后写入一次。
module ram_wr(
input clk,
input rst_n,
output ram_wr_en,
output reg[4:0] ram_wr_addr,
output reg[7:0] ram_wr_data
);
reg[5:0] wr_cnt;
assign ram_wr_en = ((wr_cnt >= 6'd0)&&(wr_cnt <= 6'd31)&& rst_n) ? 1'b1 : 1'b0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_cnt <= 6'd0;
end
else if(wr_cnt == 6'd63)begin
wr_cnt <= wr_cnt;
end
else begin
wr_cnt <= wr_cnt + 1'b1;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ram_wr_data <= 8'd0;
end
else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)begin
ram_wr_data <= ram_wr_data + 1'b1;
end
else begin
ram_wr_data <= 8'd0;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ram_wr_addr <= 5'd0;
end
else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)begin
ram_wr_addr <= ram_wr_addr + 1'b1;
end
else begin
ram_wr_addr <= 5'd0;
end
end
endmodule
ram_rd.v:模块中定义了一个读计数器(rd_cnt),循环的从 0 累加至 63。当计数范围在 0~31 之间时,从 ram 中读出数据,而其它计数值时停止读数据。
module ram_rd(
input clk,
input rst_n,
output ram_rd_en,
output reg[4:0]ram_rd_addr,
input [7:0]ram_rd_data
);
reg[5:0] rd_cnt;
assign ram_rd_en = ((rd_cnt >= 6'd0)&&(rd_cnt <= 6'd31)&& rst_n)? 1'b1 : 1'b0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_cnt <= 6'd0;
end
else if(rd_cnt == 6'd63)begin
rd_cnt <= 6'd0;
end
else begin
rd_cnt <= rd_cnt + 1'b1;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ram_rd_addr <= 5'd0;
end
else if(rd_cnt >= 6'd0 && rd_cnt <= 6'd31)begin
ram_rd_addr <= ram_rd_addr + 1'b1;
end
else begin
ram_rd_addr <= 5'd0;
end
end
endmodule
ip_2port_ram.v:例化创建的 PLL IP 核、 RAM IP 核、 RAM 写模块和 RAM 读模块。程序中例化了 ram_rw 模块和单口 ram IP 核,其中 ram_rw 模块负责产生对 ram IP 核读/写所需的所有数据、地址以和读写使能信号,同时从 ram IP 读出的数据也连接至 ram_rw 模块。
module ip_2port_ram(
input sys_clk,
input sys_rst_n,
output clk_50m,
output clk_25m,
output locked,
output ram_wr_en,
output [4:0] ram_wr_addr,
output [7:0] ram_wr_data,
output ram_rd_en,
output [4:0] ram_rd_addr,
output [7:0] ram_rd_data
);
pll_clk u_pll_clk(
.pll_rst (!sys_rst_n),
.clkin1 (sys_clk),
.pll_lock (locked),
.clkout0 (clk_50m),
.clkout1 (clk_25m)
);
ram_wr u_ram_wr(
.clk (sys_clk),
.rst_n (sys_rst_n),
.ram_wr_en (ram_wr_en),
.ram_wr_addr (ram_wr_addr),
.ram_wr_data (ram_wr_data)
);
ram_2port ram_2port_inst (
.wr_data (ram_wr_data),
.wr_addr (ram_wr_addr),
.wr_en (ram_wr_en ),
.wr_clk (clk_50m ),
.wr_rst (~sys_rst_n ),
.rd_addr (ram_rd_addr),
.rd_data (ram_rd_data),
.rd_clk (clk_25m ),
.rd_rst (~sys_rst_n )
);
ram_rd u_ram_rd(
.clk (clk_25m),
.rst_n (sys_rst_n),
.ram_rd_en (ram_rd_en ),
.ram_rd_addr (ram_rd_addr),
.ram_rd_data (ram_rd_data)
);
endmodule
ip_1port_ram_tb:
`timescale 1ns / 1ps
module ip_2port_ram_tb;
reg grs_n;
//GTP_GRS I_GTP_GRS(
GTP_GRS GRS_INST(
.GRS_N (grs_n)
);
initial begin
grs_n = 1'b0;
#5000 grs_n = 1'b1;
end
reg sys_clk;
reg sys_rst_n;
wire clk_50m ;
wire clk_25m ;
wire locked ;
wire wr_en_tb ;
wire [4:0] wr_addr_tb;
wire [7:0] wr_data_tb;
wire rd_en_tb ;
wire [4:0] rd_addr_tb;
wire [7:0] rd_data_tb;
ip_2port_ram uut (
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_50m (clk_50m ),
.clk_25m (clk_25m ),
.locked (locked ),
.ram_wr_en (wr_en_tb ),
.ram_wr_addr (wr_addr_tb),
.ram_wr_data (wr_data_tb),
.ram_rd_en (rd_en_tb ),
.ram_rd_addr (rd_addr_tb),
.ram_rd_data (rd_data_tb)
);
initial begin
sys_clk = 0;
sys_rst_n = 0;
#100;
sys_rst_n=1;
end
always #10 sys_clk=~sys_clk;
endmodule
仿真波形图如下图所示:可以发现wr_data_tb是蓝色波形存在问题,展开每个bit的数据发现第5、6、7位是“”Z"状态。把这个信号查一遍发现是ram_wr定义这个信号时位宽写成了[4:0]只有底五位有数据。
更改错误之后:各位的数据正确。
写人数据时wr_en_tb为高,写入32个数据之后不再写入;
读出数据时rd_en_tb为高,写入32个数据之后不再写入;