Verilog设计异步FIFO
转自http://ninghechuan.com
异步FIFO有两个异步时钟,一个端口写入数据,一个端口读出数据。通常被用于数据的跨时钟域的传输。
同步FIFO的设计。一个时钟控制一个计数器,计数器增加(只写不读),计数器减少(只读不写),计数器保持(不写不读)。计数器为0时,FIFO空,计数器为你定义的最大值,FIFO为满。貌似较容易设计。
很遗憾的是,异步FIFO并不能用这样的思想,因为异步FIFO有两个时钟,并没有办法控制一个计数器读写操作。只能分开读写计数器,通过比较读写指针的值来判断空满状态。
异步FIFO的读写指针
复位时,读写指针都为0。
写指针总是指向下一个要被写入的单元。复位时,写指针为0,所以指定的就是要被写入的0单元,当进行一次写操作,指针会自动加1,指向下一个要被写入的单元。
同样的,读指针总是指向当前要读的单元。复位时,读指针为0。当复位释放,这个时候如果有读使能读数据,是无效的,因为FIFO是空的。只有当写入数据后,FIFO空信号(empty)拉低时,才能读出有效的数据。
FIFO空满标志判断

当读指针和写指针相等的时候,可以认为FIFO空,认为是读指针追上了写指针,FIFO被读空了。当写指针追上读指针时,写数据比读数据快,并且写了一圈又写满了读空的一段,读写指针又相等。这样怎么能判断到底是FIFO满还是FIFO空呢?
通过读写指针位宽都多1bit的方法解决这个问题。读写指针最高位不会表示FIFO的地址深度,仅用来判断FIFO空满标志。即用N位标志读写指针的位宽,剩下的N-1 bit 表示FIFO读写深度。可以得出:
空标志:读指针等于写指针。

满标志:读指针和写指针的最高位不同,其余位相等。

if(waddr == raddr) -> empty
if({~waddr[N-1], waddr[N-2:0]} == raddr) -> full
FIFO的二进制读写指针考虑
异步FIFO的读写指针是在两个异步时钟下分别进行计数的,要判断空满标志就需要将两个指针进行比较,肯定需要将两个信号同步到一个时钟域下进行比较。这个过程是容易出现问题的,因为通常情况下的二进制计数器,可能是多个位同时变化的,例如从7-8(0111-1000)。同一个时钟沿同步多个信号可能会产生亚稳态。
采用Gray码(格雷码)可以解决这个问题,因为Gray码计数值增加,每次只变化一位。所以将一个地址指针转化为Gray后,再同步到另一个时钟域,进行比较得出空满信号。
二进制转换Gray码
assign graydata = (bindata >> 1) ^ bindata;
FIFO设计框图

从上图可以看到最中间是这个FIFO的缓存部分用一个双口RAM可以实现。
左边的模块来产生FIFO的写指针和FIFO满标志,右边的模块来产生FIFO的读指针和FIFO空标志。
本设计中的FIFO 满标志在写时钟域下产生,这样在FIFO写满后能立即检测到并产生full标志。所以需要将读指针同步到写时钟域下进行比较。
FIFO空标志在读时钟域下产生,这样FIFO写空后能立即检测到并产生empty标志,所以需要将写指针同步到读时钟域下进行比较。
框图最下面有两个同步模块,便是实现读写指针的同步操作。
空满标志判断问题

前面说过判断空满标志的方法
空标志:读指针等于写指针。
assign empty_val = (rd_cnt == wr_cnt_r);
always @(posedge rclk or negedge rst_n)begin
if(!rst_n)
fifo_empty <= 0;
else
fifo_empty <= empty_val;
end
满标志:读指针和写指针的最高位不同,其余位相等。
这种方法在二进制计数器下是完全没有问题的,但在格雷码表示下判断满标志就有问题了。如上图,四位格雷码,7(0_100)和8(1_100)的低三位是完全一样的,如果读地址为0_100,读地址为1_100,这个时候按照上文的说法,应该是FIFO写满了,但是真的是这样吗?这就是问题所在。
解决方法就是在判断FIFO满标志时,不仅判断最高位不同,次高位也应该不同,其余为相等,这个时候FIFO处于满状态。

