FIFO 的 verilog 代码(先进先出)没有显示正确的结果?
verilog code of FIFO (first in first out) is not showing the proper result?
我想设计一个有一定深度和宽度的FIFO。 FIFO的Verilog代码是用Vivado 2017.4写的。该代码能够读取输入数据,但它只显示 XX 作为输出。下面给出了 FIFO 的设计源和测试平台。帮我找出问题所在。
module fifo #(parameter WIDTH=8, parameter DEPTH=8) (
input wire [WIDTH-1:0] data_in,
output reg [WIDTH-1:0] data_out,
output reg data_valid,
input wire reset,
input wire clk
);
function integer clog2(input reg [32-1:0] value);
begin
value = value-1;
for (clog2=0; value>0; clog2=clog2+1)
value = value>>1;
end
endfunction
reg [WIDTH-1:0] data [DEPTH-1:0];
reg [clog2(DEPTH)-1:0] write_pointer;
reg [clog2(DEPTH)-1:0] read_pointer;
always @(posedge clk) begin
if (reset == 1'b0) begin
write_pointer <= 0;
read_pointer <= 1;
data_valid <= 0;
end else begin
if (write_pointer == DEPTH-1) write_pointer <= 0;
else write_pointer <= write_pointer + 1;
if (read_pointer == DEPTH-1) read_pointer <= 0;
else read_pointer <= read_pointer + 1;
data[write_pointer] <= data_in;
data_out <= data[read_pointer];
end
if (read_pointer == 0) data_valid <= 1'b1;
end
endmodule
测试台
`timescale 1ns / 1ps
module fifo_tb;
parameter WIDTH = 16;
parameter DEPTH = 8;
reg reset;
reg clk;
reg [(WIDTH-1):0] data_in;
wire [(WIDTH-1):0] data_out;
wire data_valid;
fifo #(WIDTH,DEPTH) U0(data_in,data_out,data_valid,reset,clk);
initial begin
clk = 0;
reset = 1;
#1 reset = 0;
#1 reset = 1;
end
// Create clock
always
#5 clk = ~clk;
reg signed [15:0] rom_memory [4096-1:0];
integer i=0;
initial
begin
$readmemh("C:\Users\input_7_zz.txt",rom_memory);
end
always@(posedge clk )
begin
if(~reset)
begin
data_in <= 0;
end
else
begin
data_in <= rom_memory[i];
i <= i+ 1;
end
end
endmodule
在您的测试台代码中,时钟每 5 个刻度更改一次:
// Create clock
always
#5 clk = ~clk;
RTL中的reset使用这个时钟的posedge:
always @(posedge clk) begin
if (reset == 1'b0) begin
write_pointer <= 0;
read_pointer <= 1;
data_valid <= 0;
因此,只有在复位期间检测到posedge clk 时,复位动作才会发生。换句话说,你有一个同步重置。
尽管如此,在您的测试台代码中,您的重置只持续了一个滴答声:
initial begin
clk = 0;
reset = 1;
#1 reset = 0;
#1 reset = 1; // << one tick after the reset was asserted.
end
您可能有 2 种可能的解决方案。
1) 保持同步复位,但要保证在复位过程中能检测到clk的posedge。所以,让它大于一个时钟周期(或者至少半个时钟周期,如果你确定它什么时候到来的话)。像 5 这样的东西应该适用于你的情况。将其设置为 10 或更多以确保任意起点。
initial begin
clk = 0;
reset = 1;
#1 reset = 0;
#5 reset = 1; // << 5 tick after the reset was asserted.
上面的重置将从时间 1 持续到时间 6。第一个 posegde 将在时间 5 发生。因此,对您的情况来说应该足够了。
2) 您可以在 RTL 中使用异步复位,如下所示:
always @(posedge clk or reset) begin
if (reset == 1'b0) begin
write_pointer <= 0;
read_pointer <= 1;
data_valid <= 0;
在上面的例子中,只要 'reset' 发生变化,就会执行 always 块。但是,您必须注意那里其他任务的时间安排。当您取消断言 reset 与 posedge clk 有一些偏移时,它们可能会发生。
我想设计一个有一定深度和宽度的FIFO。 FIFO的Verilog代码是用Vivado 2017.4写的。该代码能够读取输入数据,但它只显示 XX 作为输出。下面给出了 FIFO 的设计源和测试平台。帮我找出问题所在。
module fifo #(parameter WIDTH=8, parameter DEPTH=8) (
input wire [WIDTH-1:0] data_in,
output reg [WIDTH-1:0] data_out,
output reg data_valid,
input wire reset,
input wire clk
);
function integer clog2(input reg [32-1:0] value);
begin
value = value-1;
for (clog2=0; value>0; clog2=clog2+1)
value = value>>1;
end
endfunction
reg [WIDTH-1:0] data [DEPTH-1:0];
reg [clog2(DEPTH)-1:0] write_pointer;
reg [clog2(DEPTH)-1:0] read_pointer;
always @(posedge clk) begin
if (reset == 1'b0) begin
write_pointer <= 0;
read_pointer <= 1;
data_valid <= 0;
end else begin
if (write_pointer == DEPTH-1) write_pointer <= 0;
else write_pointer <= write_pointer + 1;
if (read_pointer == DEPTH-1) read_pointer <= 0;
else read_pointer <= read_pointer + 1;
data[write_pointer] <= data_in;
data_out <= data[read_pointer];
end
if (read_pointer == 0) data_valid <= 1'b1;
end
endmodule
测试台
`timescale 1ns / 1ps
module fifo_tb;
parameter WIDTH = 16;
parameter DEPTH = 8;
reg reset;
reg clk;
reg [(WIDTH-1):0] data_in;
wire [(WIDTH-1):0] data_out;
wire data_valid;
fifo #(WIDTH,DEPTH) U0(data_in,data_out,data_valid,reset,clk);
initial begin
clk = 0;
reset = 1;
#1 reset = 0;
#1 reset = 1;
end
// Create clock
always
#5 clk = ~clk;
reg signed [15:0] rom_memory [4096-1:0];
integer i=0;
initial
begin
$readmemh("C:\Users\input_7_zz.txt",rom_memory);
end
always@(posedge clk )
begin
if(~reset)
begin
data_in <= 0;
end
else
begin
data_in <= rom_memory[i];
i <= i+ 1;
end
end
endmodule
在您的测试台代码中,时钟每 5 个刻度更改一次:
// Create clock
always
#5 clk = ~clk;
RTL中的reset使用这个时钟的posedge:
always @(posedge clk) begin
if (reset == 1'b0) begin
write_pointer <= 0;
read_pointer <= 1;
data_valid <= 0;
因此,只有在复位期间检测到posedge clk 时,复位动作才会发生。换句话说,你有一个同步重置。
尽管如此,在您的测试台代码中,您的重置只持续了一个滴答声:
initial begin
clk = 0;
reset = 1;
#1 reset = 0;
#1 reset = 1; // << one tick after the reset was asserted.
end
您可能有 2 种可能的解决方案。
1) 保持同步复位,但要保证在复位过程中能检测到clk的posedge。所以,让它大于一个时钟周期(或者至少半个时钟周期,如果你确定它什么时候到来的话)。像 5 这样的东西应该适用于你的情况。将其设置为 10 或更多以确保任意起点。
initial begin
clk = 0;
reset = 1;
#1 reset = 0;
#5 reset = 1; // << 5 tick after the reset was asserted.
上面的重置将从时间 1 持续到时间 6。第一个 posegde 将在时间 5 发生。因此,对您的情况来说应该足够了。
2) 您可以在 RTL 中使用异步复位,如下所示:
always @(posedge clk or reset) begin
if (reset == 1'b0) begin
write_pointer <= 0;
read_pointer <= 1;
data_valid <= 0;
在上面的例子中,只要 'reset' 发生变化,就会执行 always 块。但是,您必须注意那里其他任务的时间安排。当您取消断言 reset 与 posedge clk 有一些偏移时,它们可能会发生。