实现异步FIFO缓冲器,支持双时钟域读写,使用Gray码安全传递指针。
异步FIFO架构:
写时钟域 读时钟域
┌──────────┐ ┌──────────┐
│ wr_ptr │ │ rd_ptr │
│ wr_en │ │ rd_en │
│ wr_data──┼──→ MEM ──→───┼──rd_data │
│ full │ [DEPTH-1:0]│ empty │
└────┬─────┘ └────┬─────┘
│ Gray码+2级同步 │ Gray码+2级同步
│ wr_ptr_gray ──→ sync │
│ rd_ptr_gray ──→ sync
↓ ↓
判满: wr_gray == ~rd_gray[MSB:MSB-1] + rd_gray[rest]
判空: rd_gray == wr_gray_sync// Asynchronous FIFO Buffer — 异步FIFO缓冲
// Dual-clock domain, pointer-based design with gray code crossing
module fifo_async #(
parameter DATA_W = 8,
parameter DEPTH = 16
)(
input wire wr_clk,
input wire wr_rst,
input wire wr_en,
input wire [DATA_W-1:0] wr_data,
output wire full,
input wire rd_clk,
input wire rd_rst,
input wire rd_en,
output wire [DATA_W-1:0] rd_data,
output wire empty
);
localparam ADDR_W = $clog2(DEPTH);
// Memory array
reg [DATA_W-1:0] mem [0:DEPTH-1];
// Write side pointers
reg [ADDR_W:0] wr_ptr;
wire [ADDR_W-1:0] wr_addr = wr_ptr[ADDR_W-1:0];
// Read side pointers
reg [ADDR_W:0] rd_ptr;
wire [ADDR_W-1:0] rd_addr = rd_ptr[ADDR_W-1:0];
// Gray code conversion
wire [ADDR_W:0] wr_ptr_gray = wr_ptr ^ (wr_ptr >> 1);
wire [ADDR_W:0] rd_ptr_gray = rd_ptr ^ (rd_ptr >> 1);
// Synchronized gray pointers (2-stage synchronizer)
reg [ADDR_W:0] rd_ptr_gray_sync1, rd_ptr_gray_sync2;
reg [ADDR_W:0] wr_ptr_gray_sync1, wr_ptr_gray_sync2;
// Gray to binary conversion
function automatic [ADDR_W:0] gray2bin;
input [ADDR_W:0] gray;
integer i;
begin
gray2bin[ADDR_W] = gray[ADDR_W];
for (i = ADDR_W-1; i >= 0; i = i - 1)
gray2bin[i] = gray2bin[i+1] ^ gray[i];
end
endfunction
wire [ADDR_W:0] rd_ptr_wr_side = gray2bin(rd_ptr_gray_sync2);
wire [ADDR_W:0] wr_ptr_rd_side = gray2bin(wr_ptr_gray_sync2);
// Full/Empty flags
assign full = (wr_ptr_gray == {~rd_ptr_gray_sync2[ADDR_W:ADDR_W-1], rd_ptr_gray_sync2[ADDR_W-2:0]});
assign empty = (rd_ptr_gray == wr_ptr_gray_sync2);
// Write logic
always @(posedge wr_clk or posedge wr_rst) begin
if (wr_rst) begin
wr_ptr <= 0;
rd_ptr_gray_sync1 <= 0;
rd_ptr_gray_sync2 <= 0;
end else begin
// Synchronize read pointer into write clock domain
rd_ptr_gray_sync1 <= rd_ptr_gray;
rd_ptr_gray_sync2 <= rd_ptr_gray_sync1;
if (wr_en && !full) begin
mem[wr_addr] <= wr_data;
wr_ptr <= wr_ptr + 1;
end
end
end
// Read logic
always @(posedge rd_clk or posedge rd_rst) begin
if (rd_rst) begin
rd_ptr <= 0;
wr_ptr_gray_sync1 <= 0;
wr_ptr_gray_sync2 <= 0;
end else begin
// Synchronize write pointer into read clock domain
wr_ptr_gray_sync1 <= wr_ptr_gray;
wr_ptr_gray_sync2 <= wr_ptr_gray_sync1;
if (rd_en && !empty) begin
rd_ptr <= rd_ptr + 1;
end
end
end
// Read data output (combinational)
assign rd_data = mem[rd_addr];
endmodule
测试:写入8个数据(1-8),读出验证顺序正确。写满FIFO验证full标志,读空验证empty标志。使用异步读写时钟(50MHz写/33MHz读)验证跨时钟域稳定性。
本实验所有Verilog代码已通过Verilator编译验证,功能行为正确。测试用例覆盖核心功能路径,确保设计满足规格要求。
异步FIFO是SoC中跨时钟域通信的基石。DDR控制器、PCIe、USB、以太网MAC都依赖FIFO缓冲。ARM AXI总线用异步FIFO连接不同频率的Master和Slave。Cliff Cummings的异步FIFO设计论文是数字设计必读文献。
尝试修改代码中的关键参数,观察仿真结果变化:
💡 Verilator会在位宽不匹配时给出Warning,这是学习的好机会
在现有基础上增加新功能:
🔧 增量开发:每次只改一个地方,验证通过后再改下一个
| 语法 | 说明 | 示例 |
|---|---|---|
reg [7:0] | 8位寄存器 | reg [7:0] data; |
wire | 组合逻辑连线 | wire valid = cnt > 5; |
always @(posedge clk) | 时序逻辑 | 上升沿触发 |
always @(*) | 组合逻辑 | 敏感列表自动推导 |
localparam | 局部常量 | localparam DIV = 50000000; |
case | 多分支选择 | 注意default分支 |
$clog2 | 计算位宽 | $clog2(16)=4 |
本设计在典型FPGA上的资源占用估算:LUT约20-150个,FF约30-120个,无BRAM/DSP依赖(特殊模块除外)。时钟频率可达50-100MHz+。Verilator仿真速度约5-10M周期/秒。