根据四位格雷码可以推论出如上结论。
//assign full_val = ((wr_cnt[ADDR_SIZE] != rd_cnt_r[ADDR_SIZE]) && // (wr_cnt[ADDR_SIZE-1] != rd_cnt_r[ADDR_SIZE-1]) && // (wr_cnt[ADDR_SIZE-2:0] == rd_cnt_r[ADDR_SIZE-2:0])); //上一行代码可简化为 assign full_val = (wr_cnt == {~rd_cnt_r[ADDR_SIZE: ADDR_SIZE-1], rd_cnt_r[ADDR_SIZE-2:0]});
always @(posedge wclk or negedge rst_n)begin
if(!rst_n)
fifo_full <= 0;
else
fifo_full <= full_val;
end
解决了空满标志的这个棘手的问题,FIFO其余部分设计就可以随即设计,当然本设计仅为初学者学习,了解FIFO的基本原理。本文学习自Reference的这个paper,欢迎讨论交流。
代码博主自己尝试着写了一个,仿真了下修改了很多问题,但肯定有bug还没解决。
我放在Github上了,处于学习的学生朋友可以一起讨论。
https://github.com/NingHeChuan/Silicon_Peasant
Reference
Simulation and Synthesis Techniques for Asynchronous FIFO Design——Clifford E. Cummings, Sunburst Design, Inc.
Verilog设计异步FIFO的更多相关文章
- 异步fifo的设计
本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据) ...
- 异步fifo的Verilog实现
一.分析 由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决? 跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO ...
- 基于FPGA的异步FIFO设计
今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域.由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出).这里的读写指针是异步的, ...
- 异步fifo的设计(FPGA)
本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据) ...
- 异步FIFO总结+Verilog实现
异步FIFO简介 异步FIFO(First In First Out)可以很好解决多比特数据跨时钟域的数据传输与同步问题.异步FIFO的作用就像一个蓄水池,用于调节上下游水量. FIFO FIFO是一 ...
- 怎么用Verilog语言描述同步FIFO和异步FIFO
感谢 知乎龚大佬 打杂大佬 网上几个nice的博客(忘了是哪个了....) 前言 虽然FIFO都有IP可以使用,但理解原理还是自己写一个来得透彻. 什么是FIFO? Fist in first out ...
- 异步FIFO空满设计延迟问题
由于设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢? 异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域 ...
- 异步FIFO及verilog原码
这几天看了Clifford E. Cummings的两篇大作<Simulation and Synthesis Techniques for Asynchronous FIFO Design&g ...
- 异步FIFO的verilog实现与简单验证(调试成功)
最近在写一个异步FIFO的时候,从网上找了许多资料,文章都写的相当不错,只是附在后面的代码都多多少少有些小错误. 于是自己写了一个调试成功的代码,放上来供大家参考. 非原创 原理参考下面: 原文 ht ...
随机推荐
- C# winform三种定时方法
1. 直接用winform 的 timers 拖控件进去 代码 public partial class Form1 : Form { public Form1() ...
- Python中DataFrame去重
# 去除重复行数据 keep:'first':保留重复行的第一行,'last':保留重复行的最后一行,False:删除所有重复行df = df.drop_duplicates( subset=['YJ ...
- 洗礼灵魂,修炼python(18)--温故加知新
类型转换: 1.str(),repr(),format():将非字符串数据转换为字符串 str():对象序列化的结果,相当于print输出 repr():程序中某个对象精确值 format():利用特 ...
- sqlserver序列定时初始化
1.创建序列 2.序列初始化存储过程 create procedure proDemo as begin alter sequence dbo.序列名 restart with 0; end 3.创建 ...
- java基础-温故而知新(02)
基本数据的自动拆装箱及享元设计模式 1.1 自动装箱 -128~127 之间的整数,装在一个内存区域. 超过这个范围的整数,装在不同的内存区域. 1.2 自动拆箱 ...
- python3的C3算法
一.基本概念 1. mro序列 MRO是一个有序列表L,在类被创建时就计算出来. 通用计算公式为: mro(Child(Base1,Base2)) = [ Child ] + merge( mro(B ...
- 用navicat手动删除了数据表的记录,再次写入的时候,怎么让id重新从1开始?
问:用navicat手动删除了mariadb数据表的记录,再次写入的时候,自增id会继续,不会从1开始. 比如,原来有10条记录,全部清空,再次写入数据,id会从11开始,怎么让他重新从1开始呢? 重 ...
- [ ArcGIS for Server 10.1 系列 ] - 重新创建Site
一般当ArcGIS Server Site发生错误.ArcGIS Server无法启动或者ArcGIS Server某服务没有实例,就可能需要重新的创建Site.有时可以通过重新创建Site,就发现其 ...
- 原生JS添加类名 删除类名
为 <div> 元素添加 class: document.getElementById("myDIV").classList.add("mystyle&quo ...
- CF848C:Goodbye Souvenir(CDQ分治)
Description 给定长度为$n$的数组, 定义数字$X$在$[l,r]$内的值为数字$X$在$[l,r]$内最后一次出现位置的下标减去第一次出现位置的下标给定$m$次询问, 每次询问有三个整数 ...