CummingsSNUG2008Boston_CDC.pdf
参考:
跨时钟域处理方法总结–最终详尽版 - love小酒窝 - 博客园
跨时钟域(CDC)设计方法之单bit信号篇(一) | 电子创新网赛灵思社区
孤独的单刀_Verilog语法,FPGA设计与调试,FPGA接口与协议-CSDN博客
跨时钟域传输总结(包含verilog代码|Testbench|仿真结果)-腾讯云开发者社区-腾讯云
异步时序设计指的是在设计中有两个或以上的时钟, 且时钟之间是同频不同相或不同频率的关系。而异步时序设计的关键就是把数据或控制信号正确地进行跨时钟域传输。
每一个触发器都有其规定的建立(setup)和保持(hold)时间参数, 在这个时间参数内, 输入信号在时钟的上升沿是不允许发生变的。 如果在信号的建立时间中对其进行采样, 得到的结果将是不可预知的,即亚稳态。
MTBF(mean time between failures) : 平均故障间隔时间
通常,MTBF越大说明系统采样失败的可能越小。可见,对于高速的设计,MTBF是更容易发生的。对于一个典型的 0.25µm 工艺的 ASIC 库中的一个触发器,我们取如下的参数:
tr = 2.3ns, τ = 0.31ns, T0 = 9.6as, f=100MHZ, a = 10MHZ, MTBF = 2.01 days
也就是说触发器以100MHZ工作,假设异步事件触发的频率,也就是数据变化的频率跟采样时钟频率相同。我们用10MHZ的频率去不停地采它的数据,每个上升沿数据都会发生变化,则每两天就可能采集到一次亚稳态(个人理解,如有误请指正)。如果使用单锁存器同步,b的时钟上升沿采集a的数据时很可能采到亚稳态数据。
信号从源时钟域跨到目的时钟域后,需要在目的时钟的“指挥”下进行操作,这一过程可视为用目的时钟对被处理后的信号进行“采集”,既然是采集,则必须满足奈奎斯特采样定理,也就是说目的时钟频率需要至少是源时钟频率的2倍。
电平信号进行CDC时,由于可以将电平信号看作一个频率很低的脉冲信号,所以不需要考虑时钟快慢,使用两级DFF就能够实现信号的同步
首先需要约定的是,所谓快时钟频率应该至少是慢时钟频率的两倍。
从快时钟域到慢时钟域会出现漏采的情况,所以需要通过降频的方法实现对数据的扩展
既然脉冲信号频率高于目的时钟才导致的漏采,那么我将脉冲信号拓宽到一定的程度不就可以保证采集到了吗?这一方法的本质实质上是降频,也就是将问题转换为我们已经能解决的用较快的时钟来采集较慢的时钟。如下图:
将信号adat扩宽为3倍aclk后,脉冲信号成为了一个较慢的信号,而目的时钟成为了一个较快的时钟,这样我们就可以使用2级同步器的方法来对被CDC信号进行同步。
module quicktoslow # (
parameter CLK_QUICK = 50k,
CLK_SLOW = 5k ,
)
(
input i_quick_clk,
input i_quick_rst,
input i_a,
input i_slow_clk,
input i_slow_rst,
output o_a
)
parameter CLK_DIV = CLK_QUICK / CLK_SLOW;
reg ri_a;
reg [1:0] ro_a;
assign o_a = ro_a[1];
reg [5:0] r_clk_cnt;
always @(posedge i_quick_clk, posedge i_quick_rst) begin
if(i_quick_rst) begin
r_clk_cnt <= 'd0;
end else if (r_clk_cnt == CLK_DIV - 1) begin
r_clk_cnt <= 'd0;
end else if (r_clk_cnt || ri_a) begin
r_clk_cnt <= r_clk_cnt + 1;
end else begin
r_clk_cnt <= r_clk_cnt;
end
end
always @(posedge i_quick_clk, posedge i_quick_rst)begin
if (i_quick_rst) begin
ri_a <= 'd0;
end else if (i_a) begin
ri_a <= 'd1;
end else if (r_clk_cnt == CLK_DIV - 1) begin
ri_a <= 'd0;
end else begin
ri_a <= ri_a;
end
end
always @(posedge i_slow_clk, posedge i_slow_rst) begin
if(i_slow_rst) begin
ro_a <= 'd0;
end else begin
ro_a[0] <= ri_a ;
ro_a[1] <= ro_a[0];
end
end
endmodule
缺点:
为了探寻一种具备普遍性的方法,我们可以使用握手法来进行CDC。握手法的本质是负反馈,通俗来讲,就是我先将被CDC信号展宽,展宽后将其同步到目的时钟域,在目的时钟域生成指示信号,该指示信号用来指示此时信号已经被目的时钟域接收,然后将指示信号反馈到源时钟域(反馈过程),源时钟域接收到这个反馈信号后将被CDC信号拉低,从而确定了展宽长度,也通过”发送--反馈--操作“这一握手过程完成了一次CDC传输。
//单比特快到慢“握手协议”
module cdc_sbit_handshake(
input aclk, //快时钟
input arst_n, //快时钟域复位信号
input signal_a,//快时钟域信号
input bclk, //慢时钟
input brst_n, //慢时钟域复位信号
output signal_b//慢时钟域输出信号
);
//慢时钟域信号展宽直至反馈信号回来再恢复
reg req;//寄存慢时钟域展宽信号
reg ack_r0;//反馈信号
always@(posedge aclk or negedge arst_n) begin
if(!arst_n) begin
req <= 1'b0;
end
else if(signal_a) begin
req <= 1'b1; //信号展宽
end
else if(ack_r0) begin
req <= 1'b0; //反馈信号到来时恢复
end
end
//展宽信号跨时钟同步至慢时钟域
reg req_r0;
reg req_r1;
reg req_r2;
always@(posedge bclk or negedge brst_n) begin
if(!brst_n)begin
{req_r2,req_r1,req_r0} <= 3'b0;
end
else begin
{req_r2,req_r1,req_r0} <= {req_r1,req_r0,req};
end
end
//生成反馈信号并同步至快时钟域
reg ack;
always@(posedge aclk or negedge arst_n) begin
if(!arst_n) begin
{ack_r0,ack} <= 2'b0;
end
else begin
{ack_r0,ack} <= {ack,req_r1};
end
end
//信号上升沿检测,让输出持续一个慢时钟周期
assign signal_b = ~req_r2 & req_r1;
endmodule
信号的跨时钟域传输可能会引入亚稳态问题,那么就需要想办法对其进行处理,从而降低亚稳态发生的概率(即提高MTBF)。
跨时钟域处理方法可以分为两个大类:单Bit信号跨时钟域处理、多Bit信号跨时钟域处理。分类的原因是多bit信号的传递不光只有亚稳态这一个问题,还可能会因为多个信号之间由于工艺、PCB布局等因素导致的信号传输延时(skew)的存在,从而导致信号被漏采或者错采。
在下面的例子中,数据的读取需要载入信号load和使能信号en同时置位,但是这两个信号之间存在一个小偏差(skew),在分别进行单bit的跨时钟域同步时,由于小skew的存在,导致这两个信号在被同步到目的时钟域后有一个时钟周期的偏差,而这个偏差直接导致数据无法被读取。
现在不妨问自己:这两个信号是否都必须传递?实际上不是必须的,应用中我们可以把两个信号合并为一个控制信号,这样就将多bit信号的跨时钟域传递转换成了单bit信号的跨时钟域传递。如下:
同步和异步跨时钟域示例分析
同步时钟、异步时钟----概念解析_一点一点的进步的博客-CSDN博客
FPGA跨时钟域处理之gray码_哔哩哔哩_bilibili
跨时钟域传输的黄金搭档:异步FIFO与格雷码_格雷码跨时钟域处理 csdn 变两位_城外南风起的博客-CSDN博客
FPGA跨时钟域之数据流跨时钟域
所谓数据流跨时钟域即:时钟不同但是时间段内的数据量一定要相同。
常用的数据流跨时钟域可以使用fifo或者ram实现。fifo的实现我们之前文章中fifo资源的介绍中已经详细讲解过了,xilinx FIFO 硬核的结构如下:
但是FIFO实现有一个问题,由于没有地址控制,需要固定时延是不方便,另外就是xilinx IP的fifo深度较大,可能存在资源浪费。
第二种方法就是使用ram自己搭建跨时钟域模块。我们使用bram做数据流跨时钟域。
例示代码如下 ,代码时钟比例为5:4,a时钟每5个clk写入4个数据,b时钟每个clk输出,代码中有两个点要注意:
1、0地址的写是能;
2、有一个防抖处理。