首页 > 编程知识 正文

verilog可综合fifo,异步fifo调用ip核

时间:2023-05-04 23:07:40 阅读:63717 作者:1741

1 .异步FIFO概念

异步FIFO使用不同的时钟进行读取和写入,异步FIFO用于在不同的时钟域传输数据,主要用于在时钟域之间传输多位数据。

2 .异步FIFO的设计难点

同步异步信号,避免准稳态数据的危害

设计适当的FIFO指针,判断FIFO是满还是空

同步FIFO指针

同步FIFO有一个计数器,用于计数存储和读取的数量。 如果FIFO仅写入操作而不进行读取操作,则计数值增加,如果FIFO仅读取操作而不进行写入操作,则计数值减少,如果没有读取操作或同时进行读取操作,则计数值不变化。 当计数器计数值达到FIFO全长时,FIFO满,当计数值为0时,FIFO为空。

但是,异步FIFO设计不能使用一个计数器来计数FIFO的数据数。 这是因为两个异步时钟必须控制同一计数器。 因此,在设计异步FIFO时,需要比较FIFO的读写指针的位置。

4 .指向异步FIFO的指针

异步FIFO的写入指针始终指向下一个要写入的地址,复位或清除时读写指针指向FIFO缓冲区的0地址。 同一读取指针始终指向在当前FIFO中有要读取的数据的地址,在空或复位时指针指向0,一旦发生读取操作,读取指针指向的地址的数据被读取,指针地址增加。

FIFO空标志发生在读指针和写指针相等的情况下。 读写指针复位为0时、进行了清空FIFO等操作时,或者读取指针赶上写入指针时。

当读取指针和写入指针相等时,FIFO的标志也会变满。 写入指针已经绕了一圈,赶上了读取指针时。 因此,存在读写指针相等时,如何判断是读空还是写满的问题。

为了区分是读空还是写满空,可以向读写指针添加额外的位。 写入指针增加后,如果超过了FIFO存储区的最后一个地址,则添加指针会反转添加的最高有效位。 同样的读取指针也执行同样的操作。 因此,在两个最高位不同的情况下,可以判断为写入指针正赶上读取指针,FIFO处于写入完全状态,如果最高位相同,则读取指针正赶上写入指针,FIFO处于读取空状态

添加图MSB以区分写入和读取

5 .读写指针计数器设计

对于二进制计数器,如果计数值从一个时钟域同步到另一个时钟域,则很危险。 因为n位数据有时会同时发生变化。 例如,如果与7到8计数对应的二进制数为0111到1000计数,则所有对应的位都会发生变化。

解决FIFO计数器问题的一个常见方法是使用格雷码进行计数。 对于两个相邻计数值,格雷码只能更改一个位。 这解决了希望在同一时钟沿同步多个bits的问题。

6 .引入格雷码计数器问题

考虑4位和3位格雷码计数器,如图2所示。 通过分析二进制代码,可以对4位格雷码的上半部分和下半部分为最高位的反向雄辩位进行中点镜像。 用于区分写入和读取,将4位格雷码转换为3位格雷码,同时将最高位转换为附加位。 我们希望4位格雷码的后3位重复第一部分的后3位,而不是关于中点的镜像。 如果将下半部分的后三位格雷码代码直接更改为上半部分的后三位一样的代码,则真正的格雷码不应该从7更改为8和15,而是从0。 真正的格雷码应该在两个相邻计数之间只有一个位发生了变化。

图24位格雷码

7 .格雷码计数器的设计

因为格雷码计数器不存在奇数长度,所以设计的额头FIFO深度为。 二进制计数器在每次自相加时检测是满还是空,以避免出现上溢或下溢。

在图3所示的格雷码计数器中,能够将二进制计数器的低位(n-1 )位直接作为FIFO存储单元的地址指针,将二进制变换为格雷码并传送到其他时钟字段。

图3格雷码计数器

