module single_port_ram
#(
parameter ADDR_width = 4, //计数器的位宽比地址位宽多一位
parameter DATA_width = 16 , //深度
parameter depth = 2** ADDR_width
)
(
input clk,
input [ADDR_width-1 :0] addr,
//input [DATA_width-1 :0] data, //单端口 -- 读写都是同一个存储器中进行操作
inout [DATA_width-1 :0] data, //端口类型
input cs,
input wr,
input oe
);
reg [DATA_width-1 :0] mem[0:depth-1 ];
reg [DATA_width-1 :0] mid_data; //每一个地址中的宽度
//&两边都会算 但是只要有一个为假,那么将全部为假
always@(posedge clk) begin
if(cs & wr ) begin
mem[addr] <= data ;
end
end
always@(posedge clk) begin
if(cs &!wr) begin
mid_data <= mem[addr] ; //每个时钟向外读
end
end
assign data = (cs & oe & !wr) ? mid_data :'hz; //oe使能写
endmodule
module ram_tb(
);
parameter ADDR_width = 4 ; //计数器的位宽比地址位宽多一位
parameter DATA_width = 16 ; //深度
parameter depth = 2** ADDR_width ;
reg clk;
reg [ADDR_width-1 :0] addr;
wire [DATA_width-1 :0] data; //Inout 类型
reg cs;
reg wr;
reg oe;
reg [DATA_width-1 :0] mid_data; //每一个地址中的宽度
always #5 clk = ~clk;
initial begin
clk = 0;
end
assign data = !oe ? mid_data : 'hz; //写进去同步到内存
initial begin
cs = 0;
wr = 0;
addr =0;
mid_data =0 ; //中间的数据
oe = 0;
repeat(2) @(posedge clk) ; //等两个升沿
//写
for (integer i= 0 ; i< depth ;i = i+1) begin
repeat (1) @(posedge clk) begin
addr = i ;
wr = 1;
cs= 1;
oe = 0 ;
mid_data = $random;
end
end
//读
for (integer i= 0 ; i< depth ;i = i+1) begin //将内存的数据一个一个之中读取出出来
repeat (1) @(posedge clk) begin
addr = i ;
wr = 0; // ?
cs= 1;
oe =1 ;
end
end
#20 $finish ;
end
single_port_ram #
(.ADDR_width(ADDR_width) ,
.DATA_width(DATA_width) ,
.depth (depth)
)
single_ram_tb(
.clk ( clk ) ,
.addr ( addr ) ,
.data ( data ) , //单端口 -- 读写都是同一个存储器中进行操作
.cs ( cs ) ,
.wr ( wr ) ,
.oe ( oe ) //
);
endmodule
程序的代码:
assign data = (cs & oe & !wr)? mid_data: 'hz;
使能无效,直接将他高阻态赋值给inout端口,意味着悬空,不做别的处理。
单端口:在某个时钟周期只能进行读/写操作。
双端口:
module double_ram #(
parameter ADDR_width = 4,
parameter DATA_WIDTH = 8,
parameter depth = 2** ADDR_width
)
(
input w_clk ,
input r_clk ,
input rst_n ,
input w_en ,
input r_en,
input [ADDR_width- 1: 0] w_addr,
input [ADDR_width- 1: 0] r_addr,
input [DATA_WIDTH-1 :0] w_data,
output reg [DATA_WIDTH-1 :0] r_data
);
reg [DATA_WIDTH-1 :0] mem [0 : depth -1]; //存储
//写
always @(posedge w_clk or negedge rst_n)
begin
if(!rst_n) //内存单元全部清0
// mem[w_addr] <= 'b0;
for(integer i = 0 ; i< depth ; i= i+1)
mem[i] <= 'b0; //每一个内存单元都清0
else if(w_en)
mem[w_addr] <= w_data; //内存的数据呢?
end
//从内存中读取数据
always @(posedge r_clk)
begin
if(r_en)
r_data <= mem[r_addr]; //地址是要一个递增
else
r_data <= 8 'd0;
end
endmodule
module double_ram_tb(
);
parameter ADDR_width = 4 ;
parameter DATA_width = 8 ; // 数据位宽
parameter depth = 2** ADDR_width ; //32深度
reg w_clk;
reg r_clk;
reg rst_n;
reg w_en;
reg r_en;
reg [ADDR_width - 1 :0] w_addr;
reg [ADDR_width - 1 :0] r_addr;
reg [DATA_width - 1 :0] w_data;
wire [DATA_width - 1 :0] r_data ; //输出的数据
// reg [DATA_WIDTH-1 :0] mem [0 : depth -1]; //存储
always #5 w_clk=~ w_clk ;
always #5 r_clk=~ r_clk ;
initial begin
w_clk = 0;
r_clk = 0;
rst_n = 0;
w_en = 0;
r_en = 0;
w_addr = 0;
r_addr = 0;
w_data = 0;
// end
#10;
rst_n = 1;
//地址和数据按照顺序 写
for (integer index = 0; index < 16 ; index = index+1 ) //串行的数据进行
begin
@(posedge w_clk )
begin
w_en = 1;
w_addr = index ;
w_data = index ; //
// mem[index] = index ;
end
end
@(posedge w_clk) w_en = 0;
//读
for (integer index = 0; index < 16 ; index = index+1 ) //串行的数据进行
begin
@(posedge r_clk )
begin
r_en = 1;
r_addr = index ; //从0开始
// w_data = index ;
// mem[index] = index ;
end
end
@ (posedge r_clk ) r_en = 0;
#10;
$finish;
end
// assign r_data = r_en ? w_data : 'hz ;
double_ram #(
. ADDR_width (ADDR_width),
. DATA_width (DATA_width),
. depth (depth)
)
double_tb
(
.w_clk ( w_clk ) ,
.r_clk ( r_clk ) ,
.rst_n ( rst_n ) ,
.w_en ( w_en ) ,
.r_en ( r_en ) ,
.w_addr ( w_addr ) ,
.r_addr ( r_addr ) ,
.w_data ( w_data ) ,
.r_data ( r_data )
);
endmodule