0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

同步FIFO设计(下)

冬至子 来源:两猿社 作者:IC猿 2023-06-05 14:44 次阅读

00回顾

前面的文章我们讲了亚稳态的产生,是由于触发器在工作过程中存在数据的建立时间和保持时间。在上升沿触发电路中,建立时间就是在时钟上升沿到来之前,触发器数据保持稳定的最小时间;而保持时间就是在时钟上升沿到来之后,触发器数据端数据还应该保持的最小时间。如果数据在时钟上升沿前后的这个窗口内发生改变,即违反了建立保持时间,会使触发器工作在一个不稳定的状态,影响下一级触发器,发生连锁反应使整个系统工作失常。

异步FIFO是一种重要的异步时钟域的数据同步手段,在实际应用中非常常见,面试时也作为常考题型出现在视野中。但是如何设计一个高可靠性、高速的异步FIFO也是一个难点。

UART项目内部虽然只使用了同步FIFO实现数据的缓存,但咱们趁热打铁,将异步FIFO的原理和设计中需要关注的地方一并讲透,可以说干货满满,建议收藏~

01异步FIFO结构

图片

异步FIFO架构

异步FIFO主要是由 端口存储器写指针产生逻辑读指针产生逻辑空满标志产生逻辑4部分组成。读写操作是由两个完全不同时钟域的时钟所控制。在写时钟域部分,由写指针产生逻辑生成写端口所需要的写地址和写控制信号;在读时钟域部分,由读指针产生逻辑生成读端口所需要的读地址和读控制信号;在空满标志产生部分,通常是把写指针与读指针相互比较产生空满标志。读写时钟属于不同的时钟域,如何同步异步信号,使触发器不产生亚稳态及如何正确地设计空、满信号的控制电路,使FIFO不会溢出,造成数据丢失是异步FIFO设计的两个难点。

指针实现方式

  • 使用二进制方式实现指针

以写指针为例。在写请求有效时,写指针在时钟上升沿来时递增。同样在读请求有效时,读指针在读时钟沿来时递增。在产生空满信号时,需要比较读写指针,由于两个指针分属于不同的时钟域,彼此之间异步,在使用二进制计数器实现指针时, 可能会导致用于比较的指针采样错误

比如,二进制的值会从1111变为0000,这时所有位都会发生改变。虽然同步以后可以有效避免亚稳态,但是仍然可能得到错误的采样值。

从1111到0000转换可能的中间值:

1111 —> 0000

1111 —> 0001

1111 —> 0010

1111 —> 0011

1111 —> 0100

1111 —> 0101

1111 —> 0110

1111 —> 0111

1111 —> 1000

1111 —> 1001

1111 —> 1010

1111 —> 1011

1111 —> 1100

1111 —> 1101

1111 —> 1110

1111 —> 1111

如果同步时钟沿在1111向0000转换的中间某个位置到来,就可能将四位2进制的任何值采样同步到新的时钟域中。而FIFO空满信号产生使用错误的指针将产生误标志,从而使FIFO满时没有正确产生满标志,导致数据丢失;FIFO空时没有正确产生空标志,导致读出垃圾数据。

所以采用二进制方式实现指针不是最终的方案,建议尽量避免使用二进制计数产生指针。

** 使用格雷码方式实现指针

1.jpg

2.jpg

格雷码(gray)相对于二进制的优势在于:格雷码 从一个数变为下一个数时只有一位发生变化

所以格雷码在转换时最多只会出现一位错误。比如从1000变为0000时,两级同步器采样要么为1000(旧值),要么为0000(新值),而不会出现其他的值。这样就可以避免产生错误的空满标志。

在格雷码实现FIFO指针时需要注意, 对于写逻辑部分,产生写满信号需要对读指针(格雷码)进行同步;对于读逻辑部分,产生读空信号需要对写指针(格雷码)进行同步

有人要问了,使用两级同步器对指针同步时,不可避免的会产生两个时钟的延迟,这个延迟会不会对读写数据产生?别急,我会掰开来讲清楚的。我们先讲讲二进制和格雷码的相互转换。

  • 两种格雷码计数器

上面我们已经讲了格雷码这种编码方式可以有效的避免绝大部分错误。那怎么产生格雷码编码方式的读写指针呢?别看格雷码计数器看起来复杂,实现起来其实很简单。这里有两种方案产生读写指针。

