story · 2020年09月18日

多时钟域的设计和综合技巧(四)

同步数据信号

将数据从一个时钟域传递到另一个时钟域类似于传递多个没有顺序关系的控制信号。但是不能使用同步器来同步数据信号,因为会采样到错误数据。
在时钟域之间同步数据的两种常用方法是:

(1)使用握手信号在时钟域之间传递数据

(2)使用FIFO(先进先出存储器)。

使用握手信号在时钟域之间传递数据

可以使用握手控制信号在时钟域之间传递数据。但是在使用握手信号时,控制信号越多,将数据从一个时钟域传递到另一个时钟域的延迟时间越长

对于许多跨时钟域数据传递,简单的双线握手控制就足够了。发送方将数据放在数据总线上,然后将“data\_valid”信号同步到接收时钟域。当新时钟域识别“data\_valid”信号的时候,接收方将数据寄存到新时钟域的寄存器中。然后通过同步器向发送方传递“acknowledge”信号。当发送方识别同步后的“acknowledge”信号时,发送方可以改变被驱动到数据总线上的数据。

使用FIFO(先进先出存储器)。

在时钟域之间传递数据的最常用方法之一是使用FIFO。FIFO是一种双端口存储器。一个端口由发送时钟域控制,它将数据快速地放入存储器中。另一个端口由接收时钟域控制。

需要控制信号用于指示FIFO是否空,满或者几乎空,几乎满。

理论上,用一个时钟将数据放入双端口存储器,然后从另一个端口读取数据非常简单,但是生成准确的空和满标志是一个具有挑战性的工作。

确定FIFO空或者满需要对写指针和读指针进行比较。但问题是两个指针是在两个不同的时钟域中生成的,所以在进行比较操作之前,两个指针必须同步到相反的时钟域中才能进行准确的比较操作。

同步到不同时钟域的FIFO指针不应该用二进制计数器实现

二进制计数器的一个特征是在计数时可能需要多个比特位同时翻转。将二进制计数器的计数值同步到新的时钟域比同步控制信号更有难度,因为数据是同时翻转的,也无法组合成1比特信号进行同步。

如果是一个简单的4位二进制计数器,从地址7(二进制0111)变化到地址8(二进制1000),所有四个计数器比特位将同时改变。

如果同步时钟边沿在这个变化的中间,则任何4位二进制都有可能被采样并同步到新的时钟域,如下图所示。

1.jpg

使用错误同步之后的二进制计数值进行比较可能会触发错误的full或empty标志,这将导致数据溢出而丢失,或者在FIFO为真空时读取数据,导致从FIFO读取错误数据。

在时钟域之间传递FIFO指针更好的方法是使用格雷码计数器。格雷码计数器一次只能改变一位。 如果是时钟信号在格雷码计数器转换期间采样,同步值将是旧值或者新值,因为一次只有一位发生改变。

2.jpg

格雷码计数器的框图如上图所示。使用寄存器存储格雷码计数值。寄存器输出反馈到gray-to-binary转换器。二进制值递增1,然后递增的二进制值被传递到binary-to-gray转换器,然后寄存到格雷码寄存器。

将格雷码计数值转换为等效的二进制计数值:

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

简单的代码实现如下:

module gray2bin_bad (bin, gray);
parameter SIZE = 4;
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
reg [SIZE-1:0] bin;
integer i;
// Syntax Error - variable index range
always @(gray)
for (i=0; i<SIZE; i=i+1)
bin[i] = ^(gray[SIZE-1:i]);
end
module

但是,上述Verilog代码是不可综合的,索引不允许使用变量。

另一种方法如下:

bin[0]= gray[3] ^ gray[2] ^ gray[1] ^ gray[0] ; // gray>>0
bin[1] = 1'b0 ^ gray[3] ^ gray[2] ^ gray[1] ; // gray>>1
bin[2] = 1'b0 ^ 1'b0 ^ gray[3] ^ gray[2] ; // gray>>2
bin[3] = 1'b0 ^ 1'b0 ^ 1'b0 ^ gray[3] ; // gray>>3
module gray2bin (bin, gray);
parameter SIZE = 4;
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
reg [SIZE-1:0] bin;
integer i;
always @(gray)
for (i=0; i<SIZE; i=i+1)
bin[i] = ^(gray>>i);
end
module

这个代码是可以综合的。

相应的,将二进制计数值转换为等效的格雷码计数值:

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

Verilog代码如下:

module bin2gray (gray, bin);
parameter SIZE = 4;
output [SIZE-1:0] gray;
input [SIZE-1:0] bin;
assign gray = (bin>>1) ^ bin;
end
module

因此,我们的格雷码计数器就设计好了,Verilog代码包含binary-to-gray转换器和gray-to-binary转换器。格雷码计数器的参数化Verilog模型如下:

module graycntr (gray, clk, inc, rst_n);
parameter SIZE = 4;
output [SIZE-1:0] gray;
input clk, inc, rst_n;
reg [SIZE-1:0] gnext, gray, bnext, bin;
integer i;
always @(posedge clk or negedge rst_n)
if (!rst_n) gray <= 0;
else gray <= gnext;
always @(gray or inc)
 beginfor (i=0; i<SIZE; i=i+1)
bin[i] = ^(gray>>i);
bnext = bin + inc;
gnext = (bnext>>1) ^ bnext;
end
endmodule

注:使用格雷码计数器的异步FIFO为什么不会发生功能错误,请听下文详解。

参考资料

《Synthesis and Scripting Techniques for Designing MultiAsynchronous ClockDesigns》

本文转载自公众号:芯片数字实验室
原文链接:https://mp.weixin.qq.com/s/cu5hQgeHShyOZTzctF-wKA
未经作者同意,请勿转载!

推荐阅读

想了解更多内容,欢迎关注芯片数字实验室专栏,由于工具,你可以专注在更重要的事情上。
推荐阅读
关注数
12313
内容数
219
前瞻性的眼光,和持之以恒的学习~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息