碎碎思 · 2021年09月03日

你的FIFO稳定吗?了解一下格雷编码/解码及异步FIFO的应用

绪论

格雷编码是由弗兰克•格雷于1953年发明的,最初是以发明专利的形式出现的。格雷码的主要特点是相邻编码值中只有一个比特发生改变,下面表中给出了3比特及4比特格雷码和对应的二进制编码,从中可以清楚地看出这一特点。这一特点使得格雷码有着非常广泛的应用。

image.png
image.png

异步FIFO的应用

格雷编码被广泛应用于使用两个不同时钟的异步FIFO(First In First Out,先人先出存储器)中。当数值从一个时钟域传递到另一个时钟域时,单比特翻转的特性就会变得极为重要。在上面的3比特格雷码编码表中,当数值从1变为2时,对于二进制编码,两个比特会发生翻转(比特0从1变为0,比特1从0变为1)。然而,在格雷编码中,只有比特1改变,而比特0和比特2不变,采用格雷编码时,所有相邻值都具有这种性质。例如,当值从7变为0时,可以看出格雷编码值从100变为000,只有一个比特发生了改变。

那么如何在异步FIFO中使用这一特性呢?在异步FIFO中,写地址与读地址根据读写操作发生连续改变,其地址是用二进制计数器进行表示的。以3比特计数器为例,该计数器从0计数到7,当达到7后归零。首先,我们使用转换公式将二进制编码转换成格雷码,并使格雷编码值从一个时钟域传递到另一个时钟域,然后使用另一转换公式将格雷码转换为二进制码。现在已经有了通用的二进制码和格雷码之间的相互转换公式.

当位宽为多个比特的信号从一个时钟域传输到另一个时钟域时,需要使用类似图6.18中的电路。该信号开始时转换为格雷码,然后进入源时钟域的寄存器。此后,通过两级同步器同步步到目的时钟域。实现同步后,通过相反的译码过程(格雷码到二进制码),就可以实现多比特值在两个时钟域之间的传递。整个过程看上去有些烦琐,并且需要占用多个时钟周期,但这一转换过程是必要的。

image.png

下面我们通过一些真实的例子来深入分析这一转换过程,然后再看不使用这一转换方法吋存在的问题。下面的例子中,在CLKA时钟域,一个值从5d变为6d我们来分析具体的传递过程。

image.png

在CLKA中,当十进制数值从5变为6时,经同步器之后,目的时钟域中的值变为101,或者仍然暂时保持为原来的111,待下一个时钟周期之后才能变成101。可以看出,无论是101还是111,最终传递的结果都是按序出现的合法的编码值。假如不使用上面的二进制码-格雷码以及格雷码-二进制码转换电路,直接在时钟域B中采用同步器,同样是时钟域A的信号值由5变成6,那么会出现什么情况呢?下表给出了可能的结果。

image.png

可以看出,同步之后的值可能是101(旧值)、110(新值),但是也可能变为100或者111。由于两个时钟相互独立,同步器输入的两个比特分别进行跨时钟域同步,这些独立同步并输出的值可能出现在不同的时钟周期上。

虽然最终所有比特会输出正确的值,并且最终输出将变为110。然而,在转变过程中,可能输出违反计数规则的值。在该例中,产生了两种非法数值,假设有8比特信号同时跳变(从0111_1111跳变到1000_0000),那么这就会产生大量不合法的值,输出端会在一个或两个完整的时钟周期内保持这些非法的值。

对于FIFO来说,其空、满状态是根据其内部数据深度进行判断得到的,当出现这些临时的非法值时,FIFO可能会产生错误的空、满状态,从而造成外部电路对其内部存储数据量的错误判断,很可能将一个存有数据的FIFO认为是空的,或者将一个没有满的FIFO当成是满的,造成系统工作错误,甚至造成系统崩溃。

二进制码转换为格雷编码的通用电路


代码如下:

module binary_to_gray #(
 parameter PTR=8
)(
 binary_value,
 gray_value
);
//**************************************
input  [PTR:0] binary_value;
output [PTR:0] gray_value;
//**************************************

wire [PTR:0] gray_value;

   
generate
 genvar i; 
 for(i=0;i<PTR;i=i+1) 
  begin
     assign gray_value[i]=binary_value[i]^binary_value[i+1];
  end
endgenerate

assign gray_value[PTR]=binary_value[PTR];

endmodule

展开后,当PTR=2时,运行 如下:

module binarytogray #(
 parameter PTR=2
)(
 input  logic [2:0] binary_value,
 output logic [2:0] gray_value
);


  assign gray_value[0]=binary_value[0]^binary_value[1];
  assign gray_value[1]=binary_value[1]^binary_value[2];

assign gray_value[2]=binary_value[2];

endmodule

格雷码转换为二进制码的通用电路

代码如下:

module gray_to_binary #(
 parameter PTR=8
)(
 binary_value,
 gray_value
);
//**************************************
input  [PTR:0] binary_value;
output [PTR:0] gray_value;
//**************************************

wire [PTR:0] binary_value;

   
generate
 genvar i; 
 for(i=0;i<PTR;i=i+1) 
  begin
     assign binary_value[i]=binary_value[i+1]^gray_value[i+1];
  end
endgenerate

assign binary_value[PTR]=gray_value[PTR];

endmodule
原文:FPGA 的逻辑
作者:碎碎思

相关文章推荐

更多FPGA技术干货请关注FPGA 的逻辑技术专栏。
推荐阅读
关注数
10605
内容数
562
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息