博文目录

  • 写在前面
  • 正文
    • FPGA/ASIC中的FIFO
    • 同步FIFO的设计
  • 参考资料
  • 交个朋友

写在前面

  • 个人博客首页
  • 注:学习交流使用!

正文


FPGA/ASIC中的FIFO

FIFO缓冲区如何用于传输数据和跨时钟域

缩写FIFO代表 First In First Out。FIFO在FPGA和ASIC设计中无处不在,它们是基本的构建模块之一。而且它们非常方便!FIFO可用于以下任何目的:

  • 跨时钟域
  • 在将数据发送到芯片外之前将其缓冲(例如,发送到DRAM或SRAM)
  • 缓冲数据以供软件在以后查看
  • 存储数据以备后用

FIFO可以认为是汽车可以驶过的单向隧道。隧道的尽头是一个带门的收费站。门一旦打开,汽车便可以离开隧道。如果那扇门从未打开,而更多的汽车继续进入隧道,那么最终隧道将充满汽车。这称为FIFO溢出,通常这不是一件好事。FIFO的深度可以认为是隧道的长度。FIFO越深,在溢出之前可以容纳更多的数据。FIFO也具有宽度,该宽度表示进入FIFO的数据的宽度(以位数为单位)。下面是任何FIFO基本接口的图像。当您查看任何FIFO时,总是会找到这些信号。通常,会有更多的信号添加其他功能,例如FIFO中的字数计数。参见下图:

FIFO可以分为写一侧和读一侧。写入一侧具有信号“写入使能wr_en”,“写入数据wr_data”和“ FIFO已满fifo_full”。设计人员切勿写入已满的FIFO! 始终检查FIFO已满标志,以确保有空间可以写入另一条数据,否则您将丢失该数据。

读取的一侧具有信号“读取使能rd_en”,“读取数据rd_data”和“ FIFO空fifo_empty”。设计人员切勿读取空的FIFO! 只要您遵循这两个基本规则,您和FIFO就会相处融洽。我再说一遍,因为它们是如此重要。

FIFO的两个规则:

  • 永远不要写入完整的FIFO(溢出)
  • 永远不要从空的FIFO中读取(下溢)

FIFO本身可以由FPGA或ASIC内的专用逻辑组成,也可以由触发器(分布式寄存器)创建。综合工具将使用这两种工具中的哪一种完全取决于您使用的FPGA供应商以及代码的结构。只需知道,当您使用专用逻辑块时,与使用基于寄存器的FIFO相比,它们具有更好的性能。
FIFO是FPGA设计人员的基本构建模块之一,对于正确理解和正确使用至关重要!


同步FIFO的设计

为了简单起见,本文先设计一个同步FIFO,仅带有空满标志。
在给出同步FIFO设计之前,有必要说说同步FIFO的原理,同步FIFO的设计很有必要,它是通往异步FIFO的基础,同步FIFO中的所有原理都理解了,异步FIFO中和同步FIFO相同的东西就不必再费心思思考了,而是直接进入重点,如何控制空满!

FIFO是先进先出的首字母缩写,它描述了如何相对于时间或优先级管理数据。在这种情况下,到达的第一个数据也将是从一组数据中离开的第一个数据。 FIFO缓冲区是一种读/写存储阵列,可自动跟踪数据进入模块的顺序并以相同顺序读出数据。在硬件中,FIFO缓冲区用于同步目的。 它通常实现为循环队列,并具有两个指针:

  • 读指针/读地址寄存器
  • 写指针/写地址寄存器

读写地址最初都位于第一个存储器位置,并且FIFO队列为空。当FIFO缓冲区的读地址和写地址之间的差等于内存阵列的大小时,则FIFO队列为Full(对于异步FIFO而言,可以设计多一位地址表示读指针以及写指针)。

FIFO可以分为同步时钟还是异步时钟,具体取决于是相同时钟(同步)还是不同时钟(异步)控制读写操作。

同步FIFO是指FIFO设计,其中使用时钟信号将数据值顺序写入存储阵列,并使用相同的时钟信号从存储阵列顺序读出数据值。 图1显示了典型FIFO的操作流程。


