本帖最后由 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;
-
-
-
- sys_rst_n=1;
- end
-
- always
-
- endmodule
-
-
仿真波形图如下图所示:可以发现wr_data_tb是蓝色波形存在问题,展开每个bit的数据发现第5、6、7位是“”Z"状态。把这个信号查一遍发现是ram_wr定义这个信号时位宽写成了[4:0]只有底五位有数据。
更改错误之后:各位的数据正确。
写人数据时wr_en_tb为高,写入32个数据之后不再写入;
读出数据时rd_en_tb为高,写入32个数据之后不再写入;