8 .将格雷码转换为二进制和二进制格雷码

二进制B[n:0]被转换为格雷码G[n:0]

G[n]=B[n]//保留最高有效位作为格雷码最高有效位

g [ n-1:0 ]=b [ n-1:0 ] ^ b [ n :1 ]//下一个高位格雷码为二进制码的高位与下一个高位不同或具有其他相似性

格雷码G[n:0]被转换为二进制B[n:0]

B[n]=G[n]//保留最高有效位作为二进制码的最高有效位

b [ n-1:0 ]=g [ n-1:0 ] ^ b [ n :1 ]//下一个高位格雷码为二进制码的高位与下一个高位不同或类似

9 .实现先进先出

将地址指针转换为格雷码时,对于两个相邻地址,只有一个位数据发生变化,因此可以使用触发器同步异步时钟的数据。

图4 FIFO的结构框图

10 .生成满空信号

读空信号在读时钟域中生成,保证了读空信号能够实时无延迟地确定。 发生读空时,读指针会赶上写指针,两个指针的值相同。 也包括扩展的最高有效位。 因为此时的格雷码也一样,所以设计比较简单。

在FIFO设计中,在光时钟区域生成光全部信号,能够实时且无延迟地确定光全部。 当写入指针赶上读取指针时发生

,此时两个指针的二进制计数器的低(n-1)位相同最高位不同。但是由于扩展了格雷码,而对于n位格雷码其(n-1)位是关于中间值镜像对称的。

图 5 有扩展位的格雷码编码

考虑图 5对于3位的格雷码地址添加一个附加位用于区分写满与读空成为4位的格雷码编码。当FIFO空时即读指针赶上写指针,此时两个格雷码完全相同(包括扩展位)。当FIFO写满时候需要考虑如下3个条件

    写指针的格雷码与同步到写时钟域的读指针格雷码的最高位不同

    写指针的格雷码与同步到写时钟域的读指针格雷码的次高位不相等

    写指针的格雷码与同步到写时钟域的读指针格雷码的其余位都相等

11.    不同的时钟速率同步的深入思考

当异步FIFO来同步两个不同的时钟域的时候,显然两个时钟的速度是不一样的,考虑当一个快的时钟域的信号同步到慢的时钟域的时候可能会存在计数值跳跃,因为在慢的时钟域的一个周期快的时钟域的计数值可能已经增加了多次了,因此就会导致如下的两个问题:

    在同步格雷码的时候如果格雷码的值增加了2个然而只采样了一次就会出现多个位的数据发生变化,这种情况下会导致多bit数据同步问题吗?

    答案是否定的。在同步多bit数据的时候当多个数据位在同步上升沿变化时会出现问题。但是对于快的时钟域的写指针的格雷码在一个时钟周期只改变一位,因此在慢的时钟域周围最多只会有一个位发生变化。

    在慢的时钟域一个周期里面快的时钟域的计数器可能已经增加了几个计数值了,那么会存在快的时钟域的已经从满状态增加到(满+1)的状态,而没能检测出满吗?

    答案是否定的,应为满状态是在写时钟域产生的,如果写的时钟比读取的时钟快,当写指针赶上从读时钟域同步过来的读指针后,满状态会马上置位,因此就不会出现溢出。

满与空状态能够准确的被置位,但是标志位清除有一定的延迟。

当我们在写时钟域产生FIFO满的状态,当写指针追赶上读指针的时候马上产生满标志,此时当读指针增长了以后FIFO就不再满了,但是写时钟域不能马上检测到读指针已经增加了,需要经过两个时钟周期,使得读指针的数据同步到写时钟域以后才能够清空满状态。这样能够保证FIFO不会溢出,相同的读FIFO也存在这样的问题。

