当前位置: 首页 > news >正文

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

相关文章

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


一、引言

FIFO表示先入先出(First in First out),它是一种存储器结构,被广泛用于芯片设计中。FIFO由存储器单元队列或阵列组成,如名所示,先被写入队列的数据也是先被读出。在芯片设计中,FIFO可以满足下列需求:

(1)当输入数据速率和输出速率不匹配时,作为临时存储单元。例如,CPU可以先将数据写入FIFO,然后继续做其他工作,设备可以很方便地从FIFO中读取数据。再如因特网控制器,它将从网络接收来的数据存入FIFO,后端的DMA(Direct 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高级数字系统设计技术和实例分析》

http://www.jsqmd.com/news/579354/

相关文章:

  • 05_Cursor之自定义规则与配置
  • web3.py错误代码大全:10个常见问题快速定位与终极解决方案
  • 从Vue 3的响应式原理,倒过来学JavaScript的Proxy、Reflect和WeakMap
  • 未来Altium许可证管理技术展望
  • Flow自定义主题系统:打造个性化阅读环境的完整教程
  • 无需重启!Telegraf动态配置更新机制详解:从痛点到实现
  • 避开ZYNQ数据交互的坑:PL端FIFO深度怎么设?DMA用HP口还是GP口?一次讲清楚
  • 简易CPU设计入门:控制总线的剩余信号(三)
  • HTML学习三
  • Apache NiFi终极指南:10个模板与版本控制技巧实现高效流程复用与团队协作
  • 10个HTTPie CLI高级功能实战技巧:从入门到精通API调试
  • 2026国产品牌测高仪推荐:精选实力厂家与高性价比机型 - 栗子测评
  • OpenClaw模型热切换方案:Qwen3.5-9B与本地小模型协同工作
  • Bootstrap FileInput终极排错指南:从初始化到上传的完整解决方案
  • 基于YOLOv8的‘海参等四类水下目标‘检测实验
  • 毕业设计用什么ai?实测8款AI论文生成工具测评,查重率仅6%超可靠!
  • OpenClaw监控方案:Phi-3-mini-128k-instruct任务日志分析与告警
  • 2026国产三坐标品牌推荐攻略:三坐标生产厂家+三坐标测量机生产厂家+三坐标测量软件培训公司全收录 - 栗子测评
  • 突破性能瓶颈:Telegraf高并发场景的负载均衡优化指南
  • 06_Cursor之上下文管理与代码库理解
  • OpenClaw多模型切换:Kimi-VL-A3B-Thinking与文本模型的协同工作流
  • OpenClaw技能市场挖掘:10个最实用的Gemma-3-12b-it插件推荐
  • 终极fswatch过滤器配置指南:如何用正则表达式精准控制文件监控范围
  • OpenClaw任务调度:Qwen3-14b_int4_awq模型定时执行设置
  • 3步实现Telegraf智能采样:降低70%数据量仍保持99%监控精度
  • 2026年热门的海关数据统计口碑公司推荐 - 品牌宣传支持者
  • 2026低温除湿机厂家/档案室除湿机厂家怎么挑?专业选型推荐厂家 - 栗子测评
  • 企业级区块链开发终极指南:web3.py可扩展架构深度解析
  • docker 安装 mindoc
  • 终极 try 版本升级指南:从 0.1.0 到 0.2.0 的 10 个重要变化