前面分享了一篇基于ise的fifo实现,主要是调用了ip核,并且设置了一系列标志符号。这篇博文主要是通过调用一个双口的ram IP核,编写相对应的读和写控制模块来实现一个异步fifo。
逻辑框图如下图所示:
在读控制模块中,没有加入读使能,设置为给地址就出数据。
调用的ram核为宽度为8,深度为256,双口ram,截图如下:
ram的写使能为:
读空与写满信号的产生:
写满信号w_full: 当写地址比读地址快一圈时,此时写指针=读指针,w_full=1;
读空信号r_empty: 当复位时,读地址=写地址,r_empty=1;
当读地址追上写地址时,写地址=读地址,r_empty=1
可见,读空与写满均是两者相等,所以需要相应的方法来进行判别到底属于哪种情况。
举个例子:存储器深度为8,所以仅需要3位地址即可,当读地址r_addr=3'b100,写地址w_addr=3'b100时,不知道到底是写满还是读空,这就出现了混淆的现象。所以,可以在3位地址的基础上增加一位作为最高位,用最高位的符号来进行判别是读空还是写满:低位相等且最高位相等相当于读追上了写,为读空;低位相等而最高位不同相当于写满。即r_addr=4'b0100,w_addr=4'b0100时,读指针追上了写指针,读空;r_addr=4'b1100,w_addr=4'b0100,写指针领先于读指针一圈,写满。
判断代码如上图所示。
由于在异步FIFO设计中,读写时钟不同需要涉及到跨时钟域的问题,此时采用二进制来进行读写地址的比较是不合适的。比如二进制的7=0111,8=1000,在由7变化到8时,每一位都会发生跳变,这样的话在地址同步到另一个时钟域的时候很大概率会发生错误。
于是,可以用格雷码来解决这个问题:
格雷码编码的好处是相邻两个数只有1位不同,所以在转换时不会像上面那种情况发生大的跳变,这样就可以极大程度上减少跨时钟域产生的影响。格雷码产生方法:本二进制数右移一位异或本二进制数,即移位并异或。
举个例子:十进制数 8
二进制数 1000
右移一位 0100
异或结果 1100
这样异或结果1100就为十进制数8的格雷码表示。
代码如下:
以上主要介绍了读空写满信号、二进制码、格雷码的产生。
跨时钟域产生格雷码读写地址模块框图:
下面是各个模块的产生:
写控制模块:
其中需要将读模块过来的读指针进行打两拍进行同步,尽可能减小亚稳态的影响。
读控制模块:
fifo memory模块:
仿真结果如下图: