赞
踩
跨时钟域问题是FPGA以及IC设计中最常见的话题,也几乎是最重要的问题。
参考:https://bbs.huaweicloud.com/blogs/283007
只要FPGA设计中的所有资源不全属于一个时钟域,那么就可能存在跨时钟域问题,因为异步逻辑其实也可以看做一种特殊的跨时钟域问题。发生跨时钟域问题的必要条件是不同时钟域之间存在信息交互,如果一个FPGA设计中存在多个时钟域的话,这几乎是无法避免的,否则各个时钟域互不相关,那么就相当于把原设计分解为多个独立的小设计,这样的话FPGA设计的功能就无法通过协作来扩展。
那么当两个不同时钟域之间进行信息交互的时候,到底会存在什么问题呢?
首先介绍亚稳态:
触发器的建立时间和保持时间在时钟上升沿左右定义了一个时间窗口,如果触发器的数据输入端口上数据在这个时间窗口内发生变化(或者数据更新),那么就会产生时序违规。存在这个时序违规是因为建立时间要求和保持时间要求被违反了,此时触发器内部的一个节点(或者要输出到外部的节点)可能会在一个电压范围内浮动,无法稳定在逻辑0或者逻辑1状态。换句话说,如果数据在上述窗口中被采集,触发器中的晶体管不能可靠地设置为逻辑0或者逻辑1对应的电平上。所以此时的晶体管并未处于饱和区对应的高或者低电平,而是在稳定到一个确定电平之前,徘徊在一个中间电平状态(这个中间电平或许是一个正确值,也许不是)。
那么当两个不同时钟域之间进行信息交互的时候,遇到的情况:
方法:脉冲展宽和跨时钟域:脉冲展宽,将快时钟域脉冲信号拓展几个时钟周期,至少俩倍于慢时钟域周期。
module single_sync_fast2slow#( parameter P_CLK_NUM = 1 //慢时钟与快时钟之间的比值 )( input i_clk_a , input i_rst_a , input i_single_a , input i_clk_b , input i_rst_b , output o_single_b ); /*--------clk_a--------*/ reg r_single_a ; reg [7:0] r_cnt_a ; always @(posedge i_clk_a or posedge i_rst_a) begin if(i_rst_a) r_single_a <= 'd0; else if(r_cnt_a == 2 * P_CLK_NUM) r_single_a <= 'd0; else if(i_single_a) r_single_a <= 1; else r_single_a <= r_single_a; end always @(posedge i_clk_a or posedge i_rst_a) begin if(i_rst_a) r_cnt_a <= 'd0; else if(r_cnt_a == 2 * P_CLK_NUM) r_cnt_a <= 'd0; else if(i_single_a || r_cnt_a > 0) r_cnt_a <= r_cnt_a + 1; else r_cnt_a <= r_cnt_a; end /*--------clk_b--------*/ reg r_single_b1,r_single_b2; assign o_single_b = r_single_b2; always @(posedge i_clk_b or posedge i_rst_b) begin if(i_rst_b)begin r_single_b1 <= 'd0; r_single_b2 <= 'd0; end else begin r_single_b1 <= r_single_a; r_single_b2 <= r_single_b1; end end endmodule
P_CLK_NUM参数表示快慢时钟周期之间的比值,要想百分百被慢时钟域正确采集,至少要在快时钟域内将脉冲宽度拓展至慢时钟周期俩倍,因此脉冲拓宽拓宽计数器需要满足:
r_cnt_a == 2 * P_CLK_NUM
仿真波形图:
方法:打俩拍:
如果快时钟域频率大于俩倍慢时钟域频率 f1 > 2 * f2 ,那么打俩拍后快时钟域频率肯定是可以正确采到慢时钟域信号,但如果不满足此条件,打俩拍也可以大大降低亚稳态,保险起见也可以先拓展脉冲宽度,然后再采集。
方法:握手协议:
具体过程:
当时钟域A产生一个脉冲后,紧接着拉高一个传输信号trans_a,直到收到B时钟域的响应信号然后拉低,那么B时钟域必然是可以采集到信号trans_a的,因为它一直为高,一旦检测到trans_a,则拉高r_single_b1,检测r_single_b1的上升沿,即可在B时钟域下也产生一个脉冲,随后产生一个响应信号r_ack_b,r_ack_b在时钟域B下的持续周期分俩种情况讨论:
localparam P_CNT_END_B = P_CLK_FRQ_A >= P_CLK_FRQ_B ? 2 : ((P_CLK_FRQ_B/P_CLK_FRQ_A) * 2 + 1);
完整代码:
module single_sync_module#( parameter P_CLK_FRQ_A = 50_000_000, parameter P_CLK_FRQ_B = 50_000_000 )( input i_clk_a , input i_rst_a , input i_single_a , input i_clk_b , input i_rst_b , output o_single_b ); localparam P_CNT_END_B = P_CLK_FRQ_A >= P_CLK_FRQ_B ? 2 : ((P_CLK_FRQ_B/P_CLK_FRQ_A) * 2 + 1); /*--------clk_a--------*/ reg r_trans_a ; reg r_ack_a1 ; reg r_ack_a2 ; /*--------clk_b--------*/ reg r_single_b ; reg r_single_b1 ; reg r_single_b2 ; reg r_ack_b ; reg [7:0] r_cnt_b ; wire w_single_b_pos ; assign o_single_b = r_single_b ; assign w_single_b_pos = r_single_b1 & !r_single_b2; /*--------clk_a--------*/ always @(posedge i_clk_a or posedge i_rst_a) begin if(i_rst_a) r_trans_a <= 'd0; else if(r_ack_a2) r_trans_a <= 'd0; else if(i_single_a) r_trans_a <= 1; else r_trans_a <= r_trans_a; end always @(posedge i_clk_a or posedge i_rst_a) begin if(i_rst_a)begin r_ack_a1 <= 'd0; r_ack_a2 <= 'd0; end else begin r_ack_a1 <= r_ack_b; r_ack_a2 <= r_ack_a1; end end /*--------clk_b--------*/ always @(posedge i_clk_b or posedge i_rst_b) begin if(i_rst_b)begin r_single_b1 <= 'd0; r_single_b2 <= 'd0; end else if(r_trans_a)begin r_single_b1 <= 'd1; r_single_b2 <= r_single_b1; end else begin r_single_b1 <= 'd0; r_single_b2 <= 'd0; end end always @(posedge i_clk_b or posedge i_rst_b) begin if(i_rst_a) r_single_b <= 'd0; else if(w_single_b_pos) r_single_b <= 1; else r_single_b <= 'd0; end always @(posedge i_clk_b or posedge i_rst_b) begin if(i_rst_a) r_ack_b <= 'd0; else if(r_cnt_b == P_CNT_END_B - 1) r_ack_b <= 'd0; else if(w_single_b_pos) r_ack_b <= 1; else r_ack_b <= r_ack_b; end always @(posedge i_clk_b or posedge i_rst_b) begin if(i_rst_a) r_cnt_b <= 'd0; else if(r_cnt_b == P_CNT_END_B - 1) r_cnt_b <= 'd0; else if(r_ack_b) r_cnt_b <= r_cnt_b + 1; else r_cnt_b <= 'd0; end endmodule
波形图,先看A慢B快的情况:
A频率为50Mhz,B频率为200Mhz,所以B的响应信号r_ack_b持续了9个时钟周期,其实这样子做的主要目的就是为了保证慢时钟域A一定存在一个上升沿可以在满足建立时间和保持时间的窗口内采集到响应信号。
波形图,A快B慢的情况:
A频率为200Mhz,B频率为50Mhz,所以B的响应信号r_ack_b持续了2个时钟周期,这足以保证时钟域A采集响应信号,甚至一个时钟周期都够了,当然这主要是为了确保r_ack_b宽度至少大于俩倍的时钟域A周期。
以上单bit跨时钟处理方法主要是适用于不连续触发的信号,如果连续触发,由于同步一次需要消耗很多时钟周期,会大量漏采数据,不过一般单bit数据主要是控制信号。
如若碰到了连续触发的情况,则需要采用异步RAM和异步FIFO了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。