/*异步fifo 参考文献 Simulation and Synthesis Techniques for Asynchronous FIFO Design*/module async_fifo(rst_n,fifo_wr_clk,fifo_wr_en,r_fifo_full,fifo_wr_data,fifo_rd_clk,fifo_rd_en,fifo_rd_data,r_fifo_empty//fifo_wr_err,//fifo_rd_err);input rst_n;input fifo_wr_en;input[15:0]fifo_wr_data;input fifo_rd_en;input fifo_rd_clk;input fifo_wr_clk;output reg r_fifo_full;output [15:0]fifo_rd_data;output reg r_fifo_empty;//output reg fifo_wr_err;//output reg fifo_rd_err;reg[9:0] rdaddress; //RAM地址为9位地址 扩展一位用于同步reg[9:0] wraddress;wire[9:0]gray_rdaddress;wire[9:0]gray_wraddress;/*同步寄存器*/reg[9:0] sync_w2r_r1,sync_w2r_r2;reg[9:0] sync_r2w_r1,sync_r2w_r2;wire fifo_empty;wire fifo_full;/*二进制转化为格雷码计数器*/assign gray_rdaddress = (rdaddress >>1) ^ rdaddress;//(({1'b0,rdaddress[9:1]}) ^ rdaddress);/*二进制转化为格雷码计数器*/assign gray_wraddress = (({1'b0,wraddress[9:1]}) ^ wraddress);assign fifo_empty = (gray_rdaddress == sync_w2r_r2);assign fifo_full = (gray_wraddress == {~sync_r2w_r2[9:8],sync_r2w_r2[7:0]});////assign fifo_wr_err = (w_fifo_full && fifo_wr_en);//assign fifo_rd_err = (fifo_empty && fifo_rd_en);ram ram(.data(fifo_wr_data),.rdaddress(rdaddress[8:0]),.rdclock(fifo_rd_clk),.wraddress(wraddress[8:0]),.wrclock(fifo_wr_clk),.wren(fifo_wr_en),.q(fifo_rd_data));/*在读时钟域同步FIFO空 sync_w2r_r2 为同步的写指针地址 延迟两拍 非实际 写指针值 但是确保不会发生未写入数据就读取*/always@(posedge fifo_rd_clk or negedge rst_n)if(!rst_n)r_fifo_empty <= 1'b1;else r_fifo_empty <= fifo_empty;/*在写时钟域判断FIFO满 sync_r2w_r2 实际延迟两个节拍 可能存在非满判断为满 但不会导致覆盖*/always@(posedge fifo_wr_clk or negedge rst_n)if(!rst_n)r_fifo_full <= 1'b1;else r_fifo_full <= fifo_full;//格雷码判断追及问题/*读数据地址生成*/always@(posedge fifo_rd_clk or negedge rst_n)if(!rst_n)rdaddress <= 10'b0;else if(fifo_rd_en && ~fifo_empty)beginrdaddress <= rdaddress + 1'b1;end/*写数据地址生成*/always@(posedge fifo_wr_clk or negedge rst_n)if(!rst_n)wraddress <= 10'b0;else if(fifo_wr_en && ~r_fifo_full)beginwraddress <= wraddress + 1'b1;end/*同步读地址到写时钟域*/always@(posedge fifo_wr_clk or negedge rst_n)if(!rst_n)beginsync_r2w_r1 <= 10'd0;sync_r2w_r2 <= 10'd0;end else beginsync_r2w_r1 <= gray_rdaddress;sync_r2w_r2 <= sync_r2w_r1;end/*同步写地址到读时钟域, 同步以后 存在延迟两个节拍*/always@(posedge fifo_rd_clk or negedge rst_n)if(!rst_n)beginsync_w2r_r1 <= 10'd0;sync_w2r_r2 <= 10'd0;end else beginsync_w2r_r1 <= gray_wraddress ;sync_w2r_r2 <= sync_w2r_r1;endendmodule

 

 

参考文献 Simulation and Synthesis Techniques for Asynchronous FIFO Design

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。