☰
数字IC前端学习笔记:FIFO的Verilog实现(二)
2026/10/27 1:12:43 网站建设 项目流程

相关文章

数字IC前端专栏https://blog.csdn.net/weixin_45791458/category_12173698.html?spm=1001.2014.3001.5482


四、异步FIFO原理

在之前,我们讨论了了同步FIFO,因为其具有单一的时钟,因此应用范围有限。在实际的引用中,我们经常遇到多个时钟域的情况,此时数据需要在两个时钟域之间实现传送,并且不能出现毛刺和亚稳态。我们以PCIe插槽上的以太网适配器板卡为例加以说明。该板卡从局域网(LAN)或以太网接收数据包,然后将数据传送给系统存储器。反过来,它从系统存储器接受数据包,然后传送给网络。板卡的一侧和网络通信,用以太网本地处理相关操作。板卡的另一侧与PCEe接口交互,以板卡自身的时钟工作。这两个时钟不仅频率不同,而且是异步的(频率不是倍数关系,如果石是倍数关系,可以认为这是同步时钟,因为相位差恒定)。此时需要使用异步FIFO将数据从一个时钟域传送到另一个时钟域。

尽管异步FIFO的操作原理与同步FIFO类似,但由于前者与两个时钟有关,电路的复杂度也会增加。对异步FIFO进行数据写入和读出操作的方式与同步FIFO极其相似,写入与读出操作也有自己的信号集,其复杂度主要体现于产生FIFO_full、FIFO_empty、room_avaliable、data_avaliable等标识。异步FIFO中产生这些标识的方法比同步FIFO中要复杂得多。

在这里我们不能使用计数器根据读取或存储信号保存FIFO中的数据量,因为他们是两个时钟域的信号,不能用两个时钟对同一个reg赋值。我们知道,当FIFO为满或空时,写入指针和读取指针是相等的。但这是不够的,我们需要另外的条件将“空”和“满”进行区分。前面介绍过,FIFO工作时,写入指针在前,读出指针跟随写入指针。当FIFO为满时,写入指针的状态是到达顶部后返回底部最后和读取指针相同,即超越读取指针一轮(FIFO深度),如果我们在指针最高位增加一个辅助位给写入和读取指针,则可以用来指示当这两个指针相同(不包括辅助位)时,是没有超越的相同,还是超越一轮的相同。

我们以一个深度为4的FIFO为例说明。此时,计数器需要两位表示,计数序列为00→01

10→11→00,在最高位增加一位辅助位后,计数序列为000→001→010→011→100→101→110

→111。正如我们所看到的,除了最高位(辅助位),其他位将循环两次,通过比较辅助位,我们就可以判断FIFO状态是满还是空。

虽然解决了空满状态的标识,但仍然存在问题,即读指针和写指针分别产生于各自时钟域,不能互相比较,否则会因时序问题产生亚稳态。解决这个问题的办法是将指针从一个时钟域传递到另一个时钟域,然后做比较。而且我们要注意,传递的是多位信号而不是一位信号,此时需要使用格雷码编码和译码电路将指针进行跨时钟域安全传送。在格雷码编码方案中,相邻编码中只有一位发生变化,该特性被用于跨时钟域矢量传递(还有其他方法,可见数字IC前端学习笔记:跨时钟域信号同步一文)。我们将所有这些零散的知识放在一个图中以便于浏览和理解具体的操作方法,如下图所示。

写指针被转换为格雷码编码,并经过触发器保存,然后经过两级触发器同步到读时钟域并解码为二进制编码,最后与读指针比教产生fifo_empty信号。读指针的行为类似,在写时钟域产生fifo_full信号。

需要注意的是,当读指针被传送到写入时钟域时,相对于读时钟域的读指针来说,可能会存在3到4个周期的延时,也就是说,可能出现读指针已经增加,而在写时钟域看来并未增加,这会导致fifo在仍有空位时给出fifo_full信号,这是异步FIFO保守的一面,如此可以确保不产生数据上溢。写指针也有类似问题,会出现FIFO仍有数据而FIFO给出fifo_empty信号的情况。这些不会影响FIFO的正确操作,在停止写入和读出后几个周期时,就可以给出正确的fifo_emty和fifo_full信号。

五、异步FIFO的Verilog实现

