mir_cs的个人空间 https://blog.eetop.cn/1826108 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

2024-08-14

热度 1已有 112 次阅读| 2024-8-14 17:12 |个人分类:一些设计概念探索|系统分类:芯片设计


异步fifo设计(1/N)

直接请出Cummings大牛的论文吧。这篇日志简要翻译一下Cummings大神的“Simulation and Synthesis Techniques for Asynchronous FIFO Design”,也当训练一下专业英语水平。

先简要说一下FIFO是什么。

FIFO(First-in-First-out Register,先入先出寄存器)通常指一个寄存器(register)阵列,该阵列可以实现写入数据和读出数据的功能,且先写入的数据会在其中储存,读出时会优先读出。FIFO基本原理是利用二进制加法溢出循环使用读指针和写指针进行读取和存储。写指针每写入一个数据就+1,读指针亦然。

1723605142760793.png



异步先入先出寄存器(FIFO)设计的仿真与综合

作者:Clifford E. Cummings, Sunburst Design, Inc. cliffc@sunburst-design.com

摘要

FIFO经常被用于从一个时钟域向另一个异步时钟域安全传递数据,而使用FIFO从一个时钟域传递数据到另外一个时钟域需要多个异步时钟设计技术。错误设计FIFO的方法数不胜数,而正确设计FIFO但难以综合和分析的设计亦是多如牛毛。

