同步RAM相关
Sync RAM related
我正在尝试在 Verilog 中为同步 ram 模拟以下代码。
当我尝试写入特定地址时,dataOut 并没有按照我的预期出现。读取指定地址数据时跳过1个地址。
谁能告诉我问题所在?
这里是 GtkWave 模拟结果:
module ram
# (parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 10,
parameter DEPTH = 1 << ADDR_WIDTH
)
( input clk,
input [ADDR_WIDTH-1:0] addr,
input [DATA_WIDTH-1:0] dataIn,
input enable,
input write,
input read,
input resetRam,
output[DATA_WIDTH-1:0] dataOut
);
reg [DATA_WIDTH-1:0] tmp_data;
reg [DATA_WIDTH-1:0] mem [DEPTH-1:0];
//reset
always @(posedge clk) begin
if(enable & resetRam) begin
for (integer i = 0; i < 2**ADDR_WIDTH; i = i + 1)
begin
mem[i] = {DATA_WIDTH{1'b0}};
end
end
end
//write
always @ (posedge clk) begin
if (enable & write & !read)
mem[addr] <= dataIn;
end
//read
always @ (posedge clk) begin
if (enable & !write & read)
tmp_data <= mem[addr];
end
assign dataOut = enable & read & !write ? tmp_data : 'hz || enable & resetRam ? 10'b0 : 'hz;
endmodule
module ram_tb;
parameter ADDR_WIDTH = 4;
parameter DATA_WIDTH = 10;
parameter DEPTH = 1 << ADDR_WIDTH;
reg clk;
reg enable;
reg write;
reg read;
reg resetRam;
reg [ADDR_WIDTH-1:0] addr;
wire [DATA_WIDTH-1:0] dataIn;
wire [DATA_WIDTH-1:0] dataOut;
reg [DATA_WIDTH-1:0] tb_data;
ram #(.DATA_WIDTH(DATA_WIDTH)) iram(
.clk(clk),
.addr(addr),
.dataIn(dataIn),
.enable(enable),
.write(write),
.read(read),
.resetRam(resetRam),
.dataOut(dataOut)
);
always #10 clk = ~clk;
assign dataIn = !read ? tb_data : 'hz ;
initial begin
$monitor("addrs = %b resetRam=%b Write = %b read=%b dataIn = %b dataOut = %b", addr,resetRam,write,read,dataIn,dataOut);
$dumpfile("ram_tb.vcd");
$dumpvars(0,ram_tb);
{clk, enable, write, addr, tb_data, read,resetRam} <= 0;
// //writing all the adrress with random value
// for (integer i = 0; i < DEPTH; i= i+1) begin
// @(posedge clk) addr <= i; write = 1; enable =1; read = 0; tb_data <= $random;
// end
// //reading them
// for (integer i = 0; i < DEPTH; i= i+1) begin
// @(posedge clk) addr = i; write = 0; enable = 1; read = 1;
// end
//Writing at specific address
@(posedge clk) addr = 4'b1000; write = 1; enable =1; read = 0; tb_data <= $random;
@(posedge clk) addr = 4'b1111; write = 1; enable =1; read = 0; tb_data <= $random;
@(posedge clk) addr = 4'b1001; write = 1; enable =1; read = 0; tb_data <= $random;
@(posedge clk) addr = 4'b0001; write = 1; enable =1; read = 0; tb_data <= $random;
//reading them
@(posedge clk) addr = 4'b1000;write = 0; enable =1; read = 1;
@(posedge clk) addr = 4'b1111;write = 0; enable =1; read = 1;
@(posedge clk) addr = 4'b1001;write = 0; enable =1; read = 1;
@(posedge clk) addr = 4'b0001;write = 0; enable =1; read = 1;
// //reset memory
// for (integer i = 0; i < DEPTH; i= i+1) begin
// repeat (1) @(posedge clk) addr <= i; enable <= 1; resetRam <=1;
// end
#200 $finish;
end
endmodule
//iverilog -o ram_tb.vpp ram.v
//vvp ram_tb.vpp
您有竞争条件。您应该在设计和测试台中为所有同步信号使用非阻塞分配 (<=
)。
在设计中,更改:
mem[i] = {DATA_WIDTH{1'b0}};
至:
mem[i] <= {DATA_WIDTH{1'b0}};
在测试台中,使用:
//Writing at specific address
@(posedge clk) addr <= 4'b1000; write <= 1; enable <=1; read <= 0; tb_data <= $random;
@(posedge clk) addr <= 4'b1111; write <= 1; enable <=1; read <= 0; tb_data <= $random;
@(posedge clk) addr <= 4'b1001; write <= 1; enable <=1; read <= 0; tb_data <= $random;
@(posedge clk) addr <= 4'b0001; write <= 1; enable <=1; read <= 0; tb_data <= $random;
//reading them
@(posedge clk) addr <= 4'b1000;write <= 0; enable <=1; read <= 1;
@(posedge clk) addr <= 4'b1111;write <= 0; enable <=1; read <= 1;
@(posedge clk) addr <= 4'b1001;write <= 0; enable <=1; read <= 1;
@(posedge clk) addr <= 4'b0001;write <= 0; enable <=1; read <= 1;
通过上述更改,我在 edaplayground 上使用 iverilog
获得了您的预期输出。
我正在尝试在 Verilog 中为同步 ram 模拟以下代码。
当我尝试写入特定地址时,dataOut 并没有按照我的预期出现。读取指定地址数据时跳过1个地址。
谁能告诉我问题所在?
这里是 GtkWave 模拟结果:
module ram
# (parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 10,
parameter DEPTH = 1 << ADDR_WIDTH
)
( input clk,
input [ADDR_WIDTH-1:0] addr,
input [DATA_WIDTH-1:0] dataIn,
input enable,
input write,
input read,
input resetRam,
output[DATA_WIDTH-1:0] dataOut
);
reg [DATA_WIDTH-1:0] tmp_data;
reg [DATA_WIDTH-1:0] mem [DEPTH-1:0];
//reset
always @(posedge clk) begin
if(enable & resetRam) begin
for (integer i = 0; i < 2**ADDR_WIDTH; i = i + 1)
begin
mem[i] = {DATA_WIDTH{1'b0}};
end
end
end
//write
always @ (posedge clk) begin
if (enable & write & !read)
mem[addr] <= dataIn;
end
//read
always @ (posedge clk) begin
if (enable & !write & read)
tmp_data <= mem[addr];
end
assign dataOut = enable & read & !write ? tmp_data : 'hz || enable & resetRam ? 10'b0 : 'hz;
endmodule
module ram_tb;
parameter ADDR_WIDTH = 4;
parameter DATA_WIDTH = 10;
parameter DEPTH = 1 << ADDR_WIDTH;
reg clk;
reg enable;
reg write;
reg read;
reg resetRam;
reg [ADDR_WIDTH-1:0] addr;
wire [DATA_WIDTH-1:0] dataIn;
wire [DATA_WIDTH-1:0] dataOut;
reg [DATA_WIDTH-1:0] tb_data;
ram #(.DATA_WIDTH(DATA_WIDTH)) iram(
.clk(clk),
.addr(addr),
.dataIn(dataIn),
.enable(enable),
.write(write),
.read(read),
.resetRam(resetRam),
.dataOut(dataOut)
);
always #10 clk = ~clk;
assign dataIn = !read ? tb_data : 'hz ;
initial begin
$monitor("addrs = %b resetRam=%b Write = %b read=%b dataIn = %b dataOut = %b", addr,resetRam,write,read,dataIn,dataOut);
$dumpfile("ram_tb.vcd");
$dumpvars(0,ram_tb);
{clk, enable, write, addr, tb_data, read,resetRam} <= 0;
// //writing all the adrress with random value
// for (integer i = 0; i < DEPTH; i= i+1) begin
// @(posedge clk) addr <= i; write = 1; enable =1; read = 0; tb_data <= $random;
// end
// //reading them
// for (integer i = 0; i < DEPTH; i= i+1) begin
// @(posedge clk) addr = i; write = 0; enable = 1; read = 1;
// end
//Writing at specific address
@(posedge clk) addr = 4'b1000; write = 1; enable =1; read = 0; tb_data <= $random;
@(posedge clk) addr = 4'b1111; write = 1; enable =1; read = 0; tb_data <= $random;
@(posedge clk) addr = 4'b1001; write = 1; enable =1; read = 0; tb_data <= $random;
@(posedge clk) addr = 4'b0001; write = 1; enable =1; read = 0; tb_data <= $random;
//reading them
@(posedge clk) addr = 4'b1000;write = 0; enable =1; read = 1;
@(posedge clk) addr = 4'b1111;write = 0; enable =1; read = 1;
@(posedge clk) addr = 4'b1001;write = 0; enable =1; read = 1;
@(posedge clk) addr = 4'b0001;write = 0; enable =1; read = 1;
// //reset memory
// for (integer i = 0; i < DEPTH; i= i+1) begin
// repeat (1) @(posedge clk) addr <= i; enable <= 1; resetRam <=1;
// end
#200 $finish;
end
endmodule
//iverilog -o ram_tb.vpp ram.v
//vvp ram_tb.vpp
您有竞争条件。您应该在设计和测试台中为所有同步信号使用非阻塞分配 (<=
)。
在设计中,更改:
mem[i] = {DATA_WIDTH{1'b0}};
至:
mem[i] <= {DATA_WIDTH{1'b0}};
在测试台中,使用:
//Writing at specific address
@(posedge clk) addr <= 4'b1000; write <= 1; enable <=1; read <= 0; tb_data <= $random;
@(posedge clk) addr <= 4'b1111; write <= 1; enable <=1; read <= 0; tb_data <= $random;
@(posedge clk) addr <= 4'b1001; write <= 1; enable <=1; read <= 0; tb_data <= $random;
@(posedge clk) addr <= 4'b0001; write <= 1; enable <=1; read <= 0; tb_data <= $random;
//reading them
@(posedge clk) addr <= 4'b1000;write <= 0; enable <=1; read <= 1;
@(posedge clk) addr <= 4'b1111;write <= 0; enable <=1; read <= 1;
@(posedge clk) addr <= 4'b1001;write <= 0; enable <=1; read <= 1;
@(posedge clk) addr <= 4'b0001;write <= 0; enable <=1; read <= 1;
通过上述更改,我在 edaplayground 上使用 iverilog
获得了您的预期输出。