前言
单bit信号的跨时钟域处理,可以分为电平信号的跨时钟域处理和脉冲信号的跨时钟域处理。
一、电平信号的跨时钟域处理
电平信号,就是说clka下的信号signal_a在clkb看来,是一个很宽的信号,会保持多个clkb的时钟周期,一定能被clkb采到。这种情况,只需要在clkb时钟域下用至少两级DFF将signal_a打两拍即可。
特别需要强调的是,此时signal_a必须是clka下的寄存器信号。如果signal_a是clka下的组合逻辑信号,一定要先在clka下用DFF打一拍,再使用两级DFF向clkb传递。这是因为clka下的组合逻辑信号会有毛刺,在clka下使用时会由setup/hold时间保证毛刺不会被clka采到,但由于异步相位不确定,组合逻辑的毛刺却极有可能被clkb采到。
具体代码如下:
module level_signal_sync(
input wire clk_b,
input wire rst_n,
input wire levl_a,
output wire levl_b,
output wire levl_b_pos,
output wire levl_b_neg
);
reg levl_b_syn1, levl_b_syn2, levl_b_syn3;
always @(posedge clk_b, negedge rst_n) begin
if(rst_n == 1'b0) begin
levl_b_syn1 <= #1 1'b0;
levl_b_syn2 <= #1 1'b0;
levl_b_syn3 <= #1 1'b0;
end
else begin
levl_b_syn1 <= #1 levl_a;
levl_b_syn2 <= #1 levl_b_syn1;
levl_b_syn3 <= #1 levl_b_syn2;
end
end
assign levl_b = levl_b_syn2;
assign levl_b_pos = levl_b_syn2 && (~levl_b_syn3);
assign levl_b_neg = (~levl_b_syn2) && levl_b_syn3;
endmodule
上面三个输出分别是经过同步之后,clkb下可以使用的电平信号、0变1脉冲信号以及1变0脉冲信号。再次强调:levl_a必须是clka的DFF信号!
二、脉冲信号的跨时钟域处理
下面是更常见的,clka下的脉冲信号,同步到clkb时钟域下,它对于clka与clkb的时钟频率关系没有任何限制,快到慢,慢到快都可以。其主要原理就是先把脉冲信号在clka下展宽,变成电平信号,再向clkb传递,当确认clkb已经“看见”信号同步过去之后,再清掉clka下的电平信号。
具体代码如下:
module pulse_signal_sync(
input wire clk_a,
input wire clk_b,
input wire rst_n,
input wire puls_a,
output wire puls_b,
output wire levl_b
);
reg levl_a;
reg levl_b_syn1, levl_b_syn2, levl_b_syn3;
reg levl_b2a_syn1, levl_b2a_syn2;
//生成clka时钟域的脉冲展宽信号
always @(posedge clk_a, negedge rst_n) begin
if(rst_n == 1'b0) begin
levl_a <= #1 1'b0;
end
else if(puls_a) begin
levl_a <= #1 1'b1;
end
else if(levl_b2a_syn2) begin
levl_a <= #1 1'b0;
end
else begin
levl_a <= #1 levl_a;
end
end
//在clkb时钟域采样展宽后的信号
always @(posedge clk_b, negedge rst_n) begin
if(rst_n == 1'b0) begin
levl_b_syn1 <= #1 1'b0;
levl_b_syn2 <= #1 1'b0;
levl_b_syn3 <= #1 1'b0;
end
else begin
levl_b_syn1 <= #1 levl_a;
levl_b_syn2 <= #1 levl_b_syn1;
levl_b_syn3 <= #1 levl_b_syn2;
end
end
//在clka时钟域内同步clkb时钟域内的展宽信号,以生成反馈信号
always @(posedge clk_a, negedge rst_n) begin
if(rst_n == 1'b0) begin
levl_b2a_syn1 <= #1 1'b0;
levl_b2a_syn2 <= #1 1'b0;
end
else begin
levl_b2a_syn1 <= #1 levl_b_syn2;
levl_b2a_syn2 <= #1 levl_b2a_syn1;
end
end
assign puls_b = levl_b_syn2 && (~levl_b_syn3);
assign levl_b = levl_b_syn2;
endmodule
总结:
1、慢时钟域到快时钟域的单bit信号,一般可以直接通过打两拍的方式来处理。
2、快时钟域到慢时钟域的脉冲信号,由于脉冲宽度较窄,采用打两拍的方式在慢时钟域内有可能采样不到,因此需要采用握手的方式处理。
3、首先是在快时钟域下将脉冲展宽(快时钟域下脉冲信号到来时将电平信号拉高,慢时钟域下的反馈信号到来后将电平信号拉低)。
4、展宽后的电平信号在慢时钟域下打两拍,结果作为电平输出信号。
5、慢时钟域下的电平输出信号在快时钟域下打两拍,结果作为反馈信号。
6、在慢时钟域下采用边沿检测的方式,可以生成脉冲输出信号。