FWFT FIFO
打个小广告: 艾伦耶格尔的数字小店
1. FWFT FIFO介绍
1.1 FIFO 基础回顾
在深入 FWFT FIFO 之前,先回顾 FIFO 的基本概念:
- FIFO(First-In-First-Out):一种按写入顺序读出数据的缓冲区。
- 核心应用场景:
- 解决数据生产者和消费者的速度不匹配
- 跨时钟域通信
- 数据流缓冲
1.2 FWFT FIFO 的独特特性
FWFT(First-Word Fall-Through) 是一种特殊的 FIFO 工作模式,其核心特点
- 数据提前暴露:FIFO 非空时,第一个有效数据自动出现在输出端口(
dout
)。 - 读操作简化:
rd_en
信号作用变为"确认消费当前数据"- 读操作后下一个数据(如果有)立即出现在
dout
- 低延迟优势:省去标准 FIFO 中
rd_en
到dout
的延迟周期
1.3 FWFT FIFO vs 标准 FIFO
行为 | 标准 FIFO | FWFT FIFO |
---|---|---|
初始状态 | dout 无效(例如空) | 若 FIFO 非空,dout 直接有效 |
读操作触发 | rd_en 拉高后下一周期输出 | rd_en 拉高后立即更新数据 |
空标志行为 | 最后一个数据读出后生效 | 最后一个数据读出时生效 |
2. 实现原理
这里我们使用寄存器代替存储器,主要是因为FIFO在不需要缓冲非常大的容量时可以使用寄存器作为载体。若需要存储器,只需添加地址转换逻辑。
3. FWFT FIFO 时序示例
假设已写入数据 A, B, C
:
时钟周期 | 操作 | dout | empty | 说明 |
---|---|---|---|---|
T1 | 无操作 | A | 0 | 数据 A 自动暴露 |
T2 | rd_en=1 | B | 0 | 读 A,B 立即出现 |
T3 | rd_en=1 | C | 0 | 读 B,C 立即出现 |
T4 | rd_en=1 | 无效 | 1 | 读 C,FIFO 变空 |
4. 设计考量
- 空标志逻辑:最后一个数据被读出时立即生效
- 接口设计:
dout
始终有效(除非 FIFO 为空)- 下游模块需根据
empty
判断数据有效性
- 资源优化:现代 FPGA Block RAM 原生支持 FWFT 模式
5.RTL 实现
/**********************************************************************
File: fwft_fifo.v
Author:艾伦耶格尔【咸鱼】
Purpose:
An small First Word Fall Through FIFO. The code will use LUTs
and optimized for low LUTs utilization.
Salted Fish Link: 【闲鱼】https://m.tb.cn/h.6dWD3XF?tk=PNtBeuDxgv4 CZ356点击链接直接打开
*******************************************************************/
`include "../define.v"
`timescale 1ns/1ps
module fwft_fifo #(
parameter WIDTH = 1,
parameter MAX_DEPTH_BITS = 2
)
(
input [WIDTH-1:0] din, // Data in
input wr_en, // Write enable
input rd_en, // Read the next word
output reg [WIDTH-1:0] dout, // Data out
output full,
output nearly_full,
output recieve_more_than_0,
output recieve_more_than_1,
input reset,
input clk
);
`LOG2
localparam DEPTH_WIDTH = log2(MAX_DEPTH_BITS +1);
wire [MAX_DEPTH_BITS-2 : 0] mux_in [WIDTH-1 :0];
wire [DEPTH_WIDTH-1 : 0] mux_sel;
wire [WIDTH-1 : 0] mux_out;
wire empty;
reg [MAX_DEPTH_BITS-2 : 0] shiftreg [WIDTH-1 :0];
reg [DEPTH_WIDTH-1 : 0] depth;
wire out_sel ;
wire out_ld ;
wire [WIDTH-1 : 0] dout_next;
genvar i;
generate
for(i=0;i<WIDTH; i=i+1) begin : lp
if(MAX_DEPTH_BITS>2) begin
always @(posedge clk ) begin
//if (reset) begin
// shiftreg[i] <= {MAX_DEPTH_BITS{1'b0}};
//end else begin
if(wr_en) shiftreg[i] <= {shiftreg[i][MAX_DEPTH_BITS-3 : 0] ,din[i]};
//end
end
end else begin
always @(posedge clk ) begin
//if (reset) begin
// shiftreg[i] <= {MAX_DEPTH_BITS{1'b0}};
//end else begin
if(wr_en) shiftreg[i] <= din[i];
//end
end //always
end //else
assign mux_in[i] = shiftreg[i];
assign mux_out[i] = mux_in[i][mux_sel];
assign dout_next[i] = (out_sel) ? mux_out[i] : din[i];
end //for
endgenerate
always @(posedge clk) begin
if (reset) begin
depth <= {DEPTH_WIDTH{1'b0}};
end else begin
if (wr_en & ~rd_en) depth <=
// synthesis translate_off
#1
// synthesis translate_on
depth + 1'h1;
else if (~wr_en & rd_en) depth <=
// synthesis translate_off
#1
// synthesis translate_on
depth - 1'h1;
end
end//always
always @(posedge clk or posedge reset) begin
if (reset) begin
dout <= {WIDTH{1'b0}};
end else begin
if (out_ld) dout <= dout_next;
end
end//always
assign full = depth == MAX_DEPTH_BITS;
assign nearly_full = depth >= MAX_DEPTH_BITS-1;
assign empty = depth == 'h0;
assign recieve_more_than_0 = ~ empty;
assign recieve_more_than_1 = ~( depth == 0 || depth== 1 );
assign out_sel = (recieve_more_than_1) ? 1'b1 : 1'b0;
assign out_ld = (depth !=0 )? rd_en : wr_en;
assign mux_sel = depth-2'd2;
// synthesis translate_off
always @(posedge clk)
begin
if (wr_en && full) begin
$display("%t ERROR: Attempt to write to full FIFO: %m", $time);
end
if (rd_en && !recieve_more_than_0) begin
$display("%t ERROR: Attempt to read an empty FIFO: %m", $time);
end
end // always @ (posedge clk)
// synthesis translate_on
endmodule