目录

  • 1. 概述
  • 2. 同步FIFO设计
    • 2.1 同步FIFO结构
    • 2.2 同步FIFO空满信号产生
      • 2.2.1 时序逻辑产生空满
        • 2.2.1.1 fifo满信号产生
        • 2.2.1.2 fifo空信号产生
      • 2.2.2 计数器产生空满
    • 2.3 verilog代码实现
      • 2.3.1 FIFO memory选取经验
      • 2.3.2 基于寄存器的FIFO设计
      • 2.3.3 基于DPRAM的FIFO设计
  • 参考资料

1. 概述

  FIFO(First In First Out)是一种先进先出的数据交互方式,几乎所有的数字芯片都会使用FIFO,用于模块间数据的缓冲、跨异步传输数据等。按照工作时钟可以分为同步FIFO和异步FIFO,同步FIFO内所有电路都工作在同一个时钟域,常用于模块间数据缓冲;异步FIFO内存在两个工作时钟,读写逻辑由不同时钟驱动,常用于跨时钟域数据交互。

  本文将给出同步FIFO的典型设计,异步FIFO的典型设计见“硬件架构的艺术:异步FIFO设计”。

2. 同步FIFO设计

2.1 同步FIFO结构

  典型的FIFO由三部分组成,FIFO写控制、FIFO读控制和FIFO memory(DPRAM或reg),各部分功能如下:

  • FIFO写控制:写指针、FIFO满信号产生;
  • FIFO读控制:读指针、FIFO空信号产生;
  • FIFO memory:读写memory逻辑;


  FIFO内部通过对写请求、读请求计数产生读、写指针,读写指针即为memory的读、写地址。写指针指向下一个要写入的地址,读指针指向下一个要读取的地址,写请求使写指针递增,读请求使读指针递增。

  FIFO模块输出empty和full信号指示其状态,fifo_full表示FIFO内空间已满不能再写入数据,fifo_empty表示FIFO内没有可供读取的下一个有效数据。

2.2 同步FIFO空满信号产生

  复位时FIFO读、写指针都归零。此时fifo_empty拉高,只能写不能读,一旦有数据写入,会将fifo_empty拉低,允许读取数据。当fifo的写指针指向fifo_depth-1时,此时进行一个写操作会使写指针归零(此时无读操作),拉高fifo_full。

  总之在读写指针相等时,FIFO要么空要么满,所以需要对这两种情况进行区分。

2.2.1 时序逻辑产生空满

2.2.1.1 fifo满信号产生

  FIFO满状态是由写操作触发的:

“在写操作使读、写指针在下个时钟保持相等时,FIFO满”

  更为通俗的解释是“写操作让写指针追上了读指针,即写指针套了读指针一圈(跑步的角度)”,此时fifo_full的condition可以写为:

condition = (wr_en == 1’b1 && (wr_ptr+1’b1 == rd_ptr))

  时序逻辑产生FIFO满信号代码如下:

