同步器在慢时钟域clk_a信号同步入快时钟域clk_b时工作的很好,但是反过来的话,会存在快时钟采样不到慢时钟域内的输入脉冲的问题。举一个很简单的例子,其中clk_a是快时钟,clk_b是要进入的慢时钟,快时钟下的pluse_a信号脉冲很窄,则位于慢时钟的两个相邻跳变沿之间,会导致信号丢失或者容易出现亚稳态。
通常的解决方法是将pluse_a进行拓宽,就是在快时钟域内先进行脉冲展宽,展宽到快时钟内能采样到为止;展宽之后的信号在快时钟域clk_b下用两级寄存器同步下就好了,再用上升沿检测检测到同步后的信号得到一个时钟clk_b周期的脉冲,表示同步完成。
其中有一个问题是在快时钟域的拓宽的信号在什么时候拉低呢?这里需要一个反馈信号,反馈信号有效时,拉低拓宽信号,其中反馈信号的目的是告诉快时钟域已经采集到了有效信号,可以将拓展信号拉低。
可以看到其中中间信号是两个,一个在signal_a是在快时钟下的拓宽信号,signal_b是在慢时钟下的扩宽信号,将signal_b进行打拍,得到扩宽输出的有效电平信号和电压信号,当clk_a时钟检测到signal_b打拍后呈现有效信号,则将表明反馈信号有效,置signal_a为0,原博主的好像有问题,我修改了一下,如果有错误,希望指出。
module tclk(
input clk_a, //快
input clk_b, //慢
input rst_n,
input pulse_a_in,
output pulse_b_out,
output lev_b_out
);
reg signal_a; //快时钟的扩宽信号
reg signal_b;
reg pulse_r1;
reg pulse_r2;
reg signal_b_a1;
reg signal_b_a2;
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
signal_a <= 1'b0;
else if(pulse_a_in == 1'b1)
signal_a <= 1'b1;///检测到快时钟域的有效输入信号,进行同步拓宽
else if(signal_b_a2 == 1'b1) //采样到慢时钟域的同步后的有效信号后,停止拓宽
signal_a <= 1'b0;
else
signal_a <= signal_a;
end
//将慢脉冲clkb对扩宽后的信号进行采样
always@(posedge clk_b or negedge rst_n)
begin
if(!rst_n)
signal_b<= 1'b0;
else
signal_b<=signal_a;
end
//将采样后的信号signal_b打两拍,解决亚稳态的问题
always@(posedge clk_b or negedge rst_n)
begin
if(!rst_n)
begin
pulse_r1<= 1'b0;
pulse_r2<= 1'b0;
end
else
begin
pulse_r1<= signal_b;
pulse_r2<=pulse_r1;
end
end
//用于给signal_a反馈的拉低信号signal_b1_a2是清除clka下的脉冲拓宽信号标志信号。
//把它同步回clka时钟域后再使用。
always @ (posedge clk_a or negedge rst_n)
begin
if (rst_n == 1'b0) begin
signal_b_a1 <= 1'b0 ;
signal_b_a2 <= 1'b0 ;
end
else begin
signal_b_a1 <= pulse_r2 ;
signal_b_a2 <= signal_b_a1 ;
end
end
//输出电平信号与脉冲信号
assign pulse_b_out=pulse_r1&(~pulse_r2);
assign lev_b_out=pulse_r2;
endmodule
module tb_CDC_practice();
reg clk_a,clk_b,rst_n;
reg pulse_a_in;
wire pulse_b_out, lev_b_out;
always
begin
#10 clk_a = ~clk_a;
end
always
begin
#30 clk_b = ~clk_b;
end
initial begin
clk_a = 1'b1;
clk_b = 1'b1;
pulse_a_in = 1'b0;
rst_n = 1'b1;
#10;
rst_n = 1'b0;
#30;
rst_n = 1'b1;
#5
pulse_a_in = 1'b1;
#15
pulse_a_in = 1'b0;
end
CDC_Practice tb_CDC_Practic(
.clk_a(clk_a),
.clk_b(clk_b),
.rst_n(rst_n),
.pulse_a_in(pulse_a_in),
.pulse_b_out(pulse_b_out),
.lev_b_out(lev_b_out)
);
endmodule