跨时钟域传输
延迟打拍法最常用的同步方法是双级触发器缓存法,俗称延迟打拍法。异步信号从一个时钟域进入另一个时钟域之前,将该信号用两级触发器连续缓存两次,可有效降低因为时序不满足而导致的亚稳态问题。电路示意图如下。https://www.runoob.com/wp-content/uploads/2021/05/v-slow2fast-1.png一般设计中使用两级触发器进行缓存即可满足设计时序需求。大量实验表明,三级触发器缓存可解决 99% 以上的此类异步时序问题。两级触发器延迟打拍并检测信号上升沿的 Verilog 描述如下:实例module delay_clap(input clk1,//异步慢时钟
input sig1,//异步信号
input rstn,//复位信号
input clk2,//目的快时钟域市政
output sig2); //快时钟域同步后的信号
reg sig2_r ; //3级缓存,前两级用于同步,后两节用于边沿检测
always @(posedge clk2 or negedge rstn) begin
if (!rstn) sig2_r<= 3'b0 ;
else sig2_r<= {sig2_r, sig1} ;//缓存
end
assign sig2 = sig2_r && !sig2_r ; //上升沿检测
延迟采样法此方法主要针对多位宽的数据传输。例如当两个异步时钟频率比为 5 时,可以先用延迟打拍的方法对数据使能信号进行 2 级打拍缓存,然后再在快时钟域对慢时钟域的数据信号进行采集。该方法的基本思想是保证信号被安全采集的时刻,而不用同步多位宽的数据信号,可节省部分硬件资源。利用打拍的方法进行延迟采样的 Verilog 描述如下。实例//同步模块工作时钟为 100MHz 的模块
//异步数据对来自工作时钟为 20MHz 的模块
module delay_sample(
input rstn,
input clk1,
input din,
input din_en,
input clk2,
output dout,
output dout_en);
//sync din_en
reg din_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn) din_en_r<= 3'b0 ;
else din_en_r<= {din_en_r, din_en} ;
end
wire din_en_pos = din_en_r && !din_en_r ;
//sync data
reg dout_r ;
reg dout_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
dout_r <= 'b0 ;
else if (din_en_pos)
dout_r <= din ;
end
//dout_en delay
always @(posedge clk2 or negedge rstn) begin
if (!rstn) dout_en_r <= 1'b0 ;
else dout_en_r <= din_en_pos ;
end
assign dout = dout_r ;
assign dout_en = dout_en_r ;
endmodule
该方法时序结果图如下所示。显然,在 clk2 时钟域,t2 时刻对数据进行采样缓存比 t1 时刻要安全的多。https://www.runoob.com/wp-content/uploads/2021/05/v-slow2fast-2.png但如果慢时钟域没有数据使能信号 din_en, 或数据使能信号一直有效,此时在快时钟域对数据使能信号进行上升沿检测的方法将会失效。因为数据使能信号一直有效,除了第一个数据,快时钟域将无法检测到后继数据的传输时刻。解决方法就是,在快时钟域对慢时钟信号的边沿进行检测。如果两个时钟的频率相差较小,可能还需要对数据进行延迟缓存,以保证采集到的是当拍时钟的数据;如果两个时钟的频率相差较大,数据采样时刻可以通过计数的方法获得,而不用对数据进行缓存。利用计数延迟采样的方法对慢时钟边沿进行检测的 Verilog 描述如下。实例//同步模块工作时钟为 100MHz 的模块
//异步数据对来自工作时钟为 999KHz 的模块
module delay_cnt_sample(
input rstn,
input clk1,
input din,
input din_en,
input clk2,
output dout,
output dout_en);
//4级缓存:3级用于打拍同步,一级用于边沿检测
reg edge_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn) edge_r<= 3'b0 ;
else edge_r<= {edge_r, clk1} ;
end
wire edge_pos = edge_r && !edge_r ;
//延迟计数器,检测到慢时钟上升沿时开始计数
reg cnt ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn) cnt <= 6'h3f ;
else if (edge_pos && din_en)
cnt <= 6'h0 ;
else if (cnt != 6'h3f) cnt <= cnt + 1'b1 ;
end
//数据同步
reg dout_r ;
reg dout_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
dout_r <= 'b0 ;
else if (din_en && cnt == 47) //大约在慢时钟周期中间时刻采样
dout_r <= din ;
end
//数据使能信号较数据采样时刻延迟一个周期输出
always @(posedge clk2 or negedge rstn) begin
if (!rstn) dout_en_r <= 1'b0 ;
else if (din_en && cnt==48)
dout_en_r <= 1'b1 ;
else dout_en_r <= 1'b0 ;
end
assign dout = dout_r ;
assign dout_en = dout_en_r ;
endmodule
<button class="copy-code-button" type="button" data-clipboard-text="//同步模块工作时钟为 100MHz 的模块//异步数据对来自工作时钟为 999KHz 的模块module delay_cnt_sample( input rstn, input clk1, input din, input din_en, input clk2, output dout, output dout_en); //4级缓存:3级用于打拍同步,一级用于边沿检测 reg edge_r ; always @(posedge clk2 or negedge rstn) begin if (!rstn) edge_r<= 3'b0 ; else edge_r<= {edge_r, clk1} ; end wire edge_pos = edge_r && !edge_r ; //延迟计数器,检测到慢时钟上升沿时开始计数 reg cnt ; always @(posedge clk2 or negedge rstn) begin if (!rstn) cnt <= 6'h3f ; else if (edge_pos && din_en) cnt <= 6'h0 ; else if (cnt != 6'h3f) cnt <= cnt + 1'b1 ; end //数据同步 reg dout_r ; reg dout_en_r ; always @(posedge clk2 or negedge rstn) begin if (!rstn) dout_r <= 'b0 ; else if (din_en && cnt == 47) //大约在慢时钟周期中间时刻采样 dout_r <= din ; end //数据使能信号较数据采样时刻延迟一个周期输出 always @(posedge clk2 or negedge rstn) begin if (!rstn) dout_en_r <= 1'b0 ; else if (din_en && cnt==48) dout_en_r <= 1'b1 ; else dout_en_r
频率相差较大的数据同步采样结果图如下。由图可知,快时钟采样时刻在慢时钟周期中央时刻左右,此时是非常安全的。
页:
[1]