Verilog设计异步FIFO

转自http://ninghechuan.com

异步FIFO有两个异步时钟,一个端口写入数据,一个端口读出数据。通常被用于数据的跨时钟域的传输。

同步FIFO的设计。一个时钟控制一个计数器,计数器增加(只写不读),计数器减少(只读不写),计数器保持(不写不读)。计数器为0时,FIFO空,计数器为你定义的最大值,FIFO为满。貌似较容易设计。

很遗憾的是,异步FIFO并不能用这样的思想,因为异步FIFO有两个时钟,并没有办法控制一个计数器读写操作。只能分开读写计数器,通过比较读写指针的值来判断空满状态。

异步FIFO的读写指针

复位时,读写指针都为0。

写指针总是指向下一个要被写入的单元。复位时,写指针为0,所以指定的就是要被写入的0单元,当进行一次写操作,指针会自动加1,指向下一个要被写入的单元。

同样的,读指针总是指向当前要读的单元。复位时,读指针为0。当复位释放,这个时候如果有读使能读数据,是无效的,因为FIFO是空的。只有当写入数据后,FIFO空信号(empty)拉低时,才能读出有效的数据。

FIFO空满标志判断

image

当读指针和写指针相等的时候,可以认为FIFO空,认为是读指针追上了写指针,FIFO被读空了。当写指针追上读指针时,写数据比读数据快,并且写了一圈又写满了读空的一段,读写指针又相等。这样怎么能判断到底是FIFO满还是FIFO空呢?

通过读写指针位宽都多1bit的方法解决这个问题。读写指针最高位不会表示FIFO的地址深度,仅用来判断FIFO空满标志。即用N位标志读写指针的位宽,剩下的N-1 bit 表示FIFO读写深度。可以得出:

空标志:读指针等于写指针。

image

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

image

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设计框图

image

从上图可以看到最中间是这个FIFO的缓存部分用一个双口RAM可以实现。

左边的模块来产生FIFO的写指针和FIFO满标志,右边的模块来产生FIFO的读指针和FIFO空标志。

本设计中的FIFO 满标志在写时钟域下产生,这样在FIFO写满后能立即检测到并产生full标志。所以需要将读指针同步到写时钟域下进行比较。

FIFO空标志在读时钟域下产生,这样FIFO写空后能立即检测到并产生empty标志,所以需要将写指针同步到读时钟域下进行比较。

框图最下面有两个同步模块,便是实现读写指针的同步操作。

空满标志判断问题

image

前面说过判断空满标志的方法

空标志:读指针等于写指针。

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处于满状态。

image

根据四位格雷码可以推论出如上结论。

//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.

猜你喜欢

转载自www.cnblogs.com/lionsde/p/10597054.html