方案A:

图片

方案A

步骤1:将格雷码转换为二进制值;

步骤2:根据条件递增二进制值;

步骤3:将二进制值转换为格雷码;

步骤4:将计数器的最终格雷码值保存到寄存器中。方案B:

图片

方案B

步骤1:根据条件递增二进制值;

步骤2:将二进制值保存到寄存器中,同时将二进制值转换为格雷码;

步骤3:将计数器的最终格雷码值保存到寄存器中。这两种方案有什么区别?

可以发现,方案A的读写指针产生必须经过相对复杂的二进制和格雷码的相互转换逻辑和递增逻辑,这条组合逻辑极有可能 限制FIFO的最高工作频率 ,成为关键路径。

方案B将二进制值的结果保存在一组额外的寄存器中,虽然增加了电路的面积(这点面积基本没有影响),但避免了复杂的组合逻辑,可以 提高系统工作频率

格雷码和二进制相互转换

  • 格雷码到二进制的转换

格雷码转换为二进制的公式为:

1.jpg

对于n位计数器来说,i

例如当n=4时,对应的格雷码到二进制的转换为:

bin[0] = gray[0] ^ gray[1] ^ gray[2] ^ gray[3]

bin[1] = gray[1] ^ gray[2] ^ gray[3]

bin[2] = gray[2] ^ gray[3]

bin[3] = gray[3]

可以看出,bin[3]是通过将格雷值右移3位得到;bin[2]是通过将格雷值右移2位得到;bin[1]是将格雷值右移1位得到;bin[0]是将格雷值右移0位得到。(右移后需按位异或)

下面是格雷码到二进制转换的Verilog代码。

module gray_to_bin(bin,gray);
parameter SIZE=4;
input  [SIZE-1:0]   gray;
output [SIZE-1:0]   bin;
reg    [SIZE-1:0]   bin;

integer i;
always @(*) begin
    for(i=0;i<=SIZE;i=i+1)
        bin = ^(gray >>i);
end
endmodule
  • 二进制到格雷码的转换

二进制到格雷码的转换公式为:

1.jpg

同样,对于n位计数器来说,i

例如当n=4时,对应的二进制到格雷码的转换为:

gray[0] = bin[0] ^ bin[1]

gray[1] = bin[1] ^ bin[2]

gray[2] = bin[2] ^ bin[3]

gray[3] = bin[3]

可以看出,可以通过逐位异或,或者将二进制码右移后与自身异或的操作,计算出对应的格雷码。

下面是二进制到格雷码转换的Verilog代码:

module bin_to_gray(bin,gray);
parameter SIZE=4;
input  [SIZE-1:0]   bin;
output [SIZE-1:0]   gray;

assign gray = (bin >>1) ^ bin;
endmodule

格雷码计数器实现

本格雷码计数器采用方案B实现,可有效提高FIFO的最大操作频率。所以不会涉及到格雷码到二进制的转换。

根据上文中格雷码和二进制码的特点,细心的同学可以发现,格雷码和二进制码的最高位是相同的,所以只需对低三位进行格雷码的转换。以写指针为例,具体实现方式如下:

parameter   SIZE=4;
reg  [SIZE:0] wbin,wbnext;
reg  [SIZE:0] wptr,wgnext;

always @(posedge wclk ornegedge rst_n) begin
    if(!rst_n) begin
        wbin           <= 'h0;
        wptr[SIZE-1:0] <= 'h0;
    end
    elsebegin
        wbin           <= wbnext;
        wptr[SIZE-1:0] <= wgnext[SIZE-1:0];
    end
end

always @(*) wptr[SIZE] = wbin[SIZE];

assign wbnext = !wfull ? wbin+winc : wbin;
assign wgnext[SIZE-1:0] = (wbnext[SIZE-1:0] >>1) ^ wbnext[SIZE-1:0];

指针同步后的影响

在FIFO中,FIFO写满时不应该再向FIFO中写数据,避免造成FIFO溢出,导致数据丢失。所以在写时钟域,需要将写指针和同步后的读指针进行比较,产生写满标志。下面我将举例说明将读指针同步到写时钟域后对写满标志和写数据的影响。

在最初的t0时刻,读写指针都为0。随着后续数据写入FIFO,写指针递增。当到达t5时刻时,FIFO写满,读写指针相等,写满信号wfull由0变为1。