module asynch_fifo #(parameter FIFO_PTR=4, FIFO_WIDTH=32) (wrclk, rstb_wrclk, write_en, write_data, snapshot_wrptr, rollback_wrptr, reset_wrptr, rdclk, rstb_rdclk, read_en, read_data, snapshot_rdptr, rollback_rdptr, reset_rdptr, fifo_full, fifo_empty, room_avail, data_avail); input wrclk; input rstb_wrclk; input write_en; input [FIFO_WIDTH-1:0] write_data; input snapshot_wrptr; //记录写指针快照 input rollback_wrptr; //恢复写指针快照 input reset_wrptr; //写指针重置为0 input rdclk; input rstb_rdclk; input read_en; output [FIFO_WIDTH-1:0] read_data; input snapshot_rdptr; //记录读指针快照 input rollback_rdptr; //恢复读指针快照 input reset_rdptr; //读指针重置为0 output reg fifo_full, fifo_empty; output reg [FIFO_PTR:0] room_avail, data_avail; localparam FIFO_DEPTH = 1 << FIFO_PTR; //2^FIFO_PTR localparam FIFO_TWICEDEPTH_MINUS1 = 2*FIFO_DEPTH - 1; reg [FIFO_PTR:0] wr_ptr_wab, wr_ptr_wab_nxt;//有辅助位的写指针 wire [FIFO_PTR:0] room_avail_nxt, data_avail_nxt; reg [FIFO_PTR:0] wr_ptr_snapshot_value; wire [FIFO_PTR:0] wr_ptr_snapshot_value_nxt; wire fifo_full_nxt, fifo_empty_nxt; reg [FIFO_PTR:0] rd_ptr_snapshot_value; wire [FIFO_PTR:0] rd_ptr_snapshot_value_nxt; wire [FIFO_PTR-1:0] wr_ptr,rd_ptr; reg [FIFO_PTR:0] rd_ptr_wab, rd_ptr_wab_nxt;//有辅助位的读指针 //写指针控制逻辑 //********************************************* always@(*) begin wr_ptr_wab_nxt=wr_ptr_wab; if(reset_wrptr) wr_ptr_wab_nxt = 0; else if(rollback_wrptr) wr_ptr_wab_nxt = wr_ptr_snapshot_value; else if(write_en && (wr_ptr_wab == FIFO_TWICEDEPTH_MINUS1)) wr_ptr_wab_nxt = 0; else if(write_en) wr_ptr_wab_nxt = wr_ptr_wab + 1; end //写指针快照 //********************************************* assign wr_ptr_snapshot_value_nxt = snapshot_wrptr ? wr_ptr_wab : wr_ptr_snapshot_value; //寄存器操作 //********************************************* always@(posedge wrclk or negedge rstb_wrclk) begin if(!rstb_wrclk)begin wr_ptr_wab <= 0; wr_ptr_snapshot_value <= 0; end else begin wr_ptr_wab <= wr_ptr_wab_nxt; wr_ptr_snapshot_value <= wr_ptr_snapshot_value_nxt; end end //写指针二进制转格雷码 //********************************************* reg [FIFO_PTR:0] wr_ptr_wab_gray; wire [FIFO_PTR:0] wr_ptr_wab_gray_nxt; //实例化转码模块(在之前的文章中已设计) binary_to_gray #(.PTR(FIFO_PTR)) binary_to_gray_wr (.binary (wr_ptr_wab_nxt), .gray_value (wr_ptr_wab_gray_nxt)); always@(posedge wrclk or negedge rstb_wrclk)begin if(!rstb_wrclk) wr_ptr_wab_gray <= 0; else wr_ptr_wab_gray <= wr_ptr_wab_gray_nxt; end //写指针同步到读时钟域 //********************************************* reg [FIFO_PTR:0] wr_ptr_wab_gray_sync1; reg [FIFO_PTR:0] wr_ptr_wab_gray_sync2; always@(posedge rdclk or negedge rstb_rdclk) begin if(!rstb_rdclk) begin wr_ptr_wab_gray_sync1 <= 0; wr_ptr_wab_gray_sync2 <= 0; end else begin wr_ptr_wab_gray_sync1 <= wr_ptr_wab_gray; wr_ptr_wab_gray_sync2 <= wr_ptr_wab_gray_sync1; end end //格雷码写指针转二进制 //********************************************* reg [FIFO_PTR:0] wr_ptr_wab_rdclk; wire [FIFO_PTR:0] wr_ptr_wab_rdclk_nxt; gray_to_binary #(.PTR(FIFO_PTR)) gray_to_binary_wr (.gray_value(wr_ptr_wab_gray_sync2), .binary(wr_ptr_wab_rdclk_nxt)); always@(posedge rdclk or negedge rstb_rdclk) begin if(!rstb_rdclk) begin wr_ptr_wab_rdclk <= 0; end else begin wr_ptr_wab_rdclk <= wr_ptr_wab_rdclk_nxt; end end //读指针控制逻辑 //********************************************* always@(*) begin rd_ptr_wab_nxt=rd_ptr_wab; if(reset_rdptr) rd_ptr_wab_nxt = 0; else if(rollback_rdptr) rd_ptr_wab_nxt = rd_ptr_snapshot_value; else if(read_en && (rd_ptr_wab == FIFO_TWICEDEPTH_MINUS1)) rd_ptr_wab_nxt = 0; else if(read_en) rd_ptr_wab_nxt = rd_ptr_wab + 1; end //读指针快照 //********************************************* assign rd_ptr_snapshot_value_nxt = snapshot_rdptr ? rd_ptr_wab : rd_ptr_snapshot_value; //寄存器操作 //********************************************* always@(posedge rdclk or negedge rstb_rdclk) begin if(!rstb_rdclk)begin rd_ptr_wab <= 0; rd_ptr_snapshot_value <= 0; end else begin rd_ptr_wab <= rd_ptr_wab_nxt; rd_ptr_snapshot_value <= rd_ptr_snapshot_value_nxt; end end //读指针二进制转格雷码 //********************************************* reg [FIFO_PTR:0] rd_ptr_wab_gray; wire [FIFO_PTR:0] rd_ptr_wab_gray_nxt; //实例化转码模块(在之前的文章中已设计) binary_to_gray #(.PTR(FIFO_PTR)) binary_to_gray_rd (.binary (rd_ptr_wab_nxt), .gray_value (rd_ptr_wab_gray_nxt)); always@(posedge rdclk or negedge rstb_rdclk)begin if(!rstb_rdclk) rd_ptr_wab_gray <= 0; else rd_ptr_wab_gray <= rd_ptr_wab_gray_nxt; end //读指针同步到写时钟域 //********************************************* reg [FIFO_PTR:0] rd_ptr_wab_gray_sync1; reg [FIFO_PTR:0] rd_ptr_wab_gray_sync2; always@(posedge wrclk or negedge rstb_wrclk) begin if(!rstb_wrclk) begin rd_ptr_wab_gray_sync1 <= 0; rd_ptr_wab_gray_sync2 <= 0; end else begin rd_ptr_wab_gray_sync1 <= rd_ptr_wab_gray; rd_ptr_wab_gray_sync2 <= rd_ptr_wab_gray_sync1; end end //格雷码读指针转二进制 //********************************************* reg [FIFO_PTR:0] rd_ptr_wab_wrclk; wire [FIFO_PTR:0] rd_ptr_wab_wrclk_nxt; gray_to_binary #(.PTR(FIFO_PTR)) gray_to_binary_rd (.gray_value(rd_ptr_wab_gray_sync2), .binary(rd_ptr_wab_rdclk_nxt)); always@(posedge wrclk or negedge rstb_wrclk) begin if(!rstb_wrclk) rd_ptr_wab_wrclk <= 0; else rd_ptr_wab_wrclk <= rd_ptr_wab_wrclk_nxt; end assign wr_ptr = wr_ptr_wab[FIFO_PTR-1:0]; assign rd_ptr = rd_ptr_wab[FIFO_PTR-1:0]; //SRAM 存储器实例化 //********************************************* sram #(.FIFO_PTR(FIFO_PTR), .FIFO_WIDTH(FIFO_WIDTH)) sram_0 (.wrclk(wrclk), .wren(write_en), .wrptr(wr_ptr), .wrdata(write_data), .rdclk(rdclk), .rden(read_en), .rdptr(rd_ptr), .rddata(read_data)); //fifo_full和room_avail信号产生 //********************************************* assign fifo_full_nxt = (wr_ptr_wab_nxt[FIFO_PTR] != rd_ptr_wab_wrclk_nxt[FIFO_PTR])&& (wr_ptr_wab_nxt[FIFO_PTR-1:0] == rd_ptr_wab_wrclk_nxt[FIFO_PTR-1:0]); assign room_avail_nxt = (wr_ptr_wab[FIFO_PTR] == rd_ptr_wab_wrclk[FIFO_PTR])? (FIFO_DEPTH-(wr_ptr_wab[FIFO_PTR-1:0]-rd_ptr_wab_wrclk[FIFO_PTR-1:0])): (rd_ptr_wab_wrclk[FIFO_PTR-1:0]-wr_ptr_wab[FIFO_PTR-1:0]); always@(posedge wrclk or negedge rstb_wrclk) begin if(!rstb_wrclk) begin fifo_full <= 0; room_avail <= 0; end else begin fifo_full <= fifo_full_nxt; room_avail <= room_avail_nxt; end end //如果两者没有差一轮,则指针相减代表着FIFO内数据量,用深度减去数据量则为剩余空间,否则直接相减(不包括辅助位)就可得到剩余空间 //fifo_empty和room_empty信号产生 //********************************************* assign fifo_empty_nxt = (rd_ptr_wab_nxt[FIFO_PTR:0] == wr_ptr_wab_rdclk_nxt[FIFO_PTR:0]); assign data_avail_nxt = (rd_ptr_wab[FIFO_PTR] == wr_ptr_wab_rdclk[FIFO_PTR])? (wr_ptr_wab_rdclk[FIFO_PTR-1:0]-rd_ptr_wab[FIFO_PTR-1:0]): (FIFO_DEPTH-(rd_ptr_wab[FIFO_PTR-1:0]-wr_ptr_wab_rdclk[FIFO_PTR-1:0])); always@(posedge rdclk or negedge rstb_rdclk) begin if(!rstb_rdclk) begin fifo_empty <= 0; data_avail <= 0; end else begin fifo_empty <= fifo_empty_nxt; data_avail <= data_avail_nxt; end end endmodule

以上内容来源于《Verilog高级数字系统设计技术和实例分析》

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询