简单双口RAM的IP核
分A端口和B端口
A为输入端口,负责数据的写入
B端口为输出端口,负责数据的读出
两端的时钟可以不同,还允许在写入A的同时读B。
配置IP
vivado 2019.2
IP catalog -> Memories & Storage Elements -> RAMs & ROMs & BRAM -> …
例化顶层
module top(
input clka,
input wea,
input [9:0]addra,
input [15:0]dina,
input clkb,
input [9:0] addrb,
output [15:0] doutb
);
blk_mem_gen_0 u1 (
.clka(clka),
.wea(wea),
.addra(addra),
.dina(dina),
.clkb(clkb),
.addrb(addrb),
.doutb(doutb)
);
endmodule
测试
主要内容为两个alwas块,一个负责操作Aport,另一个负责操作Bport
`timescale 1ns / 1ps
module tb_dram();
reg clk; //cha/chb common clk
reg rst_n;
//chnl a
reg cha_write_en;
reg[15:0] cha_data;
reg[9:0] sdpram_addr_cha;
//chnl b
wire[15:0] chb_data;
reg[9:0] sdpram_addr_chb;
reg [15:0] data1;
reg [15:0] data2;
reg [15:0] data3;
//state chicken
reg[10:0] cnta_state;
reg[10:0] cntb_state;
//cha:data input chnl
//对ram进行写操作,在选择地址的同时给予数据。
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)begin
cnta_state <= 1'b0;
cha_write_en <=1'b0;
sdpram_addr_cha <= 10'd0;
cha_data <= 16'd0;
end
else begin
case(cnta_state)
0,1,2,3,4,5,6,7,8,9:cnta_state <= cnta_state + 1'b1;
10:begin cha_write_en <= 1'b1;sdpram_addr_cha <= 10'd1;cha_data <= 16'h5555;
cnta_state <= cnta_state + 1'b1;
end
11:begin cha_write_en <= 1'b1;sdpram_addr_cha <= 10'd2;cha_data <= 16'haaaa;
cnta_state <= cnta_state + 1'b1;
end
12:begin cha_write_en <= 1'b1;sdpram_addr_cha <= 10'd3;cha_data <= 16'hcccc;
cnta_state <= cnta_state + 1'b1;
end
default:begin
cnta_state <= 10'd13;
cha_write_en <=1'b0;
sdpram_addr_cha <= 10'd0;
cha_data <= 16'd0;
end
endcase
end
end
//chb:data output chnl
//对ram做读操作,与写操作不同。
//读操作需要等2个clk才能在dout上输出选定地址的有效数据
//配置地址 -> 时序逻辑延迟经过一个clk才成功写入 -> 再过一个clk ->有效数据
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)begin
cntb_state <= 1'b0;
data1 <= 1'b0;
data2 <= 1'b0;
data3 <= 1'b0;
sdpram_addr_chb <= 10'd0;
end
else begin
case(cntb_state)
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19:
cntb_state <= cntb_state + 1'b1;
20:begin sdpram_addr_chb <= 10'd1; //选择地址1
cntb_state <= cntb_state + 1'b1;
end
21:begin sdpram_addr_chb <= 10'd2; //选择地址2
cntb_state <= cntb_state + 1'b1;
end
22:begin data1 <= chb_data; //寄存地址1上吐出的数据
sdpram_addr_chb <= 10'd3; //选择地址3
cntb_state <= cntb_state + 1'b1;
end
23:begin data2 <= chb_data; //寄存地址2上吐出的数据
cntb_state <= cntb_state + 1'b1;
end
24:begin data3 <= chb_data; //寄存地址3上吐出的数据
cntb_state <= cntb_state + 1'b1;
end
default:begin
cntb_state <= 10'd24;
sdpram_addr_chb <= 10'd0;
end
endcase
end
end
initial begin clk = 0; rst_n = 0; #50; rst_n = 1'b1; end
initial begin #600; $stop(); end
always # 10 clk = ~clk;
top u2 (
.clka(clk), // input clka
.wea(cha_write_en), // input [3 : 0] wea
.addra(sdpram_addr_cha), // input [31 : 0] addra
.dina(cha_data), // input [31 : 0] dina
.clkb(clk), // input clkb
.addrb(sdpram_addr_chb), // input [31 : 0] addrb
.doutb(chb_data) // output [31 : 0] doutb
);
endmodule