这篇文章将详细介绍一种用于设计、分析和综合跨时钟域且能保证传输安全的FIFO的方法,该方法中会使用被同步到不同时钟域的格雷码指针,以指示“FIFO满”和“FIFO空”的两种状态。完整的代码、综合和分析后的寄存器传输级(RTL)的verilog模型(FIFO Style #1)已包含在文章中。

1.0 引言

一个异步FIFO指的是一个FIFO设计,其中数据在一个时钟域内被写入FIFO缓冲区,在另一个时钟域内则会从同一个FIFO缓冲区中被读出,且这两个时钟域彼此是异步的。(思考:什么是异步?)

异步FIFO被用于从一个时钟域向另一个时钟域安全传递数据。

有很多方式可以设计一个异步FIFO,其中不乏许多错误的方式。大多数被错误实现的FIFO设计在90%的时间中仍会正常工作,而大多数几乎正确的FIFO设计在超过99%的时间中会正常工作。不幸的是,这些能在超过99%的时间中会正常工作的FIFO通常是最难检测和调试的(如果你足够幸运,能在产品上市之前注意到问题),也是诊断和召回时最烧钱的(如果错误没有被发现,直到产品被送到一个不满意的客户手上)。

本文讨论了异步FIFO设计风格和在做异步FIFO设计时必须考虑的重要细节。

本文余下的部分中将“异步FIFO”简称为“FIFO”。

2.0 传输多个异步信号

试图将多个同时变化的信号从一个时钟域同步到另一个新时钟域,并确保所有变化的信号都被同步到新时钟域的同一个时钟周期已被证明是有问题的[1]。(这是Cummings大神的另外一篇论文,讲了异步信号同步时的问题和解决方案——打两拍、握手、异步FIFO。强烈推荐看看这篇文章: Clifford E. Cummings, “Synthesis and Scripting Techniques for Designing Multi-Asynchronous Clock Designs,” SNUG 2001 (Synopsys Users Group Conference, San Jose, CA, 2001) User Papers, March 2001, Section MC1, 3rd paper. Also available at www.sunburst-design.com/papers)FIFO用于安全地将多比特信号从一个时钟域安全地传递到另一个:通过一个时钟域中的控制信号将数据放入FIFO缓冲存储器阵列中,并通过来自第二个时钟域的控制信号从同一FIFO缓冲存储器阵列的另一个端口移除数据。从概念上讲,用这些假设去设计一个FIFO似乎很容易。

做FIFO设计的难点跟生成FIFO指针、并且找到一个可靠的方式去判决FIFO的空满状态密不可分。

2.1 同步FIFO的指针

对于同步FIFO(一种FIFO,向FIFO中写入/从FIFO中读出的操作是在同一个时钟域中)设计,一种实现方案是记录向FIFO写入和从FIFO中读出的数据量,以增加(写FIFO但不读出)、减少(读FIFO但不写入)或保持(既不写入也不读出,或者同时写入读出)当前FIFO缓冲器中被填充的数据量。当这个FIFO计数器达到预定的满值时,FIFO为满,而当FIFO计数器为零时,FIFO为空。

不幸的是,对于异步FIFO设计,这种增减FIFO填充计数器是不能使用的,因为需要两个不同而异步的时钟去控制同一个计数器。若要确定异步FIFO设计的满状态和空状态,则必须对写指针和读指针进行比较。

2.2 异步FIFO的指针

为了理解FIFO的设计,我们需要了解FIFO指针是如何工作的。写指针总是指向下一个要写入的字;因此,在重置时即两个指针都被设置为零时,写指针指向的恰好也是下一个要写入的FIFO字的位置。在FIFO的写操作中,写指针所指向的内存位置被写入数据,然后增加写指针以指向下一个要写入的位置。

类似地,读指针总是指向当前要读的当前FIFO字。同样在重置即两个指针都重置为零时,FIFO为空,读指针指向无效数据(因为FIFO为空,空标志生效)。一旦第一个数据字被写入FIFO,写指针增加,空标志被清除,此时仍然寻址第一FIFO内存字的内容的读指针立即将这个有效的字驱动到FIFO数据输出端口,由接收器逻辑读取。读取指针总是指向下一个要读取的FIFO字的事实意味着接收器逻辑不必使用两个时钟周期来读取数据字。如果接收器还得在读取FIFO数据之前增加读指针,那么接收器就得在第一周期从FIFO向外读出数据,第二周期才能接受从FIFO发过来的新数据,这将是不必要的低效率。(译注:这段内容个人理解说的是FIFO读指针应该自增且永远指向下一个数据,而不是由接收器“手动”控制。)

当读指针和写指针都相等时,FIFO为空。当在重置操作期间两个指针都被重置为零,或者当读指针从FIFO读取最后一个字后赶上写指针时,就会发生这种情况。

当指针再次相等时,或者说,当写指针循环了一圈并追上读指针时,FIFO为满。但这就出现了问题。当指针相同时,FIFO可能是空,也可能是满,但到底是空还是满?

用来区分满和空状态的一种设计技术是为每个指针添加一个额外的位。当写指针增加并超过最后一个FIFO地址时,写指针将增加未使用的的最高位(MSB, Most Significant Bit),同时将其余的位设置为零,如图1所示(FIFO已经被绕了一圈,并切换了指针的最高位)。读指针也是这样做的。如果两个指针的最高位不同,这意味着写指针比读指针多绕了一圈。如果两个指针的最高位相同,这意味着两个指针绕圈的次数相同。

image.png

这里使用n位的指针,其中n-1是访问整个FIFO内存缓冲区所需的地址位数,当包括最高位在内的两个指针相等时,FIFO为空。当两个指针除了最高位都相等时,FIFO为满。(译注:在上图的例子中,n=5。)

本文中的FIFO设计采用了n位的指针去帮助处理一个拥有2n-1个可写的地址的FIFO的空/满条件。更多的空满逻辑设计细节在5.0中有体现。

2.3 二进制FIFO指针的一些注意事项

试图将一个二进制计数值从一个时钟域同步到另一个时钟域是有问题的,因为n位计数器的每一位都可以同时改变(例如7->8,在二进制数中则是0111->1000,所有位都改变了)。解决这个问题的一种方法是,在一个保持寄存器中周期性地采样并保存二进制计数值,并将同步后的rdy(准备,ready)信号传递到新的时钟域。当识别rdy信号时,接收时钟域向发送时钟域发送同步后的ack(确认,acknowledge)信号。在从接收时钟域接收到ack信号之前,采样的指针值不能改变。(译注:但指针本身此时仍可变化。)使用这种技术,可以将一个具有多个变化位的计数值安全地转移到一个新的时钟域。在接收到ack信号后,发送时钟域才被允许清除rdy信号并重新采样二进制计数值。(译注:这是经典的Valid-Ready握手,AXI总线信道用的就是这个。)

使用这种技术,二进制计数器值是被定期采样的,这就意味着并不是所有的二进制计数器值在当前都可以传递到一个新的时钟域。这就引出了一个问题,我们需要关注二进制计数器可能在两个采样计数值中间会继续增加,并溢出或下溢FIFO的情况吗?(译注:溢出(overflow)指FIFO已满但仍在往里写的行为,下溢(underflow)指FIFO空了但仍在往外读的行为。)答案是否[8]。

当写指针赶上同步并采样后的读指针时,FIFO发出满信号。同步和采样的读指针可能不会反映实际读指针的当前值,但写指针将不会尝试对超过同步后的读指针值进行计数。溢出不会发生[8]。

当读指针赶上同步和采样的写指针时,FIFO发出空信号。同步和采样后的写指针可能不会反映实际写指针的当前值,但读指针将不会尝试对超过同步后的写指针值进行计数。下溢不会发生[8]。(译注:这里叙述了假空和假满的概念。可以试着想想,假空和假满会最差会发生多少次?对性能有什么影响?)在下面讨论同步格雷码(Gray code)[6]指针之后,7.0节详细介绍了关于这种用同步后的准备-确认握手信号对二进制指针进行采样的技术的更多观察。

使用FIFO计数器指针的另一种常用方法是使用格雷码计数器。格雷码只允许每次时钟改变时变化一个位,从而消除了与试图在同一时钟边缘上的同步多个变化信号相关的问题。

2.4 FIFO测试的问题

想要测出一个FIFO设计里不易察觉的设计问题几乎是不可能的。这个问题源于这样一个事实,即RTL模拟中的FIFO指针的行为是理想的,即使实现是错误的(也判断不出)。如果在实际设计中使用,它们可能导致灾难性的故障。

在 RTL 仿真中,如果设计中包含二进制计数的 FIFO 指针,所有 FIFO 指针位将同时改变;没有机会观察到同步和比较问题。在没有后续注明延迟的门级仿真中,只有很小的机会观察到问题,即门转换在边沿信号之间产生了不同结果。但即便如此,也必须非常幸运地在上升时钟沿之前和之后产生“正确”的位序列变化。对于高速设计,上升沿和下降沿信号之间的延迟差异减小,检测问题的概率也随之减小。发现实际 FIFO 设计问题的可能性在具有后续注明延迟的门级设计中最大,但即使进行这种类型的仿真,发现问题也将变得困难,并且随着信号传播延迟的减小,观察到设计问题的几率也会降低。

显然,答案应该是要认识到存在潜在的FIFO设计问题,并从一开始就正确地进行设计。

我有时用于测试FIFO设计的行为模型是一个编码简单、对于行为测试目的非常准确,但若被用作RTL综合模型的话将很难调试的一种模型。此FIFO模型仅推荐用于FIFO的testbench。该模型准确地确定了何时应该设置FIFO满状态位和空状态位,并可用于确定应该已经存储在工作FIFO中的数据值。这个FIFO模型不能综合!!!

  module beh_fifo (rdata, wfull, rempty, wdata, winc, wclk, wrst_n, rinc, rclk, rrst_n);
   parameter DSIZE = 8;
   parameter ASIZE = 4; 
   output [DSIZE-1:0] rdata;  
   output wfull;  
   output rempty;  
   input [DSIZE-1:0] wdata;  
   input winc, wclk, wrst_n;  
   input rinc, rclk, rrst_n;  
   reg [ASIZE:0] wptr, wrptr1, wrptr2, wrptr3;  
   reg [ASIZE:0] rptr, rwptr1, rwptr2, rwptr3;  
   parameter MEMDEPTH = 1<<ASIZE;  
   reg [DSIZE-1:0] ex_mem [0:MEMDEPTH-1];  
   always @(posedge wclk or negedge wrst_n)  
       if (!wrst_n) wptr <= 0;  
       else if (winc && !wfull) begin  
           ex_mem[wptr[ASIZE-1:0]] <= wdata;  
           wptr <= wptr+1;  
       end  
   always @(posedge wclk or negedge wrst_n)  
       if (!wrst_n) {wrptr3,wrptr2,wrptr1} <= 0;  
       else {wrptr3,wrptr2,wrptr1} <= {wrptr2,wrptr1,rptr};  
   always @(posedge rclk or negedge rrst_n)  
       if (!rrst_n) rptr <= 0;  
       else if (rinc && !rempty) rptr <= rptr+1;  
   always @(posedge rclk or negedge rrst_n)  
       if (!rrst_n) {rwptr3,rwptr2,rwptr1} <= 0;  
       else {rwptr3,rwptr2,rwptr1} <= {rwptr2,rwptr1,wptr};  
   assign rdata = ex_mem[rptr[ASIZE-1:0]];  
   assign rempty = (rptr == rwptr3);  
   assign wfull = ((wptr[ASIZE-1:0] == wrptr3[ASIZE-1:0]) &&  (wptr[ASIZE] != wrptr3[ASIZE] )); 
  endmodule

在上面这个示例的行为模型中,可以使用二进制计数指针、一个Verilog数组来表示FIFO内存缓冲区、同一模块中的多个异步时钟和非寄存器输出。这个FIFO模型不是为了综合而编写的!!!

模块中的两个always块(带有连接的always块)包含在行为上表示实际RTL FIFO设计中所需要的同步。它们对于通过FIFO测试数据传输并不重要,但它们对于测试FIFO模型中正确计时的满标志和空标志很重要。行为模型中所需的同步级数的确切数量依赖于FIFO设计。该模型可用于帮助测试本文所描述的FIFO设计。(译注:同步级数在在这里是3,即打了三拍。

3.0 格雷码计数器 - 设计风格1

格雷码是以最初在1953年为该码申请专利的弗兰克·格雷命名的[6]。设计格雷码计数器有多种方法。本节详细介绍了一种简单明了的设计方法。本文描述的技术只使用一组触发器来实现格雷码计数器。另一种使用两组触发器以实现更高速度的方法详见第4.0节。

3.1 格雷码的模式

出于稍后将描述的原因,创建一个n位格雷码计数器和一个(n-1)位格雷码计数器是可取的。单独创建这两个计数器当然很容易,但创建一个通用的n位格雷码计数器并修改次高位(2nd MSB)以形成一个共享最低有效位(LSB)的(n-1)位格雷码计数器也同样简单且高效。在本文中,这将被称为“双n位格雷码计数器”。

image.png


为了更好地理解将n位格雷码转换为(n-1)位格雷码的问题,考虑如图2所示的创建一个双4位和3位格雷码计数器的例子。

最常见的格雷码,如图2所示,是一种反射码,其中除最高有效位(MSB)外,任何列中的位都关于序列中点对称[6]。这意味着4位格雷码的后半部分是前半部分的镜像,并且最高有效位被反转。

要将4位格雷码转换为3位格雷码,我们不希望4位序列后半部分的最低有效位(LSB)是前半部分最低有效位的镜像,而是希望后半部分的最低有效位重复前半部分的4位最低有效位序列。

仔细观察可知,反转4位格雷码后半部分的第二个最高有效位将会在4位序列的后三个最低有效位中产生所需的3位格雷码序列。唯一的其他问题是,带有额外最高有效位的3位格雷码不再是真正的格雷码,因为当序列从7(格雷码0100)变为8(非格雷码1000)以及从15(非格雷码1100)变为0(格雷码0000)时,有两个位在变化,而不仅仅是一个位。真正的格雷码在计数之间只改变一个位。

1

点赞

刚表态过的朋友 (1 人)

全部作者的其他最新日志

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 0

    粉丝
  • 0

    好友
  • 1

    获赞
  • 0

    评论
  • 1

    访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-9-27 07:25 , Processed in 0.022460 second(s), 15 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部