| 参数 | 值 | 说明 |
|---|---|---|
| 数据宽度 | 参数化 DATA_WIDTH | 默认8位 |
| 地址宽度 | 参数化 ADDR_WIDTH | 默认4位,深度16 |
| 写时钟 | wr_clk | 独立写时钟 |
| 读时钟 | rd_clk | 独立读时钟 |
| 复位 | 低有效异步复位 | 两域分别同步释放 |
| 满标志 | 组合逻辑输出 | 写域生成 |
| 空标志 | 组合逻辑输出 | 读域生成 |
| 同步器 | 2级FF | 格雷码指针跨域 |
// async_fifo.v
// 异步FIFO完整实现
// 双时钟域,格雷码指针,两级同步器
// 深度必须为2的幂
module async_fifo #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4
)(
// 写接口(wr_clk域)
input wire wr_clk,
input wire wr_rst_n,
input wire [DATA_WIDTH-1:0] wr_data,
input wire wr_en,
output wire full,
// 读接口(rd_clk域)
input wire rd_clk,
input wire rd_rst_n,
output wire [DATA_WIDTH-1:0] rd_data,
input wire rd_en,
output wire empty
);
// ===================== 内部信号 =====================
localparam PTR_WIDTH = ADDR_WIDTH + 1;
// 写域指针
reg [PTR_WIDTH-1:0] wr_ptr_bin;
wire [PTR_WIDTH-1:0] wr_ptr_gray;
wire [PTR_WIDTH-1:0] rd_ptr_gray_sync; // 读指针同步到写域
// 读域指针
reg [PTR_WIDTH-1:0] rd_ptr_bin;
wire [PTR_WIDTH-1:0] rd_ptr_gray;
wire [PTR_WIDTH-1:0] wr_ptr_gray_sync; // 写指针同步到读域
// RAM
reg [DATA_WIDTH-1:0] mem [0:(1<> 1);
// ===================== 读指针 =====================
always @(posedge rd_clk or negedge rd_rst_n) begin
if (!rd_rst_n)
rd_ptr_bin <= {PTR_WIDTH{1'b0}};
else if (rd_en && !empty)
rd_ptr_bin <= rd_ptr_bin + 1'b1;
end
assign rd_ptr_gray = rd_ptr_bin ^ (rd_ptr_bin >> 1);
// ===================== RAM写入 =====================
always @(posedge wr_clk) begin
if (wr_en && !full)
mem[wr_ptr_bin[ADDR_WIDTH-1:0]] <= wr_data;
end
// ===================== RAM读取 =====================
assign rd_data = mem[rd_ptr_bin[ADDR_WIDTH-1:0]];
// ===================== 同步器:读指针→写域 =====================
reg [PTR_WIDTH-1:0] rd_ptr_gray_d1, rd_ptr_gray_d2;
always @(posedge wr_clk or negedge wr_rst_n) begin
if (!wr_rst_n) begin
rd_ptr_gray_d1 <= {PTR_WIDTH{1'b0}};
rd_ptr_gray_d2 <= {PTR_WIDTH{1'b0}};
end else begin
rd_ptr_gray_d1 <= rd_ptr_gray;
rd_ptr_gray_d2 <= rd_ptr_gray_d1;
end
end
assign rd_ptr_gray_sync = rd_ptr_gray_d2;
// ===================== 同步器:写指针→读域 =====================
reg [PTR_WIDTH-1:0] wr_ptr_gray_d1, wr_ptr_gray_d2;
always @(posedge rd_clk or negedge rd_rst_n) begin
if (!rd_rst_n) begin
wr_ptr_gray_d1 <= {PTR_WIDTH{1'b0}};
wr_ptr_gray_d2 <= {PTR_WIDTH{1'b0}};
end else begin
wr_ptr_gray_d1 <= wr_ptr_gray;
wr_ptr_gray_d2 <= wr_ptr_gray_d1;
end
end
assign wr_ptr_gray_sync = wr_ptr_gray_d2;
// ===================== 满标志(写域)=====================
assign full = (wr_ptr_gray == {~rd_ptr_gray_sync[PTR_WIDTH-1:PTR_WIDTH-2],
rd_ptr_gray_sync[PTR_WIDTH-3:0]});
// ===================== 空标志(读域)=====================
assign empty = (wr_ptr_gray_sync == rd_ptr_gray);
endmodule
写路径关键时序:
读路径关键时序:
最坏情况场景:写端连续写入直到满,然后读端读取。读端需要2个rd_clk周期才能看到写指针的变化,空标志才能解除断言。这期间读端无法读出数据——这就是同步延迟的性能代价。
// async_fifo_prog.v
// 带可编程满空标志的异步FIFO
// 在基础异步FIFO上增加可编程阈值
module async_fifo_prog #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4,
parameter PROG_FULL_THRESH = 12,
parameter PROG_EMPTY_THRESH = 4
)(
input wire wr_clk,
input wire wr_rst_n,
input wire [DATA_WIDTH-1:0] wr_data,
input wire wr_en,
output wire full,
output wire prog_full,
input wire rd_clk,
input wire rd_rst_n,
output wire [DATA_WIDTH-1:0] rd_data,
input wire rd_en,
output wire empty,
output wire prog_empty
);
localparam PTR_WIDTH = ADDR_WIDTH + 1;
// 基础异步FIFO实例
wire [PTR_WIDTH-1:0] wr_ptr_bin, rd_ptr_bin;
wire [PTR_WIDTH-1:0] wr_ptr_gray, rd_ptr_gray;
wire [PTR_WIDTH-1:0] rd_ptr_gray_sync, wr_ptr_gray_sync;
reg [PTR_WIDTH-1:0] wr_ptr_bin_r, rd_ptr_bin_r;
reg [DATA_WIDTH-1:0] mem [0:(1<> 1);
// 读逻辑
always @(posedge rd_clk or negedge rd_rst_n) begin
if (!rd_rst_n)
rd_ptr_bin_r <= 0;
else if (rd_en && !empty)
rd_ptr_bin_r <= rd_ptr_bin_r + 1;
end
assign rd_ptr_bin = rd_ptr_bin_r;
assign rd_ptr_gray = rd_ptr_bin ^ (rd_ptr_bin >> 1);
assign rd_data = mem[rd_ptr_bin_r[ADDR_WIDTH-1:0]];
// 同步器
reg [PTR_WIDTH-1:0] rd_gray_d1, rd_gray_d2;
always @(posedge wr_clk or negedge wr_rst_n) begin
if (!wr_rst_n) begin
rd_gray_d1 <= 0; rd_gray_d2 <= 0;
end else begin
rd_gray_d1 <= rd_ptr_gray; rd_gray_d2 <= rd_gray_d1;
end
end
assign rd_ptr_gray_sync = rd_gray_d2;
reg [PTR_WIDTH-1:0] wr_gray_d1, wr_gray_d2;
always @(posedge rd_clk or negedge rd_rst_n) begin
if (!rd_rst_n) begin
wr_gray_d1 <= 0; wr_gray_d2 <= 0;
end else begin
wr_gray_d1 <= wr_ptr_gray; wr_gray_d2 <= wr_gray_d1;
end
end
assign wr_ptr_gray_sync = wr_gray_d2;
// 满空标志
assign full = (wr_ptr_gray == {~rd_ptr_gray_sync[PTR_WIDTH-1:PTR_WIDTH-2],
rd_ptr_gray_sync[PTR_WIDTH-3:0]});
assign empty = (wr_ptr_gray_sync == rd_ptr_gray);
// 可编程满空:需要将格雷码转回二进制计算count
// 写域计算count(用于prog_full)
wire [PTR_WIDTH-1:0] rd_bin_sync;
assign rd_bin_sync[PTR_WIDTH-1] = rd_ptr_gray_sync[PTR_WIDTH-1];
genvar i;
generate
for (i = PTR_WIDTH-2; i >= 0; i = i - 1) begin : gen_rd_g2b
assign rd_bin_sync[i] = rd_ptr_gray_sync[i] ^ rd_bin_sync[i+1];
end
endgenerate
wire [PTR_WIDTH-1:0] wr_count;
assign wr_count = wr_ptr_bin - rd_bin_sync;
assign prog_full = (wr_count >= PROG_FULL_THRESH);
// 读域计算count(用于prog_empty)
wire [PTR_WIDTH-1:0] wr_bin_sync;
assign wr_bin_sync[PTR_WIDTH-1] = wr_ptr_gray_sync[PTR_WIDTH-1];
generate
for (i = PTR_WIDTH-2; i >= 0; i = i - 1) begin : gen_wr_g2b
assign wr_bin_sync[i] = wr_ptr_gray_sync[i] ^ wr_bin_sync[i+1];
end
endgenerate
wire [PTR_WIDTH-1:0] rd_count;
assign rd_count = wr_bin_sync - rd_ptr_bin;
assign prog_empty = (rd_count <= PROG_EMPTY_THRESH);
endmodule
1. RAM实现选择
2. 同步器优化
3. 满空标志优化
4. 读数据优化
在ASIC/FPGA实现流程中,异步FIFO的跨域路径需要特殊处理:
# 设置跨域路径为虚假路径(不需要时序分析)
set_false_path -from [get_clocks wr_clk] -to [get_clocks rd_clk]
set_false_path -from [get_clocks rd_clk] -to [get_clocks wr_clk]
# 或者使用时钟组约束(更严格)
set_clock_groups -asynchronous \
-group [get_clocks wr_clk] \
-group [get_clocks rd_clk]
# 同步器内部路径:允许最大延迟
set_max_delay 2.0 -from [get_pins rd_ptr_gray_reg/Q] \
-to [get_pins rd_ptr_gray_d1_reg/D]
# 标记同步器FF,防止工具优化
set_property ASYNC_REG TRUE [get_cells rd_ptr_gray_d1_reg]
set_property ASYNC_REG TRUE [get_cells rd_ptr_gray_d2_reg]
为什么需要false_path:跨域路径由同步器处理,不需要满足传统时序约束。如果不设置false_path,工具会报告跨域路径的时序违例。
ASYNC_REG属性:告诉综合工具这些FF用于跨域同步,不要优化或移动它们。在Xilinx FPGA中尤其重要,确保同步器FF在同一个CLB内。
1. 编程题:为async_fifo添加FWFT(First-Word Fall-Through)支持。提示:在读域添加输出寄存器和预取逻辑。
2. 验证题:编写测试台,在wr_clk=100MHz, rd_clk=33MHz下测试异步FIFO,连续写入1000个数据然后读出验证。
3. 分析题:计算在wr_clk=200MHz, rd_clk=150MHz, ADDR_WIDTH=4的情况下,由于同步延迟导致的最大吞吐率损失。
4. 设计题:实现一个带错误注入的异步FIFO测试台:随机在同步器中注入1周期延迟,验证FIFO的保守性保证。
5. 思考题:在ASIC流程中,如何对异步FIFO的跨域路径做时序约束?SDC中应该使用什么命令?
🎯 实现了完整的异步FIFO
📍 里程碑:掌握了异步FIFO的全部设计细节
💡 下一步:异步FIFO验证