“单比特信号同步” 学习笔记
1. 概述
信号同步的目的是防止新时钟域中第一级触发器
的亚稳态信号对下一级逻辑造成影响。
简单的同步器由两个触发器串联而成,中间没有其它组合电路。
这种设计可以保证后面的触发器获得前一个触发器输出时
前一个触发器已退出了亚稳态,并且输出已稳定。
2.信号同步的要求
为了使同步工作能正常进行
从某个时钟域传来的信号应先通过原时钟域上的一个触发器
然后不经过两个时钟域间的任何组合逻辑
直接进入同步器的第一个触发器中。
这一要求非常重要,
因为同步器的第一级触发器对组合逻辑所产生的毛刺非常敏感。
如果一个足够长的毛刺正好满足建立-保持时间的要求
则同步器的第一级触发器会将其放行,
给新时钟域的后续逻辑送出一个虚假的信号。
3.同步造成的延时
一个经同步后的信号在两个时钟延以后就成为新时钟域中的有效信号。
信号的延迟是新时钟域中的一到两个时钟周期。
一种粗略的估算方法是同步器电路在新时钟域中造成两个时钟周期的延迟,
设计者需要考虑同步延迟对跨时钟域的信号时序造成的影响。
4.三种常用的同步器
同步器由许多种设计方法,
因为同一种同步器不能满足所有应用的需求。
同步器的类型基本上有三种:电平、边沿检测和脉冲。
虽然还存在其它类型的同步器,
但这三种类型的同步器可以解决设计者遇到的多数应用问题。
A. 电平(level signal)同步器
在电平同步器中,
跨时钟域的信号在新时钟域中要保持高电平或低电平两个时钟周期以上。
同步之后的信号是电平的形式,
而该电平所维持的时钟周期个数是其在跨时钟域期间被上升沿检测到的次数。
这种同步器是所有同步器电路的核心。
下面是用Verilog描述的电平同步器
module synzer_ls(
data_out,
clk1,
clk2,
data_in
); //level signal synchronizer
output data_out; // signal that after synchronized
input clk1; // old clk signal
input clk2; // new clk signal
input data_in; // signal that before synchronized
reg a; // DFF in the old clk domain
reg b; // the first DFF in the new clk domain
reg c; // the second DFF in the new clk domain
always @(posedge clk1) //a
a<=data_in;
always @(posedge clk2) //b
b<=a;
always @(posedge clk2) //c
c<=b;
assign data_out=c;
endmodule
B. 边沿检测(edge detecting)同步器
边沿检测同步器在电平同步器的输出端增加了一个触发器。
新增触发器的输出经反相后和电平同步器的输出进行与操作。
这一电路会检测同步器输入的上升沿,
产生一个与时钟周期等宽、高电平有效的脉冲。
如果将与门的两个输入端交换使用,
就可以构成一个检测输入信号下降沿的同步器。
将与门改为非门可以构建一个产生低电平有效脉冲的电路。
当一个脉冲进入更快的时钟域中时,
边沿检测同步器可以工作的很好。
这一电路会产生一个脉冲,用来指示输入信号上升或下降沿。
这种同步器有一个限制,
即输入脉冲的宽度必须大于同步时钟周期与第一个同步触发器所需保持时间之和。
最保险的脉冲宽度是同步器时钟周期的两倍。
如果输入是一个单时钟宽度脉冲进入一个较慢的时钟域,
则这种同步器没有作用,
在这种情况下就要采用脉冲同步器。
下面是用Verilog描述的边沿(上升沿)检测同步器
module synzer_ed(
data_out,
clk1,
clk2,
data_in
); // edge detecting synchronizer
output data_out; // signal that after synchronized
input clk1; // old clk signal
input clk2; // new clk signal
input data_in; // signal that before synchronized
reg a; // DFF in the old clk domain
reg b; // the first DFF in the new clk domain
reg c; // the second DFF in the new clk domain
reg d; // the third DFF in the new clk domain
always @(posedge clk1) //a
a<=data_in;
always @(posedge clk2) //b
b<=a;
always @(posedge clk2) //c
c<=b;
always @(posedge clk2) //d
d<=c;
assign data_out=c&&(~d);
endmodule
C. 脉冲(Pulse)同步器
脉冲同步器的输入信号是一个单时钟宽度脉冲,
它触发原时钟域中的一个翻转电路。
每当翻转电路接收到一个脉冲时,它就会在高低电平间进行转换,
然后通过电平同步器到达异或门的一个输入端,
而另一个信号经过一个时钟周期的延迟进入异或门的另一端,
翻转电路每转换一次状态,
这个同步器的输出端就产生一个单时钟宽度的脉冲。
脉冲同步器的基本功能是从某个时钟域取出一个单时钟宽度脉冲,
然后在新时钟域中建立另一个单时钟宽度的脉冲。
脉冲同步器也有一个限制,
及输入脉冲之间的最小间隔必须等于两个同步器时钟周期。
如果输入脉冲相互过近,
则新时钟域中的输出脉冲也紧密相邻,
结果是输出脉冲宽度比一个时钟周期宽。
当输入脉冲时钟周期大于两个同步器时钟周期时,这个问题更加严重。
这种情况下,如果输入脉冲相邻太近,则同步器就不能检测到每个脉冲。
下面是用Verilog描述的脉冲同步器
module synzer_pl(
data_out,
clk1,
clk2,
data_in,
rst_n
); // pulse synchronizer
output data_out; // signal that after synchronized
input clk1; // old clk signal
input clk2; // new clk signal
input data_in; // signal that before synchronized
input rst_n; // signal indicating reset
reg a; // DFF in the old clk domain
reg b; // the first DFF in the new clk domain
reg c; // the second DFF in the new clk domain
reg d; // the third DFF in the new clk domain
wire q;
wire q1;
wire di;
assign di=data_in?q1:q;
assign q=a;
assign q1=~a;
assign data_out=(c==d)?0:1;
always @(posedge clk1) //a
begin
if(!rst_n)
a<=1'b0;
else
a<=di;
end
always @(posedge clk2) //b
b<=a;
always @(posedge clk2) //c
c<=b;
always @(posedge clk2) //d
d<=c;
endmodule