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

同步FIFO

一、原理介绍

FIFO(First in, First out),顾名思义是先入先出存储器,数据的写入顺序和读出顺序一致。

一条数据流中有两个模块A和B,B接收A处理好的数据。假如A处理10个数据的时间,B只能处理5个数据,那么就会丢失5个数据,FIFO的作用就是存储A处理好的数据,B每处理完一个数据,就从FIFO中取下一个处理。

1.1 同步FIFO原理

同步FIFO和异步FIFO相比,核心区别是读写使用同一个时钟。

核心功能是:作为一个数据缓冲区,允许数据以写入的顺序被读出,同时通过空满状态标志来防止数据读空或溢出。

1.2 同步FIFO的构成

  1. 双端口RAM:读写操作独立,可同时进行;
  2. 写指针:总是指向下一个写入数据的存储地址,如果已经写入了4个数据,则指向地址5;
  3. 读指针:总是指向下一个要被读取数据的存储地址,如果已经读取了8个数据,则指向地址9;
  4. 满标志:写入的数据加上未被读出的数据个数等于FIFO的深度,输出满信号,阻止上游模块继续写入数据;
  5. 空标志:FIFO中所有数据被读出后,输出空信号,阻止下游模块继续读出数据。

1.3 FIFO的精华:空满状态的判断

本文使用计数器的方法实现空满状态判断。

定义一个计数器,计数器的量程等于FIFO的深度,即假设FIFO深度为8,那么计数器的计数范围是0~8。

计数器自增自减有如下5种情况:

  1. 上下游模块同时读写FIFO,且FIFO非空非满:计数器不变;
  2. 上游模块写FIFO,且非满:计数器自增;
  3. 下游模块读FIFO,且非空:计数器自减;
  4. 上游模块写FIFO,但满:计数器不变;
  5. 下游模块读FIFO,但空:计数器不变。

空满状态判断:

  • 当计数器等于FIFO深度,输出满信号;
  • 当计数器等于0,输出空信号。

二、同步FIFO代码

2.1 接口

module sfifo #(parameter DATA_WIDTH = 8,parameter FIFO_DEPTH = 8
)(input  wire                     clk_i,input  wire                     rstn_i,input  wire                     wr_en_i,input  wire [DATA_WIDTH-1:0]    wr_data_i,output wire                     fifo_full_o,input  wire                     rd_en_i,output wire [DATA_WIDTH-1:0]    rd_data_o,output wire                     fifo_empty_o
);
  1. wr_en_i:写使能;
  2. wr_data_i:写数据;
  3. rd_en_i:读使能;
  4. rd_data_o:读数据;
  5. fifo_full_o:写满信号;
  6. fifo_empty_o:读空信号。

2.2 读写指针

reg  [$clog2(FIFO_DEPTH)-1:0] wr_ptr_d;
reg  [$clog2(FIFO_DEPTH)-1:0] rd_ptr_d;
wire [$clog2(FIFO_DEPTH)-1:0] wr_ptr = (wr_en_i && !fifo_full_o)  ? wr_ptr_d + 1'b1 : wr_ptr_d;
wire [$clog2(FIFO_DEPTH)-1:0] rd_ptr = (rd_en_i && !fifo_empty_o) ? rd_ptr_d + 1'b1 : rd_ptr_d;always @(posedge clk_i or negedge rstn_i) if(!rstn_i) wr_ptr_d <= 'd0; else wr_ptr_d <= wr_ptr;
always @(posedge clk_i or negedge rstn_i) if(!rstn_i) rd_ptr_d <= 'd0; else rd_ptr_d <= rd_ptr;
  1. wr_ptr:写指针,当FIFO没有写满时才能自增,永远指向下一个数据被写到的地址;
  2. rd_ptr:读指针,当FIFO没有读空时才能自增,永远指向下一个被读出的数据的地址;
  3. wr_ptr_d:指向当前被写入的数据的地址;
  4. rd_ptr_d:指向当前被读出的数据的地址。

2.3 空满状态判断

