查看: 26|回复: 0

跨时钟域传输

[复制链接]
  • TA的每日心情

    2024-11-15 16:19
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    292

    主题

    26

    回帖

    2978

    积分

    管理员

    积分
    2978
    发表于 2024-11-15 11:49:11 | 显示全部楼层 |阅读模式
    延迟打拍法
    最常用的同步方法是双级触发器缓存法,俗称延迟打拍法。异步信号从一个时钟域进入另一个时钟域之前,将该信号用两级触发器连续缓存两次,可有效降低因为时序不满足而导致的亚稳态问题。电路示意图如下。
    一般设计中使用两级触发器进行缓存即可满足设计时序需求。大量实验表明,三级触发器缓存可解决 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
    <button class="copy-code-button" type="button" data-clipboard-text="//同步模块工作时钟为 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      

    频率相差较大的数据同步采样结果图如下。
    由图可知,快时钟采样时刻在慢时钟周期中央时刻左右,此时是非常安全的。

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    友情链接:

    返回顶部 返回列表