我一直在为 SystemVerilog 开发 FIFO。第一次模拟结果很好。然而,在扩展模拟以尝试使其达到极限并考虑极端情况时,我遇到了一些问题。虽然我已经能够解决大部分问题,但仍有一个问题我无法解决。
//This fifo implements a Circular Buffer topology
//Note: The way the fifo is defined it is impossible to have a depth of 1
//This is because the pointers will always be on the same position
//The fifo is therefore always "empty"
module i2s_fifo #(
parameter WIDTH = 16,
parameter DEPTH = 10
)(
input logic clk_i,
input logic rst_ni,
//Write Communication Signals
output logic wready,
input logic wvalid,
input logic [WIDTH-1:0] fifo_data_in,
//Read Communication Signals
input logic rready,
output logic rvalid,
output logic [WIDTH-1:0] fifo_data_out,
//Debuging
output logic [$clog2(DEPTH)-1:0]wptr_tb,
output logic [$clog2(DEPTH)-1:0] rptr_tb
);
//This returns how many bits are needed to add and address of X size
//For example. $clog2(7) = 3. We need three bits to access a memory of 7 values.
//Basically converts from an integer to bits
localparam int unsigned byte_depth = $clog2(DEPTH);
//Pointers
logic [byte_depth-1:0] fifo_rptr, fifo_wptr;
assign wptr_tb = fifo_wptr;
assign rptr_tb = fifo_rptr;
//We cannot read if the FIFO is empty
//We cannot write if the FIFO is full
logic fifo_empty;
logic fifo_full;
always_comb begin : fifo_state
if(fifo_rptr == fifo_wptr) begin
assign fifo_empty = 1'b1;
end else begin
assign fifo_empty = 1'b0;
end
if(fifo_wptr == DEPTH)begin //Pointer is at the edge of fifo
if (fifo_rptr == 0) begin
fifo_full <= 1'b1;
end else begin
fifo_full <= 1'b0;
end
end else if (fifo_wptr+1 == fifo_rptr)begin
fifo_full <= 1'b1;
end else begin
fifo_full <= 1'b0;
end
end
//Handshake
logic revent, wevent;
assign rvalid = !fifo_empty;
assign revent = rvalid & rready;
assign wready = !fifo_full;
assign wevent = wvalid & wready;
//INCREASE POINTER POSSITION
always_ff @(posedge clk_i) begin
if(!rst_ni)begin
fifo_rptr <= {(byte_depth){1'b0}};
end else if (revent) begin
if (fifo_rptr == DEPTH) begin
fifo_rptr <= {(byte_depth){1'b0}};
end else begin
fifo_rptr <= fifo_rptr+1;
end
end
end
always_ff @(posedge clk_i) begin
if(!rst_ni)begin
fifo_wptr <= {(byte_depth){1'b0}};
end else if (wevent) begin
if (fifo_wptr == DEPTH) begin
fifo_wptr <= {(byte_depth){1'b0}};
end else begin
fifo_wptr <= fifo_wptr+1;
end
end
end
//Write and Save data
//data_type [rows][columns] array_name;
logic [DEPTH-1:0] [(WIDTH-1):0] fifo_storage;
always_ff @(posedge clk_i)begin
if(wevent) begin
//This access the row dictated by the pointer
fifo_storage[fifo_wptr] <= fifo_data_in;
end
end
always_ff @(posedge clk_i)begin
if(revent) begin
fifo_data_out <= fifo_storage[fifo_rptr];
end
end
endmodule
这是测试台
module i2s_fifo_tb(
);
localparam WIDTH = 16;
localparam DEPTH = 10;
logic clk;
logic rst;
logic wready;
logic wvalid;
logic [WIDTH-1:0]fifo_data_in;
logic rready;
logic rvalid;
logic [WIDTH-1:0]fifo_data_out;
logic [$clog2(DEPTH)-1:0]rptr_tb;
logic [$clog2(DEPTH)-1:0]wptr_tb;
i2s_fifo #(
.WIDTH(16),
.DEPTH(10)
)UUT(
.clk_i(clk),
.rst_ni(rst),
.wready,
.wvalid,
.fifo_data_in,
.fifo_data_out,
.rready,
.rvalid,
.rptr_tb,
.wptr_tb
);
always begin
clk = '1;
#10000;
clk = '0;
#10000;
end
initial begin
rst = '0;
rready = '0;
wvalid = 0;
fifo_data_in = 16'd0;
#20000;
//Fill FIFO
rst = 1;
wvalid = 1;
fifo_data_in = 16'h1111;
#20000;
fifo_data_in = 16'h2222;
#20000;
fifo_data_in = 16'h3333;
#20000;
fifo_data_in = 16'h4444;
#20000;
fifo_data_in = 16'h5555;
#20000;
fifo_data_in = 16'h6666;
#20000;
fifo_data_in = 16'h7777;
#20000;
fifo_data_in = 16'h8888;
#20000;
fifo_data_in = 16'h9999;
#20000;
fifo_data_in = 16'haaaa;
#20000;
fifo_data_in = 16'hbbbb;
#20000;
fifo_data_in = 16'hcccc;
#20000;
//Read FIFO
wvalid = '0;
rready = '1;
#100000;
//Pause Reading
rready= 0;
#100000;
//Resume Reading
rready = 1;
#160000;
//Check correct writing
rready = 0;
fifo_data_in = 16'h1111;
#20000;
fifo_data_in = 16'h2222;
#20000;
fifo_data_in = 16'h3333;
#20000;
fifo_data_in = 16'h4444;
#20000;
wvalid = 1;
fifo_data_in = 16'h5555;
#20000;
fifo_data_in = 16'h6666;
#20000;
fifo_data_in = 16'h7777;
#20000;
wvalid = 0;
rready = 1;
#80000;
//Multiple Writing and reading
rready = 0;
wvalid = 1;
fifo_data_in = 16'h1111;
#20000;
fifo_data_in = 16'h2222;
#20000;
fifo_data_in = 16'h3333;
#20000;
rready = 1;
fifo_data_in = 16'h4444;
#20000;
fifo_data_in = 16'h5555;
#20000;
fifo_data_in = 16'h6666;
#20000;
wvalid = 0;
//Fill it again, different point
#60000;
rready = 0;
wvalid = 1;
fifo_data_in = 16'h1111;
#20000
fifo_data_in = 16'h2222;
#20000
fifo_data_in = 16'h3333;
#20000
fifo_data_in = 16'h4444;
#20000
fifo_data_in = 16'h5555;
#20000
fifo_data_in = 16'h6666;
#20000
fifo_data_in = 16'h7777;
#20000
fifo_data_in = 16'h8888;
#20000
fifo_data_in = 16'h9999;
#20000
fifo_data_in = 16'haaaa;
#20000
fifo_data_in = 16'hbbbb;
#20000
fifo_data_in = 16'hcccc;
#20000
wvalid = 0;
rready = 1;
#600000
$finish;
end
endmodule
基本上在测试平台上,我只是输入大量数据并检查 fifo 是否表现得相应。然而。在模拟中,我观察到每当读取指针指向零位置时,输出都未定义。
起初我认为这可能是时序问题,在模拟开始时我根本没有在零位置上写入任何内容,因此,当第一次尝试读取 int 时,我有一个未定义的状态。因此,我决定进一步进行模拟。然而,我观察到,每当读指针查看零位置时,状态都是未定义的。
我的假设是这部分有问题
//Write and Save data
//data_type [rows][columns] array_name;
logic [DEPTH-1:0] [(WIDTH-1):0] fifo_storage;
always_ff @(posedge clk_i)begin
if(wevent) begin
//This acces the row dictated by the pointer
fifo_storage[fifo_wptr] <= fifo_data_in;
end
end
always_ff @(posedge clk_i)begin
if(revent) begin
fifo_data_out <= fifo_storage[fifo_rptr];
end
end
即我用来分配数据的向量是错误的。但是,我无法确定确切的问题。
我希望得到一些帮助。
以下是模拟中的两个捕获:
问题是当您从 FIFO 读取时,读指针超出范围,这会导致您从 FIFO 读取时出现未知值。
您将 FIFO
DEPTH
设置为 10,这意味着您的读指针至少需要 4 位,并且您正确地将指针声明为 [3:0]
。但是,指针的某些可能值是非法的,即 10 到 15。只有从 0 到 9 的指针值才是合法的。您的仿真波形清楚地表明,当 a
为 1 时,指针到达值 10(十六进制 revent
)。这就是 fifo_data_out
信号变得未知的原因。
请注意,输出信号是未知的,因为它是在指针采样为 10 时更新的。
为了防止这种情况发生,您可以考虑更改:
if (fifo_rptr == DEPTH) begin
至:
if (fifo_rptr == (DEPTH-1)) begin
但是,您需要确保您的设计正常工作。