一个简单的RTL同步FIFO设计

FIFO 是FPGA设计中最有用的模块之一。FIFO 在模块之间提供简单的握手和同步机制,是设计人员将数据从一个模块传输到另一个模块的常用选择。

在这篇文章中,展示了一个简单的 RTL 同步 FIFO,可以直接在自己的设计中配置和使用它,该设计是完全可综合的。

为什么要自己设计FIFO

那么,为什么呢?网上有很多关于 FIFO 的 Verilog/VHDL 代码的资源,过去,我自己也使用过其中的一些。但令人沮丧的是,它们中的大多数都存在问题,尤其是在上溢出和下溢出条件下。所以想一劳永逸地解决这些问题。

FIFO 规格性能

同步,单时钟。

基于寄存器的 FIFO,适用于中小型 FIFO。

Full、Empty、Almost-full、Almost-empty 标志。

完全可配置的数据宽度、深度和标志。

完全可综合的系统 Verilog 代码。

/*===============================================================================================================================

Design : Single-clock Synchronous FIFO

Description : Fully synthesisable, configurable Single-clock Synchronous FIFO based on registers.

– Configurable Data width.

– Configurable Depth.

– Configurable Almost-full and Almost-empty signals.

===============================================================================================================================*/

module my_fifo #(

parameter DATA_W = 4 , // Data width

parameter DEPTH = 8 , // Depth of FIFO

parameter UPP_TH = 4 , // Upper threshold to generate Almost-full

parameter LOW_TH = 2 // Lower threshold to generate Almost-empty

)

(

input clk , // Clock

input rstn , // Active-low Synchronous Reset

input i_wren , // Write Enable

input [DATA_W – 1 : 0] i_wrdata , // Write-data

output o_alm_full , // Almost-full signal

output o_full , // Full signal

input i_rden , // Read Enable

output [DATA_W – 1 : 0] o_rddata , // Read-data

output o_alm_empty , // Almost-empty signal

output o_empty // Empty signal

);

/*——————————————————————————————————————————-

Internal Registers/Signals

——————————————————————————————————————————-*/

logic [DATA_W – 1 : 0] data_rg [DEPTH] ; // Data array

logic [$clog2(DEPTH) – 1 : 0] wrptr_rg ; // Write pointer

logic [$clog2(DEPTH) – 1 : 0] rdptr_rg ; // Read pointer

logic [$clog2(DEPTH) : 0] dcount_rg ; // Data counter

logic wren_s ; // Write Enable signal generated iff FIFO is not full

logic rden_s ; // Read Enable signal generated iff FIFO is not empty

logic full_s ; // Full signal

logic empty_s ; // Empty signal

/*——————————————————————————————————————————-

Synchronous logic to write to and read from FIFO

——————————————————————————————————————————-*/

always @ (posedge clk) begin

if (!rstn) begin

data_rg <= ‘{default: ‘0} ;

wrptr_rg <= 0 ;

rdptr_rg <= 0 ;

dcount_rg <= 0 ;

end

else begin

ready_rg <= 1’b1 ;

/* FIFO write logic */

if (wren_s) begin

data_rg [wrptr_rg] <= i_wrdata ; // Data written to FIFO

if (wrptr_rg == DEPTH – 1) begin

wrptr_rg <= 0 ; // Reset write pointer

end

else begin

wrptr_rg <= wrptr_rg + 1 ; // Increment write pointer

end

end

/* FIFO read logic */

if (rden_s) begin

if (rdptr_rg == DEPTH – 1) begin

rdptr_rg <= 0 ; // Reset read pointer

end

else begin

rdptr_rg <= rdptr_rg + 1 ; // Increment read pointer

end

end

/* FIFO data counter update logic */

if (wren_s && !rden_s) begin // Write operation

dcount_rg <= dcount_rg + 1 ;

end

else if (!wren_s && rden_s) begin // Read operation

dcount_rg <= dcount_rg – 1 ;

end

end

end

/*——————————————————————————————————————————-

Continuous Assignments

——————————————————————————————————————————-*/

// Full and Empty internal

assign full_s = (dcount_rg == DEPTH) ? 1’b1 : 0 ;

assign empty_s = (dcount_rg == 0 ) ? 1’b1 : 0 ;

// Write and Read Enables internal

assign wren_s = i_wren & !full_s ;

assign rden_s = i_rden & !empty_s ;

// Full and Empty to output

assign o_full = full_s ;

assign o_empty = empty_s ;

// Almost-full and Almost Empty to output

assign o_alm_full = (dcount_rg > UPP_TH) ? 1’b1 : 0 ;

assign o_alm_empty = (dcount_rg < LOW_TH) ? 1’b1 : 0 ;

// Read-data to output

assign o_rddata = data_rg [rdptr_rg] ;

endmodule

/*=============================================================================================================================*/

基于 RAM 的 FIFO

在上面的步骤中,我们看到了一个基于寄存器的同步FIFO。接下来,我们来看看基于 RAM 的 FIFO。该 FIFO 在 RAM 而不是寄存器上实现其数据阵列。这适用于在硬件上实现大型 FIFO ;特别是在 FPGA 上,FPGA 里有大量的Block RAM 可用。这将降低资源利用率,也可以获得更好的时序性能。

审核编辑:刘清

发表评论