关于verilog设计单口同步RAM的问题

一.这是我的实现代码:


`timescale 1ns / 1ps

module single_port_synrdwr#(
    parameter DATA_WIDTH = 8,
    parameter ADDR_WIDTH = 4,
    parameter ADDR_DEPTH = 2**ADDR_WIDTH
    )
(
    input                  sys_clk,
    input                  spsy_oe,   //output enable
    input                  spsy_wr,   //1 for write and 0 for read
    input                  spsy_cs,    //chip slect
    input [ADDR_WIDTH-1:0] spsy_addr,
    inout [DATA_WIDTH-1:0] spsy_data
    );
    
reg [DATA_WIDTH-1:0] spsy_mem [0:ADDR_DEPTH-1];
reg [DATA_WIDTH-1:0] spsy_mid_data;
 
//initialization for spsy_mem    
integer i;
initial begin
    for(i=0;i<ADDR_DEPTH;i=i+1)
    spsy_mem[i] = 8'b0;
end

//out data(ram is reading for now)
//now inout port is a output port
assign spsy_data = (spsy_cs & !spsy_wr & spsy_oe) ? spsy_mid_data : 8'bz;    

//write port
 always@(posedge sys_clk)begin
    if(spsy_cs & spsy_wr)
        spsy_mem[spsy_addr] <= spsy_mid_data;
    else
        spsy_mem[spsy_addr] <= spsy_mem[spsy_addr];
end 

//read port
always@(posedge sys_clk)begin
    if(spsy_cs & !spsy_wr)
        spsy_mid_data <= spsy_mem[spsy_addr];
    else
        spsy_mem[spsy_addr] <= spsy_mem[spsy_addr];
end   
     
endmodule

二.这是我的仿真代码:

`timescale 1ns / 1ps

module tb_ip_ram_practice_02();

parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 4;
parameter ADDR_DEPTH = 2**ADDR_WIDTH;//if unuseful_delete

reg                   sys_clk;
reg                   spsy_oe;
reg                   spsy_wr;
reg                   spsy_cs;
reg  [ADDR_WIDTH-1:0] spsy_addr;
wire [DATA_WIDTH-1:0] spsy_data;//wire for inout port
reg  [DATA_WIDTH-1:0] spsy_mid_data;

//in data
//now inout port is a input port
assign spsy_data  =  (spsy_cs & spsy_wr & !spsy_oe ) ? spsy_mid_data : 7'bz ;

initial begin
    sys_clk = 1'b0;
    forever 
        #10 sys_clk = ~sys_clk;
end

initial begin
    spsy_oe   = 1'b0; 
    spsy_wr   = 1'b0; 
    spsy_cs   = 1'b0; 
    spsy_addr = 4'b0;
    spsy_mid_data = 8'b0;
    
    //read
    #20
    @(posedge sys_clk) begin
        spsy_oe   = 1'b1;
        spsy_cs   = 1'b1;
    end
    repeat(15) #20 spsy_addr = spsy_addr + 4'b1;//read off 16 address's data
    
    //write
    #20
    @(posedge sys_clk) begin
        spsy_cs   = 1'b1;
        spsy_wr   = 1'b1;
    end
    repeat(15) #20 begin
        spsy_addr     = spsy_addr     + 4'b1;
        spsy_mid_data = spsy_mid_data + 4'b1;
    end

 //read agin
    #20
    @(posedge sys_clk) begin
        spsy_oe   = 1'b1;
        spsy_cs   = 1'b1;
    end
    repeat(15) #20 spsy_addr = spsy_addr + 4'b1;//read off 16 address's data
    
    @(posedge sys_clk)
    spsy_cs = 1'b0;
    #20
    $stop ;
    
end

single_port_synrdwr u(
    .sys_clk   (sys_clk),
    .spsy_oe   (spsy_oe),
    .spsy_wr   (spsy_wr),
    .spsy_cs   (spsy_cs),
    .spsy_addr (spsy_addr),
    .spsy_data (spsy_data)
);

endmodule

三.这是仿真之后的电路图
想请教大家的问题是:在wr拉高之后为什么mem里存的数据仍然是0,此时的spsy_mid_data已经开始递增了,为什么mem中存入的数据不随之递增?

img

*四.我改变了mem初始值
mem初始值全部赋1之后发现,最终的输出spsy_data是正确的,但是中间的spsy_mid_data和mem存储的数据是错误的。

img

代码,仿真都有问题
代码这么改

`timescale 1ns / 1ps
 
module single_port_synrdwr#(
    parameter DATA_WIDTH = 8,
    parameter ADDR_WIDTH = 4,
    parameter ADDR_DEPTH = 2**ADDR_WIDTH
    )
(
    input                  sys_clk,
    input                  spsy_oe,   //output enable
    input                  spsy_wr,   //1 for write and 0 for read
    input                  spsy_cs,    //chip slect
    input [ADDR_WIDTH-1:0] spsy_addr,
    inout [DATA_WIDTH-1:0] spsy_data
    );
    
reg [DATA_WIDTH-1:0] spsy_mem [0:ADDR_DEPTH-1];
reg [DATA_WIDTH-1:0] spsy_mid_data;
 
//initialization for spsy_mem    
integer i;
initial begin
    for(i=0;i<ADDR_DEPTH;i=i+1)
    spsy_mem[i] = 8'b0;
end
 
//out data(ram is reading for now)
//now inout port is a output port
assign spsy_data = (spsy_cs & !spsy_wr & spsy_oe) ? spsy_mid_data : 8'bz;    

//write port
always@(posedge sys_clk)begin
    if(spsy_cs & spsy_wr)
        spsy_mem[spsy_addr] <= spsy_data;
//    else
//        spsy_mem[spsy_addr] <= spsy_mem[spsy_addr];
end 
 
//read port
always@(posedge sys_clk)begin
    if(spsy_cs & !spsy_wr)
        spsy_mid_data <= spsy_mem[spsy_addr];
//    else
//        spsy_mem[spsy_addr] <= spsy_mem[spsy_addr];
end   

endmodule
 

仿真这么改

assign spsy_data  =  (spsy_cs & spsy_wr ) ? spsy_mid_data : 7'bz ;

always@(posedge sys_clk)begin
if(spsy_cs & spsy_wr)
spsy_mem[spsy_addr] <= spsy_mid_data;
else
spsy_mem[spsy_addr] <= spsy_mem[spsy_addr];
end

always@(posedge sys_clk)begin
if(spsy_cs & !spsy_wr)
spsy_mid_data <= spsy_mem[spsy_addr];
else
spsy_mem[spsy_addr] <= spsy_mem[spsy_addr];
end

spsy_mid_data这个信号在2个always模块里,在同一个时钟边沿,即被写,由被读。所有出现上面问题。
同一个信号在多个always模块内,同一时间点,不能同时读,又同时写

一个信号只能在同一个always进行操作。
读数据时,读使能有效,输出指定地址数据;写数据时,写使能有效,在指定地址写入指定数据;两者都需要片选有效,且不能读写不能同时进行。
首先保证代码没有逻辑错误和这种语法错误,这时候看仿真的错误才有意义。