如果在t6时刻发生了读操作,由于两级同步器包含两个触发器,将读指针同步到写时钟域会导致读指针在两个写时钟后出现,写满信号wfull在t6和t7时刻都为1,t7时刻后变为0。这虽然增加了阻止数据写入的周期(2 wclk cycle),但是对数据的准确性无任何影响。

图片

空满逻辑同步后效果

类似的,在FIFO空时也会阻止FIFO的读操作。

FIFO的读空信号产生是把写指针同步到读时钟域和读指针进行比较。

在t10时刻,写指针等于读指针,此时FIFO为空。若t11时刻时开始向FIFO写入数据,t11,t12时刻虽然FIFO不为空,但是由于两级同步器的延时,此时读空信号rempty仍然为1,这会阻止FIFO的读操作,但这是无害的。在t12时刻后,rempty释放,此时FIFO可以开始读操作。

在将满时通知写的一边FIFO已满,在将空时通知读的一边FIFO已空都是可以的,即使同步后的指针存在延时,阻止写/读的影响会使FIFO挂起一段时间,但不会导致任何错误。

空满信号产生

空满标志的产生是异步FIFO设计的核心。如何正确设计此部分的逻辑,会直接影响到FIFO的性能。空满标志的产生原则是: 写满不溢出,读空不多读

最直接的做法是采用读写指针相比较来产生空满标志。当读写指针的差值等于一个预设值时,空满信号被置位。这种实现方式逻辑简单,但是它的减法器形成了一个比较大的组合逻辑。限制了FIFO的速度。

所以一般采用相等或是不相等的比较逻辑,避免使用减法器。采用直接比较的方法必须区分当读写地址相等的时候是空还是满,所以必须增加额外的1位控制信号来区分空满标志。

以16x16异步FIFO为例,写指针wptr[4:0],读指针rptr[4:0],其中低三位为真正的读写地址,最高位用来区分空满。

若读写指针完全相等,表示读地址追上了写地址,此时FIFO为空;若最高位相反但其余位相同,表示写地址领先读地址一个周期,此时FIFO为满。

读写指针比较产生空满标志rempty和wfull,将写指针同步到读时钟域,产生读空信号rempty控制异步FIFO的读操作;将读指针同步到写时钟域,产生写满信号wfull控制异步FIFO的写操作。

空满信号的Verilog实现如下:

wire            wfull;
wire            rempty;
reg  [SIZE:0]   wptr_reg1;
reg  [SIZE:0]   wptr_reg2;
reg  [SIZE:0]   rptr_reg1;
reg  [SIZE:0]   rptr_reg2;

// 写指针在读时钟域的两级同步
always @(posedge rclk ornegedge rst_n) begin
    if(!rst_n) begin
        wptr_reg1 <= 'h0;
        wptr_reg2 <= 'h0;
    end
    elsebegin
        wptr_reg1 <= wptr;
        wptr_reg2 <= wptr_reg1;
    end
end
// 读指针在写时钟域的两级同步
always @(posedge wclk ornegedge rst_n) begin
    if(!rst_n) begin
        rptr_reg1 <= 'h0;
        rptr_reg2 <= 'h0;
    end
    elsebegin
        rptr_reg1 <= rptr;
        rptr_reg2 <= rptr_reg1;
    end
end

//空满标志产生
assign wfull  = ({!wptr[SIZE],wptr[SIZE-1:0]}==rptr_reg2)? 1'b1 : 1'b0;
assign rempty = (wptr_reg2==rptr)? 1'b1:1'b0;

好了,异步FIFO到这里讲完了,大家可以试试自己写一个异步FIFO,调整读写时钟的频率和比例,进行前后仿真看看性能怎样呢~

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7151

    浏览量

    162002
  • 控制电路
    +关注

    关注

    81

    文章

    1609

    浏览量

    134932
  • 触发器
    +关注

    关注

    14

    文章

    1685

    浏览量

    60412
  • FIFO存储
    +关注

    关注

    0

    文章

    102

    浏览量

    5895
  • 异步时钟
    +关注

    关注

    0

    文章

    17

    浏览量

    9366
