当前位置: 首页 > 教程 > Verilog >

4.2 Verilog 跨时钟域传输:慢到快

精华
小牛编辑
134浏览
2023-03-14

理论上讲,快时钟域的信号总会采集到慢时钟域传输来的信号,如果存在异步可能会导致采样数据出错,所以需要进行同步处理。此类同步处理相对简单,一般采用延迟打拍法,或延迟采样法。

延迟打拍法

最常用的同步方法是双级触发器缓存法,俗称延迟打拍法。异步信号从一个时钟域进入另一个时钟域之前,将该信号用两级触发器连续缓存两次,可有效降低因为时序不满足而导致的亚稳态问题。电路示意图如下。

一般设计中使用两级触发器进行缓存即可满足设计时序需求。大量实验表明,三级触发器缓存可解决 99% 以上的此类异步时序问题。

两级触发器延迟打拍并检测信号上升沿的 Verilog 描述如下:

实例

module delay_clap (
    input       clk1 ,   //异步慢时钟
    input       sig1 ,   //异步信号

    input       rstn ,   //复位信号
    input       clk2 ,   //目的快时钟域市政
    output      sig2 ) ; //快时钟域同步后的信号

    reg [ 2 : 0 ]    sig2_r ;   //3级缓存,前两级用于同步,后两节用于边沿检测
    always @ ( posedge clk2 or negedge rstn ) begin
      if ( !rstn ) sig2_r   <= 3'b0 ;
      else       sig2_r   <= {sig2_r [ 1 : 0 ] , sig1 } ;   //缓存
    end
    assign sig2 = sig2_r [ 1 ] && !sig2_r [ 2 ] ; //上升沿检测

延迟采样法

此方法主要针对多位宽的数据传输。

例如当两个异步时钟频率比为 5 时,可以先用延迟打拍的方法对数据使能信号进行 2 级打拍缓存,然后再在快时钟域对慢时钟域的数据信号进行采集。

该方法的基本思想是保证信号被安全采集的时刻,而不用同步多位宽的数据信号,可节省部分硬件资源。

利用打拍的方法进行延迟采样的 Verilog 描述如下。

实例

//同步模块工作时钟为 100MHz 的模块
//异步数据对来自工作时钟为 20MHz 的模块
module delay_sample (
    input               rstn ,
    input               clk1 ,
    input [ 31 : 0 ]        din ,
    input               din_en ,

    input               clk2 ,
    output [ 31 : 0 ]       dout ,
    output              dout_en ) ;

    //sync din_en
    reg [ 2 : 0 ]    din_en_r ;
    always @ ( posedge clk2 or negedge rstn ) begin
      if ( !rstn ) din_en_r   <= 3'b0 ;
      else       din_en_r   <= {din_en_r [ 1 : 0 ] , din_en } ;
    end
    wire din_en_pos = din_en_r [ 1 ] && !din_en_r [ 2 ] ;

    //sync data
    reg [ 31 : 0 ]           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 时刻要安全的多。

但如果慢时钟域没有数据使能信号 din_en, 或数据使能信号一直有效,此时在快时钟域对数据使能信号进行上升沿检测的方法将会失效。因为数据使能信号一直有效,除了第一个数据,快时钟域将无法检测到后继数据的传输时刻。

解决方法就是,在快时钟域对慢时钟信号的边沿进行检测。

如果两个时钟的频率相差较小,可能还需要对数据进行延迟缓存,以保证采集到的是当拍时钟的数据;如果两个时钟的频率相差较大,数据采样时刻可以通过计数的方法获得,而不用对数据进行缓存。

利用计数延迟采样的方法对慢时钟边沿进行检测的 Verilog 描述如下。

实例

//同步模块工作时钟为 100MHz 的模块
//异步数据对来自工作时钟为 999KHz 的模块
module delay_cnt_sample (
    input               rstn ,
    input               clk1 ,
    input [ 31 : 0 ]        din ,
    input               din_en ,

    input               clk2 ,
    output [ 31 : 0 ]       dout ,
    output              dout_en ) ;

    //4级缓存:3级用于打拍同步,一级用于边沿检测
    reg [ 3 : 0 ]    edge_r ;
    always @ ( posedge clk2 or negedge rstn ) begin
      if ( !rstn ) edge_r   <= 3'b0 ;
      else       edge_r   <= {edge_r [ 3 : 0 ] , clk1 } ;
    end
    wire edge_pos = edge_r [ 2 ] && !edge_r [ 3 ] ;

    //延迟计数器,检测到慢时钟上升沿时开始计数
    reg [ 5 : 0 ] 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 [ 31 : 0 ]           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

频率相差较大的数据同步采样结果图如下。

由图可知,快时钟采样时刻在慢时钟周期中央时刻左右,此时是非常安全的。

本章节源码下载

Download