再看一幅图:

从这幅图中我们可以得到如下信息:

  • 写指针WP总是指向下一个时钟要写的地址;
  • 读指针RP总是指向下一个时钟要读的地址;
  • 读指针等于写指针的时候有可能为空,有可能为满。

这几点都很重要,到后面我们慢慢体会。

代码设计

// Reborn Lee
// blog address: https://blog.csdn.net/Reborn_Lee
module syn_fifo#(parameter DATA_WIDTH = 8,parameter DATA_DEPTH = 8)(input i_clk,input i_rst,//write portinput wr_en,input [DATA_WIDTH - 1 : 0] wr_data,output wr_full,//read portinput rd_en,output [DATA_WIDTH - 1 : 0] rd_data,output rd_empty);//define ramreg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];reg [$clog2(DATA_DEPTH) : 0] fifo_cnt = 0;reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0;reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;// keep track of the  fifo counteralways@(posedge i_clk) beginif(i_rst) beginfifo_cnt <= 0;endelse beginif(wr_en && !rd_en) begin //wr_en is asserted and fifo is not fullfifo_cnt <= fifo_cnt + 1;endelse if(rd_en && !wr_en) begin // rd_en is asserted and fifo is not emptyfifo_cnt <= fifo_cnt - 1;endendend//keep track of the write  pointeralways@(posedge i_clk) beginif(wr_en && !wr_full) beginif(wr_pointer == DATA_DEPTH - 1) beginwr_pointer <= 0; endelse beginwr_pointer <= wr_pointer + 1;endendend//keep track of the read pointer always@(posedge i_clk) beginif(rd_en && !rd_empty) beginif(rd_pointer == DATA_DEPTH - 1) beginrd_pointer <= 0;endelse beginrd_pointer <= rd_pointer + 1;endendend//write data into fifo when wr_en is assertedalways@(posedge i_clk) beginif(wr_en) beginfifo_buffer[wr_pointer] <= wr_data;endend//read data from fifo when rd_en is asserted//assign rd_data = (rd_en)?fifo_buffer[rd_pointer]: 'bz;assign rd_data = fifo_buffer[rd_pointer];assign wr_full = (fifo_cnt == DATA_DEPTH)? 1 : 0;assign rd_empty = (fifo_cnt == 0) ? 1 : 0;endmodule

测试平台:

