SystemVerilog 4深度FIFO实现异常求助:读数据与标志位问题
SystemVerilog FIFO实现问题排查与解决
问题背景
使用SystemVerilog实现深度4、位宽8的FIFO模块及对应测试平台,但出现数据读取异常、标志位更新错误的问题。
FIFO模块代码
module fifo #( parameter DEPTH = 4, // Depth of the FIFO parameter WIDTH = 8 // Width of each entry )( input logic clk, input logic rst, input logic [WIDTH-1:0] data_in, input logic wr_en, input logic rd_en, output logic [WIDTH-1:0] data_out, output logic full, output logic empty ); logic [WIDTH-1:0] mem [0:DEPTH-1]; // FIFO memory logic [1:0] wr_ptr; // Write pointer (2-bit for DEPTH=4) logic [1:0] rd_ptr; // Read pointer (2-bit for DEPTH=4) logic [2:0] count; // Counter to track elements (needs 3 bits for DEPTH=4) always_ff @(posedge clk or posedge rst) begin if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; full <= 0; empty <= 1; end else begin // Handle simultaneous read and write if (wr_en && !full && rd_en && !empty) begin mem[wr_ptr] <= data_in; // Write new data wr_ptr <= (wr_ptr + 1) % DEPTH; rd_ptr <= (rd_ptr + 1) % DEPTH; // Read from FIFO end // Only write else if (wr_en && !full) begin mem[wr_ptr] <= data_in; wr_ptr <= (wr_ptr + 1) % DEPTH; count <= count + 1; end // Only read else if (rd_en && !empty) begin data_out <= mem[rd_ptr]; rd_ptr <= (rd_ptr + 1) % DEPTH; count <= count - 1; end // Correctly update full and empty flags full <= (count == DEPTH - 1); empty <= (count == 0); end end endmodule
测试平台代码
module test_fifo; // Declare signals logic clk; logic rst; logic [7:0] data_in; logic wr_en; logic rd_en; logic [7:0] data_out; logic full; logic empty; // Instantiate FIFO DUT fifo #(4, 8) dut ( .clk(clk), .rst(rst), .data_in(data_in), .data_out(data_out), .wr_en(wr_en), .rd_en(rd_en), .full(full), .empty(empty) ); // Clock generation initial begin clk = 0; forever #5 clk = ~clk; end // Reset generation initial begin rst = 1; #20 rst = 0; end // Test sequence initial begin // Initialize signals data_in = 0; wr_en = 0; rd_en = 0; // Wait for reset to complete @(negedge rst); // Test 1: Write data to FIFO $display("Test 1: Writing data to FIFO"); for (int i = 0; i < 4; i++) begin @(posedge clk); data_in = i; wr_en = 1; rd_en = 0; $display("Write Data: %h, Full: %b, Empty: %b", data_in, full, empty); end @(posedge clk); wr_en = 0; // Test 2: Read data from FIFO $display("Test 2: Reading data from FIFO"); for (int i = 0; i < 4; i++) begin @(posedge clk); wr_en = 0; rd_en = 1; $display("Read Data: %h, Full: %b, Empty: %b", data_out, full, empty); end @(posedge clk); rd_en = 0; // Test 3: Simultaneous read and write $display("Test 3: Simultaneous read and write"); for (int i = 0; i < 4; i++) begin @(posedge clk); data_in = i + 4; wr_en = 1; rd_en = 1; $display("Write Data: %h, Read Data: %h, Full: %b, Empty: %b", data_in, data_out, full, empty); end @(posedge clk); wr_en = 0; rd_en = 0; // Test 4: Test FIFO full condition $display("Test 4: Testing FIFO full condition"); for (int i = 0; i < 5; i++) begin @(posedge clk); data_in = i + 8; wr_en = 1; rd_en = 0; $display("Write Data: %h, Full: %b, Empty: %b", data_in, full, empty); end @(posedge clk); wr_en = 0; // Test 5: Test FIFO empty condition $display("Test 5: Testing FIFO empty condition"); for (int i = 0; i < 5; i++) begin @(posedge clk); wr_en = 0; rd_en = 1; $display("Read Data: %h, Full: %b, Empty: %b", data_out, full, empty); end @(posedge clk); rd_en = 0; // End simulation $display("Simulation completed."); $finish; end endmodule
仿真结果
Test 1: Writing data to FIFO Write Data: 00, Full: 0, Empty: 1 Write Data: 01, Full: 0, Empty: 1 Write Data: 02, Full: 0, Empty: 0 Write Data: 03, Full: 0, Empty: 0 Test 2: Reading data from FIFO Read Data: xx, Full: 0, Empty: 0 Read Data: 00, Full: 0, Empty: 0 Read Data: 01, Full: 1, Empty: 0 Read Data: 02, Full: 0, Empty: 0 Test 3: Simultaneous read and write Write Data: 04, Read Data: 03, Full: 0, Empty: 1 Write Data: 05, Read Data: 03, Full: 0, Empty: 1 Write Data: 06, Read Data: 03, Full: 0, Empty: 0 Write Data: 07, Read Data: 03, Full: 0, Empty: 0 Test 4: Testing FIFO full condition Write Data: 08, Full: 0, Empty: 0 Write Data: 09, Full: 0, Empty: 0 Write Data: 0a, Full: 1, Empty: 0 Write Data: 0b, Full: 0, Empty: 0 Write Data: 0c, Full: 0, Empty: 0 Test 5: Testing FIFO empty condition Read Data: 03, Full: 0, Empty: 0 Read Data: 0b, Full: 0, Empty: 0 Read Data: 0c, Full: 0, Empty: 0 Read Data: 08, Full: 0, Empty: 0 Read Data: 09, Full: 1, Empty: 0 Simulation completed.
具体问题
- 首次读操作输出
xx,但数据已正确写入FIFO; - 同时执行读写操作时,读数据输出异常,疑似
rd_ptr指针卡住; - Test4中FIFO满时
full标志未正确置位,Test5中读完所有数据后empty标志仍为0。
问题分析与解决方案
1. 首次读输出xx的问题
原因:读操作中data_out采用非阻塞赋值<=,在时钟上升沿触发赋值后,数据要到下一个时钟周期才会更新。但测试平台在同一个时钟上升沿就打印data_out,此时数据还处于未知状态。
解决:调整打印时机,等待数据更新完成后再打印,比如将打印移到时钟下降沿:
// Test 2修改示例 for (int i = 0; i < 4; i++) begin @(posedge clk); wr_en = 0; rd_en = 1; @(negedge clk); // 等待数据更新完成 $display("Read Data: %h, Full: %b, Empty: %b", data_out, full, empty); end
也可以在FIFO复位时初始化data_out为0,避免未知值出现。
2. 同时读写时rd_ptr卡住的问题
原因:同时读写的分支中,仅更新了rd_ptr但未给data_out赋值,导致输出一直保持旧值;且该分支未维护count变量,后续标志位计算也会出错。
解决:在同时读写分支中添加data_out赋值,保持count不变(读写抵消,元素数量不变):
if (wr_en && !full && rd_en && !empty) begin mem[wr_ptr] <= data_in; data_out <= mem[rd_ptr]; // 读取当前指针指向的数据 wr_ptr <= (wr_ptr + 1) % DEPTH; rd_ptr <= (rd_ptr + 1) % DEPTH; // count无需更新,读写操作抵消 end
3. full/empty标志位错误的问题
原因:
full的判断条件错误:深度为4的FIFO,count等于4时才是满,原代码写成count == DEPTH -1;- 标志位采用时序逻辑更新,依赖当前周期的
count值,但count的非阻塞赋值要到下一个周期才生效,导致标志位延迟一个周期更新; - 同时读写分支未维护
count,导致count值错误。
解决:
- 修正
full判断条件,改用组合逻辑计算标志位(避免时序延迟问题):
// 移除always_ff中的full/empty赋值,替换为组合逻辑 assign full = (count == DEPTH); assign empty = (count == 0);
- 确保所有操作分支中
count的更新逻辑正确:仅写时count+1,仅读时count-1,同时读写时count不变。
修正后的完整FIFO模块
module fifo #( parameter DEPTH = 4, // Depth of the FIFO parameter WIDTH = 8 // Width of each entry )( input logic clk, input logic rst, input logic [WIDTH-1:0] data_in, input logic wr_en, input logic rd_en, output logic [WIDTH-1:0] data_out, output logic full, output logic empty ); logic [WIDTH-1:0] mem [0:DEPTH-1]; // FIFO memory logic [$clog2(DEPTH)-1:0] wr_ptr; // 自动计算指针宽度,增强通用性 logic [$clog2(DEPTH)-1:0] rd_ptr; logic [$clog2(DEPTH+1)-1:0] count; // 计数范围覆盖0~DEPTH // 组合逻辑计算标志位,避免时序延迟 assign full = (count == DEPTH); assign empty = (count == 0); always_ff @(posedge clk or posedge rst) begin if (rst) begin wr_ptr <= '0; rd_ptr <= '0; count <= '0; data_out <= '0; // 复位初始化data_out,避免未知值 end else begin // 默认保持原值,避免生成 latch data_out <= data_out; wr_ptr <= wr_ptr; rd_ptr <= rd_ptr; count <= count; // 同时读写 if (wr_en && !full && rd_en && !empty) begin mem[wr_ptr] <= data_in; data_out <= mem[rd_ptr]; wr_ptr <= (wr_ptr + 1) % DEPTH; rd_ptr <= (rd_ptr + 1) % DEPTH; end // 仅写 else if (wr_en && !full) begin mem[wr_ptr] <= data_in; wr_ptr <= (wr_ptr + 1) % DEPTH; count <= count + 1; end // 仅读 else if (rd_en && !empty) begin data_out <= mem[rd_ptr]; rd_ptr <= (rd_ptr + 1) % DEPTH; count <= count - 1; end end end endmodule
内容的提问来源于stack exchange,提问作者apna
相关产品推荐
相关产品推荐

