Chapter8. 单bit信号跨时钟域同步CDC
本章导图
单bit信号慢到快传输
对于电平信号快到慢
由于电平信号高电平时间足够长,因此直接打两拍避免亚稳态是可以的。
对于边沿信号的同步
要将快时钟域的边沿信号提取,同样的,如果边沿信号如果不满足3edge条件依旧会采不到;思路就是打三拍,后两拍检测边沿。
module edge_sync
(
input clk1,
input clk2,
input edge_in,
output edge_out
);
reg [2:0] edge_rr;
reg edge_r;
// 本地时钟域打一拍
always @(posedge clk1 or negedge rst_n)begin
if(!rst_n)
edge_r <= 1'b0;
else
edge_r <= edge_in;
end
// 目的时钟域打三拍,前两拍避免亚稳态,后两拍提取边沿
always @(posedge clk2 or negedge rst_n)begin
if(!rst_n)
edge_rr <= 1'b0;
else
edge_rr <= {edge_rr[1:0], edge_r};
end
// 上升沿为例
assign edge_out = ~edge_rr[2] & edge_rr[1];
endmodule
单bit信号快到慢传输
快到慢电平同步
反馈式跨时钟域传输
- 应用背景:要求adata的每一次反转都同步到bclk,信号从aclk域同步到bclk域,再同步回aclk域。aclk的data只有看到同步回来的值之后才能再翻转。
- 缺点:aclk中对adata相当于做了扩展,两次同步也增加了延时,一般都用于adata变化频率很低的情况;也正因此,反馈同步的方式不能做到像下文那样aclk中的一个cycle的pulse对应bclk中的一个cycle pulse
module feedback_sync
(
input aclk,
input bclk,
input arst_n,
input brst_n,
input adata,
output ack, // usually set as feedback signal to output
output bdata
);
reg adata_r;
reg [1:0] data_ack, data_req;
always @(posedge aclk or negedge arst_n)begin
if(!arst_n)
adata_r <= 1'b0;
else if (adata)
adata_r <= 1'b1;
else if (!adata & data_ack[2])
adata_r <= 1'b0;
end
always @(posedge aclk or negedge arst_n)begin
if(!arst_n)
data_ack <= 1'b0;
else
data_ack <= {data_ack[0], data_req[1]};
end
always @(posedge bclk or negedge brst_n)begin
if(!brst_n)
data_req <= 1'b0;
else
data_req <= {data_req[0], adata_r};
end
assign bdata = data_req[1];
assign ack = data_ack[1];
endmodule
快到慢脉冲同步
脉冲同步的核心思想
-
同步aclk时钟域的一个单周期的pulse到bclk时钟域时,我们期望bdata是什么样呢?
期望bclk时钟域也是单周期的一个pulse
为什么这么期望?
因为例如一些计数器,计数到某个值产生一个pulse,然后对应到bclk里面的操作,清空、使能、pop FIFO等操作;这就需要一个脉冲准确对应一次操作;而对于之前的电平信号(状态信号)而言,那种打两拍操作的,不能确定是为高多少周期为低多少周期,因此简单打两拍的操作不适用于这种场景。
-
简单来说就是:
- 状态、电平信号CDC -> 打两拍可以
- 脉冲(需要准确单周期pulse映射的)-> 打两拍不行,需要进行脉冲同步
脉冲同步的操作:
- 将aclk时钟域的pulse信号转换为level信号
- 用2flop synchronizer同步这个信号
- 在bclk时钟域中将level信号转换为pulse
脉冲同步的pulse间隔要求
- 这也是脉冲同步的局限性:需要aclk的两个pulse之间间隔足够大(两个pulse上升沿的间隔),要满足bclk的3edge要求。
- 更加细节的问法:如果不确定pulse脉冲的间隔,那么aclk和bclk需要满足什么关系呢?
- 答:在aclk中两个pulse最小的间隔为一个aclk周期(实际上就是2分频),那么转换成level信号就变成了aclk的4分频(再2分频一次),那么level信号的高电平持续时间最短为2个aclk cycle,那么也就说如果这2个clk cycle满足3edge的要求,那么就可以保证bclk也对应产生pulse
- 再深入一些询问:如果不能满足上述的关系,怎么办?
- 答:那就只能使用反馈的方法了,adata通过下文的f2s_pulse_sync同步到bclk,然后bclk再通过f2s_pulse_sync(也可以打两拍因为这里就是慢到快了,前提是3edge)同步回aclk作为反馈信号pulse_en
- 继续问:前面把每个pulse又同步回来,效率实在不高,有没有别的办法?
- 答:异步FIFO,aclk产生一个pulse就push一个数据,bclk产生一个pulse就pop
module f2s_pulse_sync
(
input clk1,
input clk2,
input rst_n1,
input rst_n2,
input pulse,
output reg pulse_out
);
// xor gate
reg pulse_r, pulse_e;
reg [2:0] pulse_rr;
reg pulse_end;
always @(posedge clk1 or negedge rst_n1)begin
if(!rst_n1)
pulse_r <= 1'b0;
else
pulse_r <= pulse;
end
always @(posedge clk1 or negedge rst_n1)begin
if(!rst_n1)
pulse_e <= 1'b0;
else
pulse_e <= pulse_e ^ pulse_r;
end
always @(posedge clk2 or negedge rst_n2)begin
if(!rst_n2)
pulse_rr <= 1'b0;
else
pulse_rr <= {pulse_rr[1:0], pulse_e};
end
always @(posedge clk2 or negedge rst_n2)begin
if(!rst_n2)
pulse_out <= 1'b0;
else
pulse_out <= pulse_rr[2] ^ pulse_rr[1];
end
endmodule
`timescale 1ns/1ps
module tb;
reg clk, rst_n;
initial begin
clk = 1'b0;
#5 clk = 1'b1;
forever #5 clk = ~clk;
end
reg clk2;
initial begin
clk2 = 1'b0;
#15 clk2 = 1'b1;
forever #15 clk2 = ~clk2;
end
initial begin
rst_n = 1'b0;
#28 rst_n = 1'b1;
end
reg pulse;
initial begin
pulse = 1'b0;
#48 pulse = 1'b1;
#10 pulse = 1'b0;
#30 pulse = 1'b1;
#10 pulse = 1'b0;
end
wire out;
f2s_pulse_sync inst (clk, clk2, rst_n, rst_n, pulse, out);
initial begin
$dumpfile("showmebug.vcd");
$dumpvars(0, tb);
#1000
$finish;
end
endmodule
满足3edge的情况:
不满足3edge的情况:
标签:同步,Chapter8,bclk,CDC,aclk,pulse,信号,input,bit From: https://www.cnblogs.com/pu1se/p/16617443.html