握手概念的引入
上篇文章中,我们基本了解了单bit信号跨时钟域的三种同步器,在最后一种脉冲同步器中,实现了要求较为苛刻的从慢时钟域到快时钟域的跨越,并输出了与新时钟周期等宽的脉冲信号。但在功能仿真中可以看到,要跨越时钟域的两个单bit信号,存在一个最小间隔的要求,即输入脉冲的最小间隔必须等于两个新时钟的时钟周期。而且,上述三种同步器的设计中,同步器的控制传递都是单向的,仅从原时钟域到目的时钟域,后者并没有状态反馈,这样,在输入脉冲间隔不满足要求的情况之下,信号丢失且没有指示,这必然是设计者不想看到的情况。
要解决以上问题,就需要引入握手机制,保证每个脉冲都同步成功,同步成功后再进行下一个脉冲的同步。全握手(full-handshake)的基本原理:双方电路在声明或终止各自的握手信号信号前都要等待对方的相应。完整的同步过程(A→B)可有以下4个步骤:
- 请求信号的产生:当同步器处于空闲状态时,在输入脉冲到来时,A声明它的请求信号sync_reg;
- 请求信号的跨越与应答信号的产生:sync_reg信号需要跨时钟域同步到B,与此同时,B产生同步脉冲,并产生应答信号sync_ack,此时,已经给出了要输出的脉冲
- 应答信号的跨越与请求信号的清除:sync_ack信号跨时钟域同步到A,与此同时,A清除之前产生的sync_reg;
- 应答信号的清除:在sync_reg信号清除之后,B清除sync_ack信号。此时,一次信号的跨时钟域完成,等待同步下一个脉冲。
给出上述表达的示意图:
Mike Stein给出了全握手方式所消耗的延时。根据经验估算法,信号跨越一个时钟域需要花两个时钟周期的实现,信号在跨域多个时钟域前被电路寄存,因而,上述方式的延时构成为<5TA + 6TB>。由此也可以知道,全握手方式需要消耗很多的时钟周期。
给出握手方式的设计及测试代码:
//脉冲同步器
module pulse_syc_handshake(
input wire din,
input wire clk_A,
input wire rst_n_A,
input reg clk_B,
input reg rst_n_B,
output wire sync_idle, //给出同步器是否空闲的信号
output reg sync_fail, //同步失败:位于原时钟域
output wire dout
);
reg src_sync_req; //原时钟产生同步请求
reg req_state_dly1;
reg req_state_dly2; //同步器的输出
reg req_state_dly3; //目的时钟延后信号,以保证脉冲输出
reg dst_sync_ack; //目的时钟产生应答
reg ack_state_dly1;
reg ack_state_dly2; //同步器的输出
wire src_sync_ack; //原时钟接收应答
//同步器空闲状态的判断:原时钟下:请求和应答信号同时无效
assign sync_idle = ~(src_sync_req | src_sync_ack );
//同步失败的判断
always @(posedge clk_A or negedge rst_n_A)
begin
if(rst_n_A == 1'b0)
sync_fail <= 1'b0;
else if(din & (~sync_idle)) //源时钟脉冲到来,此时同步器不空闲,给出同步失败
sync_fail <= 1'b1;
else
sync_fail <= 1'b0;
end
//原时钟产生请求信号,请求信号的产生相当于将脉冲转化为了电平
always @(posedge clk_A or negedge rst_n_A)
begin
if(rst_n_A == 1'b0)
src_sync_req <= 1'b0;
else if(din & sync_idle) //源时钟脉冲到来,且源时钟空闲,传递请求。同时完成了脉冲转电平
src_sync_req <= 1'b1;
else if(src_sync_ack) //检测到应答以后,清除请求
src_sync_req <= 1'b0;
end
//同步原时钟请求信号到目的时钟,利用请求信号跨时钟域
always @(posedge clk_B or negedge rst_n_B)
begin
if(rst_n_B == 1'b0)
begin
req_state_dly1 <= 1'b0;
req_state_dly2 <= 1'b0;
req_state_dly3 <= 1'b0;
end
else
begin
req_state_dly1 <= src_sync_req;
req_state_dly2 <= req_state_dly1; //打两拍结束
req_state_dly3 <= req_state_dly2; //再外接一个寄存器,以保证脉冲输出
end
end
//上升沿检测,产生输出脉冲
assign dout = (~req_state_dly3) & req_state_dly2; //完成输出脉冲
//目的时钟产生应答信号
always @(posedge clk_B or negedge rst_n_B)
begin
if(rst_n_B == 1'b0)
dst_sync_ack <= 1'b0;
else if (req_state_dly2) //同步高电平已到达
dst_sync_ack <= 1'b1;
else
dst_sync_ack <= 1'b0;
end
//同步目的时钟产生的应答信号到原时钟
always @(posedge clk_A or negedge rst_n_A)
begin
if(rst_n_A == 1'b0)
begin
ack_state_dly1 <= 1'b0;
ack_state_dly2 <= 1'b0;
end
else
begin
ack_state_dly1 <= dst_sync_ack;
ack_state_dly2 <= ack_state_dly1;
end
end
assign src_sync_ack = ack_state_dly2;
endmodule
module pulse_syc_handshake_tb;
reg clk_A;
reg rst_n_A;
reg din;
wire sync_idle;
wire sync_fail;
reg clk_B;
reg rst_n_B;
wire dout;
always #10 clk_A = ~clk_A;
always #40 clk_B = ~clk_B;
initial
fork
clk_A = 1'b1;
din = 1'b0;
clk_B = 1'b0;
#10 rst_n_A = 1'b0;
#20 rst_n_B = 1'b0;
#60 rst_n_A = 1'b1;
#70 rst_n_B = 1'b1;
#300 din = 1'b0;
#320 din = 1'b1;
#340 din = 1'b0;
//脉冲间隔大于等于5Ta+6Tb
#920 din = 1'b1;
#940 din = 1'b0;
//脉冲间隔小于5Ta+6Tb
#1400 din = 1'b1;
#1420 din = 1'b0;
join
pulse_syc_handshake u1(.clk_A(clk_A),
.rst_n_A(rst_n_A),
.din(din),
.sync_idle(sync_idle),
.sync_fail(sync_fail),
.clk_B(clk_B),
.rst_n_B(rst_n_B),
.dout(dout));
endmodule
给出Modelsim的仿真波形:
从波形图中可以看出,当在输入脉冲到来时,同步器空闲信号(sync_idle)无效,表示此时同步器正被使用;
在一定时钟周期之后,同步器完成了信号的同步,输出了脉冲信号(dout)。但此时,同步器仍未进入空闲,这是因为B时钟域的应答信号正在向A时钟域跨越。在应答信号到达A时钟域之后,请求信号被无效,同时,在B时钟域检测到无效的请求信号以后,应答信号也被无效,最终两个信号都无效的情况下,同步器进入空闲状态,等待下一个脉冲的输入
当同步器被使用的情况下,输入脉冲到来时,会同步失败信号会被直接拉高,以表示此时无法正常完成信号的同步。
重点考虑完成这样的同步所花费的时钟周期,在不考虑输入脉冲在A时钟域寄存的前提之下:
- A时钟域的请求信号跨越至B时钟域,所消耗的时间为(TB,2TB](此时,请求信号顺利跨越到B时钟域);
- B时钟域检测到同步来的请求信号,消耗时间为TB(此时,时序已稳定,消耗时间可视为常数);
- B时钟域产生的应答信号,跨时钟域到A时钟域,所消耗的时间为(TA, 2TA];
- A时钟域检测到同步的应答信号,将请求信号置为无效,消耗时间为TA;
- 无效的请求信号仍要同步到B时钟域,所消耗的时间为(TB,2TB];
- 请求信号无效,应答信号随之拉低,消耗时间为TB;
- 无效的应答信号需要再次跨越到A时钟域,所消耗的时间为(TA, 2TA];
通过上述推导,可得最坏情况下所需要的时钟周期为(5TA+6TB)才能完成整个操作,同步器才能再次被置为空闲。**这种稳定性很好的全握手方式,需要消耗巨大的时钟周期(原先的脉冲同步器仅消耗2TB)。**因而又有很多稳定性和速度折衷的方法,比如部分握手,通信双方的电路都不等对方的相应就终止各自的信号,并继续执行握手命令序列,在本文中就暂不详述了,之后详细了解再做整理哈。
参考文章
- Stein, Mike . “跨越鸿沟: 同步世界中的异步信号.” 电子设计技术 11(7)(2004): 76-76.
- 路科验证笔试题目整理