收藏 人收藏

    评论

    相关推荐

    同步FIFO设计详解及代码分享

    FIFO (先入先出, First In First Out )存储器,在 FPGA 和数字 IC 设计中非常常用。 根据接入的时钟信号,可以分为同步 FIFO 和异步 FIFO
    发表于 06-27 10:24 1325次阅读
    <b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>设计详解及代码分享

    握手型接口的同步FIFO实现

    按照正常的思路,在前文完成前向时序优化和后向时序优化后,后面紧跟的应该是双向时序优化策略了,不过不急,需要先实现一下握手型同步FIFO
    的头像 发表于 12-04 14:03 299次阅读
    握手型接口的<b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>实现

    基于VHDL和FPGA的非对称同步FIFO设计实现

    本文采用VHDL描述语言,充分利用Xilinx公司Spartan II FPGA的系统资源,设计实现了一种非对称同步FIFO,它不仅提供数据缓冲,而且能进行数据总线宽度的转换。
    发表于 01-13 11:33 1778次阅读

    同步FIFO设计

    发表于 11-19 11:58 12次下载

    同步FIFO之Verilog实现

    FIFO的分类根均FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO
    的头像 发表于 11-01 09:57 1359次阅读

    怎样设计一个同步FIFO?(1)

    今天咱们开始聊聊FIFO的设计。FIFO是一个数字电路中常见的模块,主要作用是数据产生端和接受端在短期内速率不匹配时作为数据缓存。FIFO是指First In, First Out,即先进先出,跟大家排队一样。越早排队的人排在越
    的头像 发表于 05-04 15:48 578次阅读

    怎样设计一个同步FIFO?(2)

    开始往下读之前,老李先问一个问题,假如现在让你设计一个深度为N的基于2port SRAM的同步FIFO,请问至少需要多大的SRAM? 假设SRAM的位宽就是你的数据宽度,那么问题就是问你需要的SRAM的行数至少是多少?如果你觉得答案是显而易见的N,那么你值得读完这一篇。
    的头像 发表于 05-04 15:55 680次阅读
    怎样设计一个<b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>?(2)

    怎样设计一个同步FIFO?(3)

    我们说这个结构之所以使得FIFO的输出Q在读完SRAM之后保持稳定,其实需要SRAM本身可以保持RDATA在读操作之后的多个周期保持稳定。即SRAM本身的读时序如下图所示:图中cycle 4,5,6都没有读操作,SRAM的RDATA依然保持D0不变。
    的头像 发表于 05-04 15:59 432次阅读
    怎样设计一个<b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>?(3)

    FIFO设计—同步FIFO

    FIFO是异步数据传输时常用的存储器,多bit数据异步传输时,无论是从快时钟域到慢时钟域,还是从慢时钟域到快时钟域,都可以使用FIFO处理。
    发表于 05-26 16:12 1068次阅读
    <b class='flag-5'>FIFO</b>设计—<b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>

    同步FIFO设计(上)

    FIFO,First In First Out,先入先出队列,顾名思义,即第一个到达的数据也将会是第一个离开。
    的头像 发表于 06-05 14:39 582次阅读
    <b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>设计(上)

    一个简单的RTL同步FIFO设计

    FIFO 是FPGA设计中最有用的模块之一。FIFO 在模块之间提供简单的握手和同步机制,是设计人员将数据从一个模块传输到另一个模块的常用选择。
    发表于 06-14 08:59 259次阅读

    基于寄存器的同步FIFO

      FIFO 是FPGA设计中最有用的模块之一。FIFO 在模块之间提供简单的握手和同步机制,是设计人员将数据从一个模块传输到另一个模块的常用选择。 在这篇文章中,展示了一个简单的 RTL
    的头像 发表于 06-14 09:02 492次阅读

    基于Verilog的同步FIFO的设计方法

    同步FIFO的设计主要包括读写地址的产生、数据的读写、以及状态的控制。下面我们将分别介绍这三个方面的设计。
    发表于 08-31 12:53 304次阅读

    同步FIFO设计分析

    模块虽小但是要有新意,首先写一个同步FIFO,这是一个烂大街的入门级项目,但是我肯定不会写的那么简单
    的头像 发表于 09-11 17:11 367次阅读
    <b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>设计分析

    同步FIFO和异步FIFO的区别 同步FIFO和异步FIFO各在什么情况下应用

    同步FIFO和异步FIFO的区别 同步FIFO和异步FIFO各在什么情况下应用? 1.
    的头像 发表于 10-18 15:23 1016次阅读