数字IC前端学习笔记:FIFO的Verilog实现(一)

张开发
2026/6/8 3:58:41 15 分钟阅读
数字IC前端学习笔记:FIFO的Verilog实现(一)
相关文章数字IC前端专栏https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482一、引言FIFO表示先入先出First in First out它是一种存储器结构被广泛用于芯片设计中。FIFO由存储器单元队列或阵列组成如名所示先被写入队列的数据也是先被读出。在芯片设计中FIFO可以满足下列需求1当输入数据速率和输出速率不匹配时作为临时存储单元。例如CPU可以先将数据写入FIFO然后继续做其他工作设备可以很方便地从FIFO中读取数据。再如因特网控制器它将从网络接收来的数据存入FIFO后端的DMADirect Memory Access直接存储器访问控制器位于PCIe或PCI接口电路中从FIFO中读取数据然后写入系统存储器。2用于不同时钟域之间的同步。实际应用中数据有时会从一个时钟域进入另一个时钟域此时FIFO不仅做临时数据存储单元也起到数据同步的作用。3输入数据和输出数据的数据宽度不匹配时可以用数据宽度调整电路。我们在后面再详细介绍这些应用现在先给出FIFO的结构和输入输出端口如下图所示宽度为6、深度为8的FIFO。这意味着此FIFO中有8个存储位置每个位置可以存储6位数据。注意实际上读写指针对于外部是不可见的图中画出只是为了后面说明方便。二、FIFO操作电路复位后FIFO为空写指针和读指针都指向同一个位置该位置通常是0。数据由write_data端口输入由控制信号write_en控制由read_data端口输出由控制信号read_en控制。write_pointer指针指向下一个可能的空位置队列尾元素的后一个位置而read_pointer指向下一个被读取的位置队列首元素。我们一般采取循环队列的方式构建FIFO即如果我们持续写入当write_pointer指向最大值时例如111如果再次写入元素write_pointer将回到0。读指针也拥有这种循环的性质换句话说写指针和读指针以环形的方式移动写指针在前读指针在后追随。写操作和读操作也被称为push/pop、put/get或fill/drain操作。我们也可以同时进行读操作和写操作因为两种操作使用不同的指针、控制信号和数据总线。FIFO的这种操作就像一个水箱它有一个进水口让水进入水箱还有一个出口让水流出水箱。在任何时候可以一边进水一边放水我们需要关注的是除了FIFO空或满读指针和写指针不能相同。我们要确保两种情况不会发生一是给满的FIFO写入数据所有位置都有数据没有多余位置二是从空的FIFO中读取数据FIFO中没有有效数据。它们分别被称为上溢overrun和下溢underrun。FIFO会产生fifo_full和fifo_empty信号用于表示FIFO是否已满或已空。当FIFO为满时禁止写入数据当FIFO为空时禁止继续读取数据。在详细叙述前先通过时序图来进一步增强我们的理解如下图所示。三、同步FIFO原理在同步FIFO中单一时钟用于写入和读取操作。数据流和相关的控制逻辑在一个时钟域内工作。同步FIFO用于临时存储数据此时写入和读取操作即可以同时发生也可以发生在不同时刻。由于同步FIFO只使用了一个时钟其控制逻辑相对于异步FIFO来说简单得多前面讨论过一些输入和输出端口现在需要增加一些有用的输出如fifo_full、fifo_empty、room_available、data_available。从名称中可以看出fifo_full信号表示FIFO为满fifo_empty表示FIFO为空。这两个信号作为边界条件提醒外部电路不要写满的FIFO和读空的FIFO。FIFO还可能提供其他信号如almost_full和almost_empty用于提前提供FIFO的满和空的信息。例如当做设计的FIFO还剩余2到3个空余位置时almost_full信号有效此时负责外围写入的电路就要考虑停止写入了因为从决定停止到write_en信号被置为无效可能还需要多个时钟周期。如果写入逻辑等待fifo_full标识有效后才将write_en信号置为无效就可能太迟了可能会有信号被错误写入或遗漏取决于是否有保险措施。almost_empty也采用类似的工作方式用于防止数据下溢。相较于almost_full和almost_empty信号我们有时更愿意使用room_available和data_available来提供准确的剩余空间和数据深度信息本文下面的FIFO就是使用了这两个信号。写逻辑使用room_available因为它关注还有多少空间读逻辑使用data_available因为它关注还有多少数据可读。四、同步FIFO的Verilog实现module synch_fifo #(parameter FIFO_PTR 4, FIFO_WIDTH 32, FIFO_DEPTH 16) (fifo_clk,rstb, fifo_wren, fifo_wrdata, fifo_rden, fifo_rddata, fifo_full, fifo_empty, fifo_room_avail, fifo_data_avail); //端口声明 //******************************************************* input fifo_clk; input rstb; input fifo_wren; input [FIFO_WIDTH - 1 : 0] fifo_wrdata; input fifo_rden; input [FIFO_WIDTH - 1 : 0] fifo_rddata; output reg fifo_full; output reg fifo_empty; output reg [FIFO_PTR : 0] fifo_room_avail; output [FIFO_PTR : 0] fifo_data_avail; //变量定义 //******************************************************* reg [FIFO_PTR-1 : 0] wr_ptr,wr_ptr_nxt; reg [FIFO_PTR-1 : 0] rd_ptr,rd_ptr_nxt; reg [FIFO_PTR : 0] num_entries,num_entries_nxt; reg [FIFO_PTR : 0] fifo_room_avail; reg fifo_full,fifo_empty; wire [FIFO_PTR : 0] fifo_room_avail_nxt; wire [FIFO_PTR : 0] fifo_data_avail_nxt; wire fifo_full_nxt,fifo_empty_nxt; //写指针控制逻辑 //******************************************************* always(*) begin wr_ptr_nxt wr_ptr; if(fifo_wren) begin if(wr_ptr FIFO_DEPTH - 1) wr_ptr_nxt 0; else wr_ptr_nxt wr_ptr 1; end end //读指针控制逻辑 //******************************************************* always(*) begin rd_ptr_nxt rd_ptr; if(fifo_rden) begin if(rd_ptr FIFO_DEPTH - 1) rd_ptr_nxt 0; else rd_ptr_nxt rd_ptr 1; end end //计算FIFO内的数据量 //******************************************************* always(*) begin num_entries_nxt num_entries; if(fifo_wren fifo_rden) num_entries_nxt num_entries; else if(fifo_wren) num_entries_nxt num_entries 1; else if(fifo_rden) num_entries_nxt num_entries - 1; end //产生提示信号 //******************************************************* assign fifo_full_nxt (num_entries_nxt FIFO_DEPTH); assign fifo_empty_nxt (num_entries_nxt 0); assign fifo_data_avail num_entries; assign fifo_room_avail_nxt (FIFO_DEPTH - num_entries_nxt); //寄存器操作 //******************************************************* always(posedge fifo_clk or negedge rstb) begin if(!rstb) begin wr_ptr 0; rd_ptr 0; num_entries 0; fifo_full 0; fifo_empty 0; fifo_room_avail 0; end else begin wr_ptr wr_ptr_nxt; rd_ptr rd_ptr_nxt; num_entries num_entries_nxt; fifo_full fifo_full_nxt; fifo_empty fifo_empty_nxt; fifo_room_avail fifo_room_avail_nxt; end end //可以自己写一个存储器也可以直接例化存储器ip这里选择后者 //******************************************************* sram #(.FIFO_PTR (FIFO_PTR), .FIFO_WIDTH (FIFO_WIDTH)) sram_0 (.wrclk(fifo_clk), .wren(fifo_wren), .wrptr(wr_ptr), .wrdata(fifo_wrdata), .rdclk(fifo_clk), .rden(fifo_rden), .rdptr(rd_ptr), .rddata(fifo_rddata)); endmodule以上内容来源于《Verilog高级数字系统设计技术和实例分析》

更多文章