双口RAM定义 及多个ram verilog rtl仿真
程序员文章站
2024-02-22 21:30:52
...
自己写的采用这种数组形式写的RAM块占用存储器容量的大小,是满足定义的最小的2的n次方。
reg [dw-1:0] mem [(1<<aw) -1:0] /* synthesis syn_ramstyle="block_ram" */;
如果129个数据,>128需要256的ram
所以mem为[7:0],8位地址线
比方写了一个有300K的存储器块,那实际会生成一个512K的存储块,因为256K不够。
reg [dw-1:0] mem [300*1024 -1:0]
实际上
reg [dw-1:0] mem [512*1024 -1:0]
如果想节约空间怎么办,写一个256K的存储器RAM之后再写一个64K的存储器RAM,两种通过选择器连接形成一个新的256K + 64K的RAM。
code
module ram(
rclk ,raddr,dout,
//其他信号,举例dout
wclk,waddr,wdin,wen
);
//参数定义
parameter ADDR_W = 18; //2^8-1个
parameter DATA_W = 8;
//read输入信号定义
input rclk ;
input [ ADDR_W -1:0] raddr ;
//read输出信号定义
output[DATA_W-1:0] dout ;
//write输入信号定义
input wclk ;
input wen ; // write enable, active high
input [ ADDR_W -1:0] waddr ;
input [ DATA_W -1:0] wdin ;
//中间信号ram定义
reg[DATA_W -1:0] mem[(1<<ADDR_W)-1:0] ;//block ram
reg [ ADDR_W -1:0] ra_buffer;
//read
always @(posedge rclk )begin
ra_buffer<=raddr;
end
assign dout=mem[ra_buffer];//数据在地址后的一个周期
//write
always @(posedge wclk )begin
if(wen)
mem[waddr]=wdin;
end
initial $readmemh("init_mem",mem);
endmodule
RAMB36E1为bram,
BUFG两个时钟,
LUT2/3/6
IBUF
寄存器
对于640480一共需要640480个数据,640*480/1024=300K,所以为了省bram空间,拆成256K+64K,用addr[18]作为分界线
初始化mem文件
通过载入ram
initial $readmemh(“bmp_256K.mem”,mem);
module vga_dpram(
input wr_clk ,
input [11:0] wr_din,
input [18:0] wr_addr,
input wr,
input rd_clk,
input [18:0] rd_addr ,
output [11:0] rd_dout
);
wire[11:0] rd_dout_256K;
wire[11:0] rd_dout_64K;
//地址延迟一拍出data
reg addr18;
always @(posedge rd_clk )begin
addr18<=rd_addr[18];
end
assign rd_dout=(addr18==0)?rd_dout_256K:rd_dout_64K;
generic_dpram_256Kx12 ram_256Kx12(
.rclk ( rd_clk ),
.raddr( rd_addr[17:0] ),
.dout ( rd_dout_256K ),
.wclk ( wr_clk ),
.waddr( wr_addr[17:0] ),
.wdin ( wr_din ),
.wen ( wr&~rd_addr[18] )
);
generic_dpram_64Kx12 ram_64Kx12(
.rclk ( rd_clk ),
.raddr( rd_addr[17:0]),
.dout ( rd_dout_64K ),
.wclk ( wr_clk ),
.waddr( wr_addr[17:0] ),
.wdin ( wr_din ),
.wen ( wr &rd_addr[18] )
);
endmodule
module generic_dpram_256Kx12(
rclk ,raddr,dout,
//其他信号,举例dout
wclk,waddr,wdin,wen
);
//参数定义
parameter ADDR_W = 18;
parameter DATA_W = 12;
//read输入信号定义
input rclk ;
input [ ADDR_W -1:0] raddr ;
//read输出信号定义
output[DATA_W-1:0] dout ;
//write输入信号定义
input wclk ;
input wen ; // write enable, active high
input [ ADDR_W -1:0] waddr ;
input [ DATA_W -1:0] wdin ;
//中间信号bram定义
reg[DATA_W -1:0] mem[(1<<ADDR_W)-1:0] ;//block ram
reg [ ADDR_W -1:0] ra_buffer;
//read
always @(posedge rclk )begin
ra_buffer<=raddr;
end
assign dout=mem[ra_buffer];//数据在地址后的一个周期
//write
always @(posedge wclk )begin
if(wen)
mem[waddr]=wdin;
end
initial $readmemh("bmp_256k.mem",mem);
endmodule
module generic_dpram_64Kx12(
rclk ,raddr,dout,
//其他信号,举例dout
wclk,waddr,wdin,wen
);
//参数定义
parameter ADDR_W = 16;
parameter DATA_W = 12;
//read输入信号定义
input rclk ;
input [ ADDR_W -1:0] raddr ;
//read输出信号定义
output[DATA_W-1:0] dout ;
//write输入信号定义
input wclk ;
input wen ; // write enable, active high
input [ ADDR_W -1:0] waddr ;
input [ DATA_W -1:0] wdin ;
//中间信号bram定义
reg[DATA_W -1:0] mem[(1<<ADDR_W)-1:0] ;//block ram
reg [ ADDR_W -1:0] ra_buffer;
//read
always @(posedge rclk )begin
ra_buffer<=raddr;
end
assign dout=mem[ra_buffer];//数据在地址后的一个周期
//write
always @(posedge wclk )begin
if(wen)
mem[waddr]=wdin;
end
initial $readmemh("bmp_64k.mem",mem);
endmodule
tb
按地址读出数据,测试是否初始化数据正确
module ram_dout_tb();
parameter CNT_LEN=640*480;
reg rst_n;
//uut的输入信号
reg[11:0] wr_din ;
reg[18:0] wr_addr ;
reg wr;
initial begin
wr = 0;
wr_din=0;
wr_addr=0;
end
reg rd_clk;
reg [18:0] rd_addr ;
wire [11:0] rd_dout;
always @(posedge rd_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_addr<=0;
end
else if(rd_addr==CNT_LEN-1)begin
rd_addr<=0;
end
else begin
rd_addr<=rd_addr+1;
end
end
//时钟周期,单位为ns,可在此修改时钟周期。
parameter CYCLE = 40;
//复位时间,此时表示复位2个时钟周期的时间。
parameter RST_TIME = 2 ;
vga_dpram uut(
. wr_clk ( rd_clk ),
. wr_din( wr_din ),
. wr_addr( wr_addr ),
. wr ( wr ),
. rd_clk ( rd_clk ),
. rd_addr ( rd_addr ),
. rd_dout ( rd_dout )
);
//生成本地时钟25M
initial begin
rd_clk = 0;
forever
#(CYCLE/2)
rd_clk=~rd_clk;
end
//产生复位信号
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE*RST_TIME);
rst_n = 1;
end
endmodule
数据应该在地址后一个时钟输出。
抽样检测正确