// ---- fifo full
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0) beginfifo_full <= 1'b0;endelse if(rd_en == 1'b1) beginfifo_full <= 1'b0;endelse if((wr_en == 1'b1) && (wr_ptr + 1'b1 == rd_ptr)) beginfifo_full <= 1'b1;end
end

2.2.1.2 fifo空信号产生

  FIFO空状态是由读操作触发的:

“在读操作使读、写指针在下个时钟保持相等时,FIFO空”

  更为通俗的解释是“读操作让读指针追上了写指针”,此时fifo_empty的condition可以写为:

condition = (rd_en == 1’b1 && (rd_ptr+1’b1 == wr_ptr))

// ---- fifo empty
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0) beginfifo_empty <= 1'b0;endelse if(wr_en == 1'b1) beginfifo_empty <= 1'b0;endelse if((rd_en == 1'b1) && (rd_ptr + 1'b1 == wr_ptr)) beginfifo_empty <= 1'b1;end
end

2.2.2 计数器产生空满

  组合逻辑产生空满是通过使用计数器来持续指示FIFO中空或满位置的个数。计数器的位宽要能够表示FIFO深度,位宽关系如下:

localparam FIFO_COUNT_WIDTH = FIFO_DEPTH_WIDTH + 1;

  计数器在复位时归零,写操作会让计数器加一,读操作会让计数器减一,读写操作同时发生计数器不变。空满信号产生如下:

  • 空信号:计数器为零
  • 满信号:计数器等于FIFO深度

  该方法的缺点是当FIFO深度较大时,比较器的规模较大,此时组合逻辑延迟变大,最终会降低FIFO最大工作频率。计数器产生空满的代码实现如下:

// ==========================================================================
// localpara
// ==========================================================================
localparam FIFO_DEPTH_WIDTH = $clog2(FIFO_DEPTH);
localparam FIFO_COUNT_WIDTH = FIFO_DEPTH_WIDTH + 1;
// ==========================================================================
// fifo count
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0) beginfifo_count <= {FIFO_COUNT_WIDTH{1'b0}};endelse if((wr_en == 1'b1) && (rd_en == 1'b0) && (fifo_full != 1'b1)) beginfifo_count <= fifo_count + 1'b1;endelse if((rd_en == 1'b1) && (wr_en == 1'b0) && (fifo_empty != 1'b1)) beginfifo_count <= fifo_count - 1'b1;end
end
// ==========================================================================
// fifo status
assign fifo_full  = (fifo_count[FIFO_COUNT_WIDTH] == 1'b1);
assign fifo_empty = (fifo_count == {FIFO_COUNT_WIDTH{1'b0}});

2.3 verilog代码实现

2.3.1 FIFO memory选取经验

  FIFO的memory选择使用寄存器或DPRAM取决于FIFO的深度和数据位宽,当FIFO_DEPTH * FIFO_DATA_WIDTH > 1024,memory选择DPRAM,反之选择寄存器。

  memory规模在1024bit时,DPRAM的面积可能会大于寄存器,并不是任何时候选择DPRAM作为memory都是最优选择,不仅在FIFO的设计中如此,在cache buffer的设计中也是如此。

2.3.2 基于寄存器的FIFO设计

  使用寄存器实现同步FIFO的代码如下:

module sync_fifo_reg (
module sync_fifo_reg(clk,rst_n,wr_en,wr_data,rd_en,rd_data,fifo_full,fifo_empty
);// ==========================================================================
// parameter
// ==========================================================================
parameter FIFO_DATA_WIDTH = 8;
parameter FIFO_DEPTH      = 16;// ==========================================================================
// localpara
// ==========================================================================
localparam FIFO_DEPTH_WIDTH = $clog2(FIFO_DEPTH);
localparam FIFO_COUNT_WIDTH = FIFO_DEPTH_WIDTH + 1;// ==========================================================================
// I/O
// ==========================================================================// -------- clock && reset
input                               clk;
input                               rst_n;// -------- fifo wr
input                               wr_en;
input   [FIFO_DATA_WIDTH-1:0]       wr_data;// -------- fifo rd
input                               rd_en;
output  [FIFO_DATA_WIDTH-1:0]       rd_data;// -------- fifo status
output                              fifo_full;
output                              fifo_empty;// ==========================================================================
// signal define
// ==========================================================================// -------- fifo wr ctrl
reg     [FIFO_DEPTH_WIDTH-1:0]      wr_ptr;// -------- fifo rd ctrl
reg     [FIFO_DEPTH_WIDTH-1:0]      rd_ptr;// -------- fifo count
reg     [FIFO_COUNT_WIDTH-1:0]      fifo_count ;// -------- fifo memory
reg     [FIFO_DATA_WIDTH-1:0]       fifo_mem[FIFO_DEPTH-1:0];// ==========================================================================
// main body
// ==========================================================================// ==========================================================================
// fifo wr ctrl
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0) beginwr_ptr <= {FIFO_DATA_WIDTH{1'b0}};endelse if((wr_en == 1'b1) && (fifo_full != 1'b1)) beginwr_ptr <= wr_ptr + 1'b1;end
end
// ==========================================================================
// fifo rd ctrl
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0) beginrd_ptr <= {FIFO_DATA_WIDTH{1'b0}};endelse if((rd_en == 1'b1) && (fifo_empty != 1'b1)) beginrd_ptr <= rd_ptr + 1'b1;end
end
// ==========================================================================
// fifo count
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0) beginfifo_count <= {FIFO_COUNT_WIDTH{1'b0}};endelse if((wr_en == 1'b1) && (rd_en == 1'b0) && (fifo_full != 1'b1)) beginfifo_count <= fifo_count + 1'b1;endelse if((rd_en == 1'b1) && (wr_en == 1'b0) && (fifo_empty != 1'b1)) beginfifo_count <= fifo_count - 1'b1;end
end// ==========================================================================
// fifo wr/rd
always @(posedge clk) beginif((wr_en == 1'b1) && (fifo_full != 1'b1)) beginfifo_mem[wr_ptr] <= wr_data;end
endassign rd_data = fifo_mem[rd_ptr];// ==========================================================================
// fifo status
assign fifo_full  = (fifo_count[FIFO_COUNT_WIDTH-1] == 1'b1);
assign fifo_empty = (fifo_count == {FIFO_COUNT_WIDTH{1'b0}});endmodule

2.3.3 基于DPRAM的FIFO设计

  当设计需要大容量的FIFO时,使用寄存器搭建FIFO的面积成本太高,通常选择使用DPRAM作为FIFO的存储体。DPRAM和寄存器的区别在于读操作,寄存器读有效和读数据有效在同一个时钟周期,而DPRAM读有效拉高后的一个时钟周期,有效数据才从DPRAM读出。从时序上来看如下图所示:

  • 寄存器:读使能和读数据有效在同一个时钟周期
  • DPRAM:读数据有效delay读使能一个周期


  使用DPRAM作为FIFO的存储体,若仍然使用“基于寄存器的FIFO设计”会存在两个问题:

  • 性能损失:由于读数据有效滞后读使能一个时钟周期,若读使能连续,此时性能损失很小;若读使能不连续,可能会对后级性能产生影响。
  • rd_data_valid产生变复杂:由于读数据有效滞后读使能一个时钟周期,后级无法使用fifo_empty产生rd_data_valid。在“基于寄存器的FIFO设计”中,后级可以通过以下代码产生rd_data_valid,但此时只能在外部增加逻辑产生rd_data_valid,这显然不利于FIFO的高频率地例化。

    assign rd_data_valid = (reg_rd_en) && (!fifo_empty);

  博客“基于SRAM的同步FIFO设计”给出了一种解决办法,FIFO中除了SRAM存储数据(图示DPRAM的读延迟为三个时钟周期,但一般数字芯片读DPRAM的延迟都是为一个时钟周期),还增加了一个小容量的寄存器组缓存数据,结构如下图所示:

参考资料

  1. 硬件架构的艺术
  2. https://www.cnblogs.com/digital-wei/p/6103271.html
  3. https://blog.csdn.net/huangzhicong3/article/details/108317910
  4. 基于SRAM的同步FIFO设计

硬件架构的艺术:同步FIFO设计相关推荐

  1. 2-10、秋招年6月晋升期——《硬件架构的艺术-数字电路的设计方法与技术》

    写在前:虽然这几个月比较忙,但是挤一挤还是有时间更新文章的,说到底还是懒啊hhh,昨晚受到家人激励,嗯,,,,继续更新~ 第4章 时钟分频器 感兴趣的可以浏览一下. 以我目前工作年限,暂时还未涉及到这 ...

  2. 硬件架构的艺术(四)

    硬件架构的艺术 第七八九章 处理字节顺序 大端小端的特点 处理字节顺序不匹配问题 消抖技术 抖动 开关行为 开关种类 消抖技术 RC消抖 硬件消抖电路 软件消抖电路 在足以让抖动停止后的时间读出开关 ...

  3. 【硬件架构的艺术】学习笔记(4)流水线的艺术

    目录 写在前面 4. 流水线的艺术 4.1 介绍 4.2 影响最大时钟频率的因素 4.2.1 时钟偏移 4.2.2 时钟抖动 4.3 流水线 读书笔记汇总 写在前面 这个博客系列是对最近阅读的书籍&l ...

  4. 硬件架构的艺术(一)

    硬件架构的艺术第一.二章 亚稳态 亚稳态的概念 亚稳态窗口 MTBF 以下情况可能会发生亚稳态: 减小亚稳态发生的概率的方法: 亚稳态测试电路 同步器的类型: 模式A: 模式B: 综上:减小亚稳态发生 ...

  5. 硬件架构的艺术-行波计数器笔记

    1.同步电路和异步电路 同步电路:顾名思义,就是保证保持同一个时刻.就是在同一个时钟的上升沿或者下降沿去处理数据.(即同时的去处理数据)  异步设计:电路没有统一的时钟,即不是所有的触发器都同时处理数 ...

  6. 同步FIFO设计verilog设计及仿真

    同步FIFO设计 1.功能定义: 用16*8 RAM实现一个同步先进先出(FIFO)队列设计.由写使能端控制该数据流的写入FIFO,并由读使能控制FIFO中数据的读出.写入和读出的操作由时钟的上升沿触 ...

  7. 同步fifo的串并_同步FIFO设计Spec(示例代码)

    为什么要写Spec文档: 记得刚进公司实习的时候,导师安排我写一个SM4算法AHB接口模块,要求写代码前 写出详细的设计文档,详细到什么程度呢,看着文档就能把代码写好,作为一个只 在学校写过数字钟的小 ...

  8. 任意深度同步FIFO设计总结(非2次幂)

    同步FIFO属于是最基本的模块之一了,但是对于任意深度(非2次幂与2次幂均可以)的同步FIFO可能有些难度,但是只要了解格雷码的性质就能解决.接下来将介绍整个设计的思考流程,不单单是给出解决方法. 对 ...

  9. fifo的rdata_同步FIFO设计

    本文从微信公众号--数字IC小站,​转载,欢迎关注,微信公众号更新更多更快带选通信号的同步FIFO(重发)​mp.weixin.qq.com ​我们常见的同步FIFO一般都是固定位宽输入,固定位宽输出 ...

最新文章

  1. php读取mysql分页查询
  2. python可视化爬虫框架_8个最高效的Python爬虫框架
  3. 数据库连接python_python连接数据库
  4. Java黑皮书课后题第5章:**5.28(显示每月第一天是周几)编写程序,提示用户输入年份和代表概念第一天是周几的数字,然后在控制台显示该年各个月份的第一天是周几
  5. JAVA反射机制及其原理实现
  6. SAP CRM WebClient UI Text Type 显示的过滤逻辑
  7. 计算机专业评副高论文要求,护士晋升副高职称论文要求
  8. SpringBoot中.properties文件中配置项显示到页面中文乱码解决
  9. Codeforces Round #436 (Div. 2)
  10. 换手率:为什么美国人不爱频繁申赎基金?
  11. OpenCasCade标注显示类(独立)
  12. 视频接口的种类及数据类型
  13. HTML5工程师利用原生js开发百度搜索黑洞漩涡特效
  14. 公关世界杂志公关世界杂志社公关世界编辑部2022年第22期目录
  15. C++如何获取虚函数表(vtbl)的内容及虚成员函数指针存放原理
  16. vue下载pdf为空问题解决
  17. 前端sku-spu详解
  18. 相机的变焦,对焦和景深的理解和照相機的成像原理
  19. latex特殊符号用法
  20. Matlab——高斯白噪声处理

热门文章

  1. 墨天轮国产数据库沙龙 | 四维纵横姚延栋 :MatrixDB,All-in-One高性能时序数据库
  2. IT 战略规划-方法论
  3. 现代计算机基本工作原理,计算机基本原理
  4. 2022高压电工考题模拟考试平台操作
  5. 宝塔虚拟服务器,通过宝塔面板配置虚拟主机(共享服务器)
  6. 23种设计模式(GOF)
  7. 自学实前后端践项目3 Spring Cloud微服务 6
  8. 《Linux Shell脚本攻略》学习笔记-第一章
  9. 用计算机制作微课教学教案,微课教案设计
  10. python数字1 3怎么表示_Python3数据类型之数字