reg  [$clog2(FIFO_DEPTH):0] fifo_cnt_d;
wire [$clog2(FIFO_DEPTH):0] fifo_cnt = ((!fifo_full_o  && wr_en_i) && (!fifo_empty_o && rd_en_i)) ? fifo_cnt_d :( !fifo_full_o  && wr_en_i)                                ? fifo_cnt_d + 1'b1 :( !fifo_empty_o && rd_en_i)                                ? fifo_cnt_d - 1'b1 : fifo_cnt_d;always @(posedge clk_i or negedge rstn_i) if(!rstn_i) fifo_cnt_d <= 'd0; else fifo_cnt_d <= fifo_cnt;assign fifo_full_o  = (fifo_cnt_d == FIFO_DEPTH) ? 1'b1 : 1'b0;
assign fifo_empty_o = (fifo_cnt_d == 'd0)        ? 1'b1 : 1'b0;

2.4 数据读写

reg  [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
reg  [DATA_WIDTH-1:0] rd_data;always @(posedge clk_i or negedge rstn_i) if(!fifo_full_o  && wr_en_i) mem[wr_ptr_d] <= wr_data_i;
always @(posedge clk_i or negedge rstn_i) if(!fifo_empty_o && rd_en_i) rd_data       <= mem[rd_ptr_d];assign rd_data_o    = rd_data;

数据读写独立进行,可同时读同时写。

2.5 仿真

`timescale 1ns/1ns
module sfifo_tb();
reg             clk_i;
reg             rstn_i;reg             wr_en_i;
reg  [7:0]      wr_data_i;
wire            fifo_full_o;reg             rd_en_i;
wire [7:0]      rd_data_o;
wire            fifo_empty_o;sfifo #(.DATA_WIDTH(8),.FIFO_DEPTH(8)
)my_sfifo(.clk_i          (clk_i),.rstn_i         (rstn_i),.wr_en_i        (wr_en_i),.wr_data_i      (wr_data_i),.fifo_full_o    (fifo_full_o),.rd_en_i        (rd_en_i),.rd_data_o      (rd_data_o),.fifo_empty_o   (fifo_empty_o)
);always #10 clk_i = ~clk_i;initial beginclk_i = 0;rstn_i = 1;wr_en_i = 0;wr_data_i = 0;rd_en_i = 0;#20 rstn_i = 0;#20 rstn_i = 1;@(negedge clk_i);pop();push(1);pop();@(posedge clk_i) $finish;
endtask push(input [7:0] data);if(fifo_full_o) beginwr_en_i = 1'b1;wr_data_i = data;$display("FULL! Can't push now!");@(negedge clk_i) wr_en_i = 1'b0;end else beginwr_en_i   = 1'b1;wr_data_i = data;$display("Push: %0d", data);@(negedge clk_i) wr_en_i = 1'b0;end
endtasktask pop();if(fifo_empty_o) begin$display("Empty! Can't pop now!");end else beginrd_en_i = 1'b1;@(negedge clk_i) rd_en_i = 1'b0;$display("Pop: %0d", rd_data_o);end
endtaskendmodule

2.6 部分仿真结果

写入8个数据,
image

读出8个数据,
image

同时读写,
image

本文的原理部分和代码均参考自
掰开揉碎讲 FIFO(同步FIFO和异步FIFO) - Doreen的FPGA自留地 - 博客园
https://www.cnblogs.com/DoreenLiu/p/17348480.html

http://www.hskmm.com/?act=detail&tid=28213

相关文章:

  • P13274 [NOI2025] 三目运算符
  • Microsoft Office不小心卸载或重装系统后,如何重新安装 ... - sherlock
  • HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包) - 实践
  • 使用JaCoCo进行代码覆盖率分析
  • 计算机视觉专家入选德国国家科学院
  • 2025 年工程管理软件/软件系统/软件App/软件平台/工程管理软件和验房系统公司/企业推荐榜:数字化转型下的实用选型指南
  • 【Java学习】【Java基础】--第1篇:入门Java和对面向对象的理解
  • solutions
  • 技术面:Spring (事务传播机制、事务失效的原因、BeanFactory和FactoryBean的关系)
  • 安装与配置MySQL 8 on Ubuntu,包括权限授予、数据库备份及远程连接
  • 04-最简单的字符设备驱动
  • 完整教程:手机可视化方案(针对浓度识别)
  • AI元人文系列文章:决策范式与无为而治
  • SAP导入证书
  • Kubernetes存储卷:保障有状态应用的数据持久化
  • MySQL的查询操作语法要点
  • 华为链路聚合配置
  • 手机adb 调试自己
  • 离线安装 mysql
  • what is a good parent
  • 2025 年公共/商场/学校/地铁/电影院/会所/机场/卫生间隔断厂家选购指南:优质厂商推荐与实用选择策略
  • 为什么不该用 Double 表示金额及解决方案
  • Windows开发环境安装备忘录
  • Vue.use(Vuex)
  • [Gym-100343E]Convex Permutominoes 题解
  • MyBatis 中的动态 SQL 的相关使用方法(Javaee/MyBatis) - 教程
  • 网络优化问题
  • Java环境安装备忘录
  • 深入解析:【Spring MVC终极指南】一文掌握请求处理与响应!从Servlet原生方式到SpringMVC高效优雅写法
  • foobar2000 v2.25.2 汉化版