标准FIFO
异步FIFO需要使用格雷码同步读写信号,可参考牛客异步FIFO例题,不同于牛客例题之处在于FIFO读空信号置位与FIFO最后一个有效数据输出在同一个周期,与Xilinx FIFO一致。核心代码如下:
logic [FIFO_DEPTH_WIDTH : 0] rd_ptr_r = 'd0, wr_ptr_r = 'd0;
logic [FIFO_DEPTH_WIDTH : 0] rd_ptr_gray_r = 'd0, wr_ptr_gray_r = 'd0;
logic [FIFO_DEPTH_WIDTH : 0] rd_ptr_gray_cdc_r = 'd0, wr_ptr_gray_cdc_r = 'd0;
logic [FIFO_DEPTH_WIDTH : 0] rd_ptr_gray_scdc_r = 'd0, wr_ptr_gray_scdc_r = 'd0;
always_ff @(posedge m_clk) begin
if (wr_en & ~wfull) begin
wr_ptr_r <= wr_ptr_r + 1;
end
end
always_ff @(posedge s_clk) begin
if (rd_en & ~rempty) begin
rd_ptr_r <= rd_ptr_r + 1;
end
end
always_ff @(posedge s_clk) begin
rd_ptr_gray_r[FIFO_DEPTH_WIDTH] <= rd_ptr_r[FIFO_DEPTH_WIDTH];
for (integer i = 0; i < FIFO_DEPTH_WIDTH; i++) begin
rd_ptr_gray_r[i] <= rd_ptr_r[i + 1] ^ rd_ptr_r[i];
end
end
always_ff @(posedge m_clk) begin
rd_ptr_gray_cdc_r <= rd_ptr_gray_scdc_r;
rd_ptr_gray_scdc_r <= rd_ptr_gray_r;
end
always_ff @(posedge m_clk) begin
wr_ptr_gray_r[FIFO_DEPTH_WIDTH] <= wr_ptr_r[FIFO_DEPTH_WIDTH];
for (integer i = 0; i < FIFO_DEPTH_WIDTH; i++) begin
wr_ptr_gray_r[i] <= wr_ptr_r[i + 1] ^ wr_ptr_r[i];
end
end
always_ff @(posedge s_clk) begin
wr_ptr_gray_cdc_r <= wr_ptr_gray_scdc_r;
wr_ptr_gray_scdc_r <= wr_ptr_gray_r;
end
logic [FIFO_DEPTH_WIDTH : 0] rd_ptr_m_clk, wr_ptr_s_clk;
assign wfull = (wr_ptr_r[FIFO_DEPTH_WIDTH] != rd_ptr_m_clk[FIFO_DEPTH_WIDTH]) && (wr_ptr_r[FIFO_DEPTH_WIDTH - 1 : 0] == rd_ptr_m_clk[FIFO_DEPTH_WIDTH - 1 : 0]);
assign rempty = rd_ptr_r == wr_ptr_s_clk;
always_comb begin
wr_ptr_s_clk[FIFO_DEPTH_WIDTH] = wr_ptr_gray_cdc_r[FIFO_DEPTH_WIDTH];
for (integer i = FIFO_DEPTH_WIDTH - 1; i >= 0; i--) begin
wr_ptr_s_clk[i] = wr_ptr_s_clk[i + 1] ^ wr_ptr_gray_cdc_r[i];
end
end
always_comb begin
rd_ptr_m_clk[FIFO_DEPTH_WIDTH] = rd_ptr_gray_cdc_r[FIFO_DEPTH_WIDTH];
for (integer i = FIFO_DEPTH_WIDTH - 1; i >= 0; i--) begin
rd_ptr_m_clk[i] = rd_ptr_m_clk[i + 1] ^ rd_ptr_gray_cdc_r[i];
end
end
logic [TDATA_WIDTH - 1 : 0] ram[FIFO_DEPTH - 1 : 0];
always_ff @(posedge m_clk) begin
if (wr_en & ~wfull) begin
ram[wr_ptr_r[FIFO_DEPTH_WIDTH - 1 : 0]] <= m_axis_tdata;
end
end
always_ff @(posedge s_clk) begin
if (rd_en & ~rempty) begin
s_axis_tdata <= ram[rd_ptr_r[FIFO_DEPTH_WIDTH - 1 : 0]];
end
end
FWFT FIFO简单实现
基于标准异步FIFO,引入FWFT机制。一种简单做法即将ram输出改为组合逻辑输出,这种方式实现的FWFT延时最低。
always_comb begin
// if (rd_en & ~rempty) begin
s_axis_tdata = ram[rd_ptr_r[FIFO_DEPTH_WIDTH - 1 : 0]];
// end
end
FWFT FIFO优化
然而,在FPGA中所有存储资源如(BRAM、URAM)均具有一个周期的读延时,在构造大容量fifo时,为了让综合工具将RAM综合为BRAM / URAM,更合适的做法为添加寄存器slice,这种方式会增加1个周期的延迟:
initial rempty = 1'b1;
assign wfull = sfifo_wfull;
always @(posedge s_clk) begin
if (~sfifo_rempty & (rempty | rd_en)) begin
s_axis_tdata <= ram[rd_ptr_r[FIFO_DEPTH_WIDTH - 1 : 0]];
end
end
assign sfifo_rd_en = (rempty | rd_en);
always @(posedge s_clk) begin
if (~sfifo_rempty) begin
rempty <= 1'b0;
end else if (rd_en) begin
rempty <= 1'b1;
end
end
完整代码
完整工程代码可公众号回复ASYNC_FIFOs下载。