使用claude迭代三次,终于写出功能正确的异步fifo了
专栏:ExASIC Jan. 13, 2026, 4:06 p.m. 23 阅读
1、满标志没有拉高;2、深度为8的fifo,写了10个数据(满没拉高),读了10次,空也没立即拉高,可以读出10个数据;3、没来rd_en之前就有第一个读数据输出

这是之前的实验《给CodeBuddy写的async fifo做了个验证,发现了两个bug》,发现满标志逻辑有错误。今天用clause也做了同样的实验,也有同样的bug。下面总结了遇到的几个问题:
1、满标志没有拉高
2、深度为8的fifo,写了10个数据(满没拉高),读了10次,空也没立即拉高,可以读出10个数据
3、没来rd_en之前就有第一个读数据输出

给ai出错信息,他去修改迭代,三次后逻辑终于正确了。最终的verilog代码如下:

//=============================================================================
// 异步FIFO设计模块(修复版V2)
// 特性:位宽和深度可通过parameter配置,带满/空标志
//=============================================================================

module async_fifo #(
    parameter DATA_WIDTH = 8,           // 数据位宽
    parameter FIFO_DEPTH = 8            // FIFO深度(必须为2的幂次)
)(
    // 写时钟域
    input  wire                     wr_clk,     // 写时钟
    input  wire                     wr_rst_n,   // 写复位(低有效)
    input  wire                     wr_en,      // 写使能
    input  wire [DATA_WIDTH-1:0]    wr_data,    // 写数据
    output wire                     full,       // 满标志

    // 读时钟域
    input  wire                     rd_clk,     // 读时钟
    input  wire                     rd_rst_n,   // 读复位(低有效)
    input  wire                     rd_en,      // 读使能
    output reg  [DATA_WIDTH-1:0]    rd_data,    // 读数据(寄存器输出)
    output wire                     empty       // 空标志
);

    //=========================================================================
    // 参数定义
    //=========================================================================
    localparam ADDR_WIDTH = $clog2(FIFO_DEPTH);  // 地址位宽
    localparam PTR_WIDTH  = ADDR_WIDTH + 1;      // 指针位宽(多一位用于判断空满)

    //=========================================================================
    // 信号定义
    //=========================================================================
    // FIFO存储器
    reg [DATA_WIDTH-1:0] fifo_mem [0:FIFO_DEPTH-1];

    // 二进制指针
    reg [PTR_WIDTH-1:0] wr_ptr_bin;      // 写指针(二进制)
    reg [PTR_WIDTH-1:0] rd_ptr_bin;      // 读指针(二进制)

    // 格雷码指针
    reg [PTR_WIDTH-1:0] wr_ptr_gray;     // 写指针(格雷码)
    reg [PTR_WIDTH-1:0] rd_ptr_gray;     // 读指针(格雷码)

    // 同步后的格雷码指针
    reg [PTR_WIDTH-1:0] wr_ptr_gray_sync1, wr_ptr_gray_sync2;  // 写指针同步到读时钟域
    reg [PTR_WIDTH-1:0] rd_ptr_gray_sync1, rd_ptr_gray_sync2;  // 读指针同步到写时钟域

    // 下一个指针值
    wire [PTR_WIDTH-1:0] wr_ptr_bin_next;
    wire [PTR_WIDTH-1:0] rd_ptr_bin_next;
    wire [PTR_WIDTH-1:0] wr_ptr_gray_next;
    wire [PTR_WIDTH-1:0] rd_ptr_gray_next;

    // 实际写入和读取使能
    wire wr_valid;
    wire rd_valid;

    //=========================================================================
    // 二进制转格雷码
    //=========================================================================
    function [PTR_WIDTH-1:0] bin2gray;
        input [PTR_WIDTH-1:0] bin;
        begin
            bin2gray = bin ^ (bin >> 1);
        end
    endfunction

    //=========================================================================
    // 有效写入和读取信号
    //=========================================================================
    assign wr_valid = wr_en && !full;
    assign rd_valid = rd_en && !empty;

    //=========================================================================
    // 写时钟域逻辑
    //=========================================================================

    // 写指针计算
    assign wr_ptr_bin_next  = wr_ptr_bin + wr_valid;
    assign wr_ptr_gray_next = bin2gray(wr_ptr_bin_next);

    // 写指针寄存器
    always @(posedge wr_clk or negedge wr_rst_n) begin
        if (!wr_rst_n) begin
            wr_ptr_bin  <= {PTR_WIDTH{1'b0}};
            wr_ptr_gray <= {PTR_WIDTH{1'b0}};
        end else begin
            wr_ptr_bin  <= wr_ptr_bin_next;
            wr_ptr_gray <= wr_ptr_gray_next;
        end
    end

    // 写数据到FIFO存储器
    always @(posedge wr_clk) begin
        if (wr_valid) begin
            fifo_mem[wr_ptr_bin[ADDR_WIDTH-1:0]] <= wr_data;
        end
    end

    // 读指针同步到写时钟域(两级同步)
    always @(posedge wr_clk or negedge wr_rst_n) begin
        if (!wr_rst_n) begin
            rd_ptr_gray_sync1 <= {PTR_WIDTH{1'b0}};
            rd_ptr_gray_sync2 <= {PTR_WIDTH{1'b0}};
        end else begin
            rd_ptr_gray_sync1 <= rd_ptr_gray;
            rd_ptr_gray_sync2 <= rd_ptr_gray_sync1;
        end
    end

    // 满标志生成(在写时钟域判断)
    assign full = (wr_ptr_gray[PTR_WIDTH-1]   != rd_ptr_gray_sync2[PTR_WIDTH-1]  ) &&
                  (wr_ptr_gray[PTR_WIDTH-2]   != rd_ptr_gray_sync2[PTR_WIDTH-2]  ) &&
                  (wr_ptr_gray[PTR_WIDTH-3:0] == rd_ptr_gray_sync2[PTR_WIDTH-3:0]);

    //=========================================================================
    // 读时钟域逻辑
    //=========================================================================

    // 读指针计算
    assign rd_ptr_bin_next  = rd_ptr_bin + rd_valid;
    assign rd_ptr_gray_next = bin2gray(rd_ptr_bin_next);

    // 读指针寄存器
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            rd_ptr_bin  <= {PTR_WIDTH{1'b0}};
            rd_ptr_gray <= {PTR_WIDTH{1'b0}};
        end else begin
            rd_ptr_bin  <= rd_ptr_bin_next;
            rd_ptr_gray <= rd_ptr_gray_next;
        end
    end

    // 读数据寄存器输出(只有rd_en有效时才更新输出)
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            rd_data <= {DATA_WIDTH{1'b0}};
        end else if (rd_valid) begin
            rd_data <= fifo_mem[rd_ptr_bin[ADDR_WIDTH-1:0]];
        end
    end

    // 写指针同步到读时钟域(两级同步)
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            wr_ptr_gray_sync1 <= {PTR_WIDTH{1'b0}};
            wr_ptr_gray_sync2 <= {PTR_WIDTH{1'b0}};
        end else begin
            wr_ptr_gray_sync1 <= wr_ptr_gray;
            wr_ptr_gray_sync2 <= wr_ptr_gray_sync1;
        end
    end

    // 空标志生成(在读时钟域判断)
    assign empty = (rd_ptr_gray == wr_ptr_gray_sync2);

endmodule

总结:ai需要与用户多次交互,反复修改,才能实现需要的功能。并不是想像中的一步就能提供正确无误的结果。比起用户从零开始写,还是能节省很多时间。如果用户能够细化设计需求,提供每一段逻辑的实现方式,那ai应该可以做得更好更快。

感谢阅读,更多文章点击这里:【专栏:ExASIC】
最新20篇 开设专栏