硬件架构的艺术:同步FIFO设计
目录
- 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的延迟都是为一个时钟周期),还增加了一个小容量的寄存器组缓存数据,结构如下图所示:
参考资料
- 硬件架构的艺术
- https://www.cnblogs.com/digital-wei/p/6103271.html
- https://blog.csdn.net/huangzhicong3/article/details/108317910
- 基于SRAM的同步FIFO设计
硬件架构的艺术:同步FIFO设计相关推荐
- 2-10、秋招年6月晋升期——《硬件架构的艺术-数字电路的设计方法与技术》
写在前:虽然这几个月比较忙,但是挤一挤还是有时间更新文章的,说到底还是懒啊hhh,昨晚受到家人激励,嗯,,,,继续更新~ 第4章 时钟分频器 感兴趣的可以浏览一下. 以我目前工作年限,暂时还未涉及到这 ...
- 硬件架构的艺术(四)
硬件架构的艺术 第七八九章 处理字节顺序 大端小端的特点 处理字节顺序不匹配问题 消抖技术 抖动 开关行为 开关种类 消抖技术 RC消抖 硬件消抖电路 软件消抖电路 在足以让抖动停止后的时间读出开关 ...
- 【硬件架构的艺术】学习笔记(4)流水线的艺术
目录 写在前面 4. 流水线的艺术 4.1 介绍 4.2 影响最大时钟频率的因素 4.2.1 时钟偏移 4.2.2 时钟抖动 4.3 流水线 读书笔记汇总 写在前面 这个博客系列是对最近阅读的书籍&l ...
- 硬件架构的艺术(一)
硬件架构的艺术第一.二章 亚稳态 亚稳态的概念 亚稳态窗口 MTBF 以下情况可能会发生亚稳态: 减小亚稳态发生的概率的方法: 亚稳态测试电路 同步器的类型: 模式A: 模式B: 综上:减小亚稳态发生 ...
- 硬件架构的艺术-行波计数器笔记
1.同步电路和异步电路 同步电路:顾名思义,就是保证保持同一个时刻.就是在同一个时钟的上升沿或者下降沿去处理数据.(即同时的去处理数据) 异步设计:电路没有统一的时钟,即不是所有的触发器都同时处理数 ...
- 同步FIFO设计verilog设计及仿真
同步FIFO设计 1.功能定义: 用16*8 RAM实现一个同步先进先出(FIFO)队列设计.由写使能端控制该数据流的写入FIFO,并由读使能控制FIFO中数据的读出.写入和读出的操作由时钟的上升沿触 ...
- 同步fifo的串并_同步FIFO设计Spec(示例代码)
为什么要写Spec文档: 记得刚进公司实习的时候,导师安排我写一个SM4算法AHB接口模块,要求写代码前 写出详细的设计文档,详细到什么程度呢,看着文档就能把代码写好,作为一个只 在学校写过数字钟的小 ...
- 任意深度同步FIFO设计总结(非2次幂)
同步FIFO属于是最基本的模块之一了,但是对于任意深度(非2次幂与2次幂均可以)的同步FIFO可能有些难度,但是只要了解格雷码的性质就能解决.接下来将介绍整个设计的思考流程,不单单是给出解决方法. 对 ...
- fifo的rdata_同步FIFO设计
本文从微信公众号--数字IC小站,转载,欢迎关注,微信公众号更新更多更快带选通信号的同步FIFO(重发)mp.weixin.qq.com 我们常见的同步FIFO一般都是固定位宽输入,固定位宽输出 ...
最新文章
- php读取mysql分页查询
- python可视化爬虫框架_8个最高效的Python爬虫框架
- 数据库连接python_python连接数据库
- Java黑皮书课后题第5章:**5.28(显示每月第一天是周几)编写程序,提示用户输入年份和代表概念第一天是周几的数字,然后在控制台显示该年各个月份的第一天是周几
- JAVA反射机制及其原理实现
- SAP CRM WebClient UI Text Type 显示的过滤逻辑
- 计算机专业评副高论文要求,护士晋升副高职称论文要求
- SpringBoot中.properties文件中配置项显示到页面中文乱码解决
- Codeforces Round #436 (Div. 2)
- 换手率:为什么美国人不爱频繁申赎基金?
- OpenCasCade标注显示类(独立)
- 视频接口的种类及数据类型
- HTML5工程师利用原生js开发百度搜索黑洞漩涡特效
- 公关世界杂志公关世界杂志社公关世界编辑部2022年第22期目录
- C++如何获取虚函数表(vtbl)的内容及虚成员函数指针存放原理
- vue下载pdf为空问题解决
- 前端sku-spu详解
- 相机的变焦,对焦和景深的理解和照相機的成像原理
- latex特殊符号用法
- Matlab——高斯白噪声处理