`timescale 1ns / 1ps// Engineer: Reborn Lee
// Module Name: syn_fifo_tb
//https://blog.csdn.net/Reborn_Lee
//module syn_fifo_tb();parameter DATA_WIDTH = 8;parameter DATA_DEPTH = 8;reg i_clk;reg i_rst;//write portreg wr_en;reg [DATA_WIDTH - 1 : 0] wr_data;wire wr_full;//read portreg rd_en;wire [DATA_WIDTH - 1 : 0] rd_data;wire rd_empty;initial begini_clk = 0;forever begin#5 i_clk = ~i_clk;endendinitial begini_rst = 1;wr_en = 0;rd_en = 0;@(negedge i_clk) i_rst = 0;@(negedge i_clk) wr_en = 1;wr_data = $random;repeat(3) begin@(negedge i_clk)wr_data = $random;    end@(negedge i_clk)wr_en = 0;rd_en = 1;repeat(3) begin@(negedge i_clk); end@(negedge i_clk)rd_en = 0;wr_en = 1;wr_data = $random;repeat(7) begin        @(negedge i_clk)wr_data = $random;end#20 $finish;endsyn_fifo #(.DATA_WIDTH(DATA_WIDTH),.DATA_DEPTH(DATA_DEPTH))inst_syn_fifo(.i_clk    (i_clk),.i_rst    (i_rst),.wr_en    (wr_en),.wr_data  (wr_data),.wr_full  (wr_full),.rd_en    (rd_en),.rd_data  (rd_data),.rd_empty (rd_empty));endmodule

仿真波形

先看最直观的信息:

写入FIFO的数据依次是24,81, 09, 63,读出的数据(从读使能有效开始读)24,81,09,63,读完之后的一个时钟,不在读了,空信号拉高,表示读空了。如下图用箭头以及数字示意:


我们再看看是否写入FIFO的数据依次是24,81,09,63:


确实如此!

再看看读数据的情况:


也确实是从0指针开始读的。

至于,这个FIFO的某些地方值为什么是红色的,是因为没有给FIFO的存储空间赋初值,在仿真时候显示红色,未知而已,在实际的FPGA或者ASIC中,实际是随机值。

我们再来看看设计代码中的写指针,初值为0,在写使能有效时,当时钟上升沿到达时,写指针加1:

//keep track of the write  pointeralways@(posedge i_clk) beginif(wr_en && !wr_full) beginif(wr_pointer == DATA_DEPTH - 1) beginwr_pointer <= 0; endelse beginwr_pointer <= wr_pointer + 1;endendend

而此时,也就是写使能有效,且时钟上升沿到来时,又对FIFO进行写操作:

//write data into fifo when wr_en is assertedalways@(posedge i_clk) beginif(wr_en) beginfifo_buffer[wr_pointer] <= wr_data;endend

我想提醒的是,此时写入的FIFO空间地址,应该是指针加1之前的地址值(指针值),这是因为使用了非阻塞赋值,指针即使加1了,在此刻时钟上升沿写FIFO时,加1的指针还未生效,这就是非阻塞赋值的作用了。
你不信吗?
按照上面说的,在仿真中,指针的值应该比写入FIFO中的地址值大1.
看看仿真图:


这本不是问题,可还是要提出来,就怕有的同学会迷!

刚才的Verilog设计对于写采用的是同步写,但是对于读却采用的是异步读,如果我们采用同步读呢?就和FIFO写数据达成统一,我们可以猜测(其实内心很确信),读指针值超越读数据地址1,也就是说,如果读地址在时钟上升沿为2的话,其实当前读的值为1地址的值。
那我们测试一下吧,先令读改为同步读:

// assign rd_data = fifo_buffer[rd_pointer];always@(posedge i_clk) beginif(rd_en) beginrd_data <= fifo_buffer[rd_pointer];endend

然后观测仿真结果:


数据的存取倒是没有问题,先进先出。
但可以看到的另一个情况是,1地址时,存的数据和取得数据其实都是0地址的数据。继续看仿真图:


可见,此时的地址虽然变成了1,但对于FIFO来说,并未生效,存以及取仍然按照前一个地址来存或取,这是非阻塞赋值的原因。
但这些细节问题,并不会影响我们使用FIFO,我们使用FIFO的时候不必关注这些,我们只需要只要我们存取都是先进先出即可。
封装成FIFO模块,用就是了!不过对于数字设计师来说,这种细节你还是要知道的,要不然用FIFO是没有灵魂的,还有就是如果面试或者笔试让你写一个FIFO你该怎么办呢?
既然是设计,你肯定要知道细节了,因为是你设计的细节。

VHDL版设计

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;entity module_fifo_regs_no_flags isgeneric (g_WIDTH : natural := 8;g_DEPTH : integer := 32);port (i_rst_sync : in std_logic;i_clk      : in std_logic;-- FIFO Write Interfacei_wr_en   : in  std_logic;i_wr_data : in  std_logic_vector(g_WIDTH-1 downto 0);o_full    : out std_logic;-- FIFO Read Interfacei_rd_en   : in  std_logic;o_rd_data : out std_logic_vector(g_WIDTH-1 downto 0);o_empty   : out std_logic);
end module_fifo_regs_no_flags;architecture rtl of module_fifo_regs_no_flags istype t_FIFO_DATA is array (0 to g_DEPTH-1) of std_logic_vector(g_WIDTH-1 downto 0);signal r_FIFO_DATA : t_FIFO_DATA := (others => (others => '0'));signal r_WR_INDEX   : integer range 0 to g_DEPTH-1 := 0;signal r_RD_INDEX   : integer range 0 to g_DEPTH-1 := 0;-- # Words in FIFO, has extra range to allow for assert conditionssignal r_FIFO_COUNT : integer range -1 to g_DEPTH+1 := 0;signal w_FULL  : std_logic;signal w_EMPTY : std_logic;beginp_CONTROL : process (i_clk) isbeginif rising_edge(i_clk) thenif i_rst_sync = '1' thenr_FIFO_COUNT <= 0;r_WR_INDEX   <= 0;r_RD_INDEX   <= 0;else-- Keeps track of the total number of words in the FIFOif (i_wr_en = '1' and i_rd_en = '0') thenr_FIFO_COUNT <= r_FIFO_COUNT + 1;elsif (i_wr_en = '0' and i_rd_en = '1') thenr_FIFO_COUNT <= r_FIFO_COUNT - 1;end if;-- Keeps track of the write index (and controls roll-over)if (i_wr_en = '1' and w_FULL = '0') thenif r_WR_INDEX = g_DEPTH-1 thenr_WR_INDEX <= 0;elser_WR_INDEX <= r_WR_INDEX + 1;end if;end if;-- Keeps track of the read index (and controls roll-over)        if (i_rd_en = '1' and w_EMPTY = '0') thenif r_RD_INDEX = g_DEPTH-1 thenr_RD_INDEX <= 0;elser_RD_INDEX <= r_RD_INDEX + 1;end if;end if;-- Registers the input data when there is a writeif i_wr_en = '1' thenr_FIFO_DATA(r_WR_INDEX) <= i_wr_data;end if;end if;                           -- sync resetend if;                             -- rising_edge(i_clk)end process p_CONTROL;o_rd_data <= r_FIFO_DATA(r_RD_INDEX);w_FULL  <= '1' when r_FIFO_COUNT = g_DEPTH else '0';w_EMPTY <= '1' when r_FIFO_COUNT = 0       else '0';o_full  <= w_FULL;o_empty <= w_EMPTY;-- ASSERTION LOGIC - Not synthesized-- synthesis translate_offp_ASSERT : process (i_clk) isbeginif rising_edge(i_clk) thenif i_wr_en = '1' and w_FULL = '1' thenreport "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS FULL AND BEING WRITTEN " severity failure;end if;if i_rd_en = '1' and w_EMPTY = '1' thenreport "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS EMPTY AND BEING READ " severity failure;end if;end if;end process p_ASSERT;-- synthesis translate_on
end rtl;

仿真就算了,和Verilog版一致也可。

带有几乎空almost empty 以及几乎满 almost full的同步FIFO
带有几乎空以及几乎满的同步FIFO设计也不是什么难事,我们只需要设置两个参数,几乎空以及几乎满的阈值,最后再将读写计数器和阈值对比,如果小于几乎空阈值,则几乎空标志有效;如果大于几乎满阈值,则几乎满标志有效。
设计十分简单,就在上述代码基础上添加几条,这里不再赘余。


参考资料

  • 参考资料1
  • 参考资料2

交个朋友

  • 个人微信公众号:FPGA LAB;

  • 知乎:李锐博恩。

  • FPGA/IC技术交流2020

FPGA基础知识极简教程(3)从FIFO设计讲起之同步FIFO篇相关推荐

  1. FPGA基础知识极简教程(10)二进制到BCD转换算法

    文章目录 写在前面 正文 快速认识 实现方式一 实现方式二 写在最后 写在前面 FPGA基础知识极简教程(9)讲到了七段数码管的显示Verilog设计,我们都知道,要在数码管上显示的数字,使用BCD编 ...

  2. FPGA基础知识极简教程(7)详解亚稳态与跨时钟域传输

    博文目录 写在前面 正文 FPGA或ASIC中的传播延迟 建立和保持时间是什么? 建立和保持时间与传播延迟和时钟频率有何关系? 如果违反建立和保持时间会发生什么? FPGA中的亚稳定是什么? 亚稳态何 ...

  3. FPGA基础知识极简教程(4)从FIFO设计讲起之异步FIFO篇

    博文目录 写在前面 正文 同步FIFO回顾 $clog2()系统函数使用 综合属性控制资源使用 异步FIFO设计 FIFO用途回顾 异步FIFO原理回顾 异步FIFO设计 异步FIFO仿真 参考资料 ...

  4. FPGA基础知识极简教程(9)七段数码管显示的Verilog简单设计

    博文目录 写在前面 正文 七段数码管原理 七段数码管译码表 单个七段数码管显示verilog设计 多个数码管动态扫描显示 参考资料 交个朋友 写在前面 作为FPGA的基础知识教程怎么能少得了这个简单的 ...

  5. FPGA基础知识极简教程(1)从布尔代数到触发器

    博文目录 写在前面 正文 初学者数字设计 什么是FPGA? 什么是ASIC? 数字设计师如何使用布尔代数? 使用查找表(LUT)在FPGA内部执行布尔代数 触发器如何在FPGA中工作? 参考资料 交个 ...

  6. FPGA基础知识极简教程(8)详解三态缓冲器

    博文目录 写在前面 正文 全双工与半双工 FPGA和ASIC中的三态缓冲器 如何在VHDL和Verilog中推断出三态缓冲区 参考资料 交个朋友 写在前面 下面用举例子的方式引出三态门,内容过长,大家 ...

  7. FPGA基础知识极简教程(6)UART通信与移位寄存器的应用

    博文目录 写在前面 正文 关于UART的介绍 UART通信过程 UART.RS232以及TTL之间的关系 UART的使用场合 有关UART的总结 调试UART的技巧 UART的Verilog实现 波特 ...

  8. FPGA基础知识极简教程(5)什么是锁存器以及如何在FPGA开发中避免生成锁存器?

    博文目录 写在前面 正文 什么是D锁存器? 锁存器是如何生成的? 如何避免生成锁存器? 参考资料 交个朋友 写在前面 个人微信公众号: FPGA LAB 个人博客首页 注:学习交流使用! 本文我们将讨 ...

  9. FPGA基础知识极简教程(2)抛却软件思维去设计硬件电路

    博文目录 写在前面 正文 可综合以及不可综合的代码 每个软件程序员需要了解的有关硬件设计的内容 参考资料 交个朋友 写在前面 相关博文 个人博客首页 注:学习交流使用! 学过一门或多门软件语言的数字设 ...

最新文章

  1. Socket / ServerSocket
  2. XSS盗COOKIE
  3. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16...
  4. 空客fctm避免已识别风险_最远可航行15000公里,南航首架空客A350飞机首航,将先飞广州-上海航线再飞国际...
  5. VTK:截锥体用法实战
  6. 17.QT-事件处理分析、事件过滤器、拖放事件
  7. [poj3261]Milk Patterns
  8. JavaScript学习笔记(八)
  9. 想赚钱,你对钱敏感么?
  10. CPU负载均衡之EAS
  11. 计算机组成原理——思维导图分享
  12. 局域网 服务器禁止共享文件夹,一键设置局域网共享文件夹权限,禁止他人打印...
  13. plc secs通讯协议_一种SECSGEM通讯协议转换的方法与流程
  14. PS零基础入门系列-PS图层样式案例实用技巧
  15. 设计模式二十四讲之《状态模式》
  16. Excel冻结窗口及设置下拉菜单
  17. 数据库——MySQL——完整性约束
  18. “哥”不信“神马浮云”
  19. 5G NR OFDM链路层仿真及Matlab代码实现(1):LDPC信道编译码之5G Tollbox中相关函数使用介绍
  20. Python——绑定与方法调用

热门文章

  1. 要学习的别人的博客网址---收藏
  2. mysql字符串相加函数concat()
  3. SIP穿越NATFireWall解决方案
  4. 169v 条目不存在_存在麒麟?牛顿烈焰激光剑理论!生活中的科学思维
  5. python网络开发框架_greenev首页、文档和下载 - Python网络服务框架 - OSCHINA - 中文开源技术交流社区...
  6. as cast float server sql_Sql Server中Float格式转换字符串varchar方法
  7. linux下redis安装教程,linux下安装配置redis图文详解
  8. 在我方某前沿防守地域 matlab,蒙特卡洛方法模拟小例子
  9. suse安装MySQL-python_python2.7.9安装mysql-python模块
  10. PHP邮件队列,php群发邮件,用数据库做邮件队列