这是之前的实验《给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应该可以做得更好更快。
