You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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值错误。

解决:

  1. 修正full判断条件,改用组合逻辑计算标志位(避免时序延迟问题):
// 移除always_ff中的full/empty赋值,替换为组合逻辑
assign full = (count == DEPTH);
assign empty = (count == 0);
  1. 确保所有操作分支中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 12:58:10