跨时钟域处理一直是数字IC,FPGA等数字电路设计中最常见的问题,英文说法是Clock Domain Conversion。之所以需要进行数字信号的跨时钟域处理,主要是因为当信号进入异步时钟域时,如果信号进行跳变的时刻处于异步时钟域中触发器的建立时间和保持时间内,触发器无法确定采集到的信号究竟是高还是低,所以输出端会出现不稳定的状态,也就是亚稳态。如下图所示:
对于数字信号的跨时钟域问题,有很多已经非常成熟的处理方式,例如寄存器打两拍,DMUX,结绳信号法,异步FIFO等。本篇文章主要介绍的方法是单bit脉冲信号跨时钟域的结绳信号法。该方法具有一定的普适性,无论信号是从快到慢时钟域,还是慢到快时钟域,都可以实现信号跨时钟域的安全传输。话不多说,先上代码:
module cdc_pro(
input clka , // 时钟 a
input clkb , // 时钟 b
input rst_n , // 复位信号,低有效
input pulse_ina , // 脉冲信号,时钟域 a 到时钟域 b 的传输信号 ( a --> b )
output signal_outb , // 时钟域的双寄存器打拍信号
output pulse_outb // 时钟域 b 最终的同步信号
);
reg signal_b_r1 ; // 时钟域 b 第1级打拍信号
reg signal_b_r2 ; // 时钟域 b 第2级打拍信号
reg signal_b_r3 ; // 时钟域 b 第2级打拍信号
reg signal_a_r1 ; // 时钟域 b 反馈到 a 的第1级打拍 结绳信号
reg signal_a_r2 ; // 时钟域 b 反馈到 a 的第2级打拍 结绳信号
reg signal_a ; // 脉冲信号 pulse_ina 的展宽信号
// 在 clka 内对脉冲信号进行展宽
always @ (posedge clka or negedge rst_n) begin
if (~rst_n) begin
signal_a <= 1'b0 ;
end //
else if (pulse_ina == 1'b1) begin // 对输入脉冲信号进行展宽
signal_a <= 1'b1 ;
end //
else if (signal_a_r2 == 1'b1) begin // 直到收到时钟域 b 的结绳信号才结束展宽,拉低信号
signal_a <= 1'b0 ;
end //
else begin
signal_a <= signal_a ;
end //
end //
// 对脉冲展宽信号 signal_a 进行寄存器打拍
always @ (posedge clkb or negedge rst_n) begin
if (~rst_n) begin
{signal_b_r3, signal_b_r2, signal_b_r1} <= 0;
end //
else begin
{signal_b_r3, signal_b_r2, signal_b_r1} <= {signal_b_r2, signal_b_r1, signal_a};
end //
end //
assign signal_outb = signal_b_r2 ; // 2级打拍信号输出
assign pulse_outb = (~signal_b_r3) & (signal_b_r2) ; // 上升沿检测,形成 clkb 内脉冲信号
// 对 clkb 内提取的二级打拍信号进行反馈结绳,结绳信号在clka内也需要进行打两拍
always @ (posedge clka or negedge rst_n) begin
if (~rst_n) begin
{signal_a_r2, signal_a_r1} <= 0 ;
end //
else begin
{signal_a_r2, signal_a_r1} <= {signal_a_r1, signal_b_r2} ;
end //
end //
endmodule
观察上述结绳法的代码,其实可以按照结构分为3部分,第1部分是将时钟域a的脉冲信号进行展宽处理,防止时钟域b漏采到信号;第2部分是脉冲展宽信号的跨时钟域打拍和上升边沿检测;第3部分是时钟域b的反馈结绳信号到时钟域a的跨时钟域处理。在vivado软件中生成上述代码的RTL图,如下所示:
利用结绳法处理跨时钟域信号有两个关键点需要注意,一个是对脉冲信号的展宽和拉低处理;另一个是对结绳信号的选择和打拍处理,这两点在代码中都有很好的体现与注释。
跨时钟域信号的处理在数字电路设计相关工作的笔试和面试中属于高频出现的题目,希望我的文章能够对需要学习相关知识的小伙伴有一些帮助!