【【深入浅出跨时钟域异步处理方法】】

【【深入浅出跨时钟域异步处理方法】】

两个异步跨时钟域下的信号并不会完全保持隔离,有时候也需要交流。所以怎么处理好两个时钟域下的时序问题就显得尤为关键。
这就需要用到一种名为跨时钟域处理的方法( Clock Domain Crossing CDC ) 处理
信号可以分为电平信号和脉冲信号,也可以分为单比特信号和总线信号。

单比特电平信号的跨时钟域处理方法 : 背靠背跨时钟域处理方法

将一个信号分别打了两拍 :

对于单比特脉冲信号的跨时钟域处理,需要比较源时钟和目的时钟的时钟快慢关系。

一 : 如果源时钟相比目的时钟慢,则源时钟的脉冲信号相比目的时钟来说是一个电平信号。这种跨时钟域的处理处理先按照上述的方法进行背靠背结构将脉冲信号打进来,然后再取得这样一个信号的上升沿或下降沿,就能转变成目的时钟域的脉冲信号 。

assign a_fall = (~a_latch) &  a_latch_r    ;   // 采样下降沿 新为0 旧为1 
assign a_rise = (a_latch)  & (~a_latch_r)  ;   // 采样上升沿 新为1 旧为0 

二 : 若源时钟要比目的时钟快,则源时钟上的脉冲信号,在目的时钟看来可能会变成一个毛刺,因此为了处理这样的问题,需要进行如下的操作
下面附上这里的代码


```c

```c

```c
module clk1toclk2(   //  怎么系统的把一个时钟域下的a 转化成另一个时钟域下的b
    input            clk1    ,
    input            clk2    ,
    input            a       ,
    input            rst1_n  ,
    input            rst2_n  ,
    output           b       ,
    output           busy
  );

  reg          a_latch          ;
  reg          b_latch          ;
  reg          b_latch_r        ;
  reg          b_latch_2r       ;
  reg          b_feedback_latch ;
  reg          c_latch          ;
  reg          c_latch_r        ;
  

  //  ----------------------------------
  always@(posedge clk1 or negedge rst1_n)
  begin
    if(rst1_n == 0)
      a_latch   <=  0 ;
    else if(a == 1)        // 现在clk1 的时钟域把a变成电平
      a_latch   <=  1 ;
    else if(c_latch_r == 1)
      a_latch <= 1'b0  ;
  end

  always@(posedge clk2 or negedge rst2_n)
  begin
    if( rst2_n == 0)
    begin
      b_latch      <=  1'b0  ;
      b_latch_r    <=  1'b0  ;
      b_latch_2r   <=  1'b0  ;
    end
    else
    begin
      b_latch    <=  a_latch   ;           //  跨时钟第一拍,容易产生亚稳态
      b_latch_r  <=  b_latch   ;           //  跨时钟第二拍,几乎没有亚稳态
      b_latch_2r <=  b_latch_r ;           //  为了转换为脉冲而打拍
    end
  end


  assign  b = b_latch_r & (~b_latch_2r) ;  // 抓取脉冲信号

  always@(posedge  clk2 or negedge rst2_n)
  begin
    if(rst2_n == 0)
      b_feedback_latch  <=  1'b0 ;
    else if(b == 1)
      b_feedback_latch  <= 1 ;
    else if(~b_latch_r)
      b_feedback_latch  <=  0 ;
  end


  always@(posedge clk1 or negedge rst1_n)
  begin
    if(rst1_n == 0)
    begin
      c_latch   <=  1'b0  ;
      c_latch_r <=  1'b0  ;
    end
    else
    begin
      c_latch    <=  b_feedback_latch ;
      c_latch_r  <=  c_latch   ;
    end
  end

  assign  busy = a_latch | c_latch_r  ;    // busy 信号是给前级让其不能输出的意思 

endmodule

观察发现,先将输入a在时钟源clk1下转换成电平信号 a_latch
然后使用前文介绍的背靠背打拍方法,在clk2上打拍两次,第一次得到b_latch,第二次得到b_latch_r
再使用前文介绍的给电平取脉冲的方法,抓取b_latch_r的脉冲得到信号b,这样就可以把脉冲信号a跨时钟域得到脉冲信号b
此时握手过程尚未结束,因为各个latch还在latch中,需要让它们都回到0
方法是: 先将目的时钟域的b信号也latch住,得到b_feedback_latch(即脉冲变成电平信号)
再通过打拍将其同步到clk1上先得到c_latch 再得到c_latch_r
当a_latch看到c_latch_r为1,说明目的时钟域的脉冲信号b已成功生成,a_latch就恢复为0,再通过打拍传递使得b_latch_r也为0。b_feedback_latch看到b_latch_r为0,则它也变为0,再经过打拍传递,最终c_latch_r也为0 .

三 : 对于总线信号的跨时钟域传输

对于总线信号的跨时钟域处理有两种方式,一种是异步FIFO,另一种是vld握手
异步fifo天生就可以处理这种问题,而在有些需要更为简单的处理方式允许源时钟域在数据到达目的时钟域之前可以等待一段时间,在这段时间内不会再过来新的数据。当vld跨时钟完毕之后,在目的的时钟域上用跨时钟后的vld直接采样总线信号即可。
下面介绍一个从快到慢的总线跨时钟处理代码
其实处理的效果就是和之前的差不多

module  clk12clk2_2(
    input                  clk1   ,
    input                  clk2   ,
    input                  rst1_n ,
    input                  rst2_n ,
    input                  vld1   ,    // 其实相对来说vld其实是一个脉冲信号
    input    [7 : 0]       data1  ,
    output                 vld2   ,
    output reg  [7 : 0]    data2
  );


  reg               vld1_latch           ;
  reg               vld2_latch           ;
  reg               vld2_latch_r         ;
  reg               vld2_latch_2r        ;
  reg               vld2_feedback_latch  ;
  reg               c_latch              ;
  reg               c_latch_r            ;
  wire              busy                 ;

  // 这是一个快到慢的使用形式
  always@(posedge clk1 or negedge rst1_n )
  begin
    if(rst1_n == 0)
      vld1_latch  <=  1'b0 ;
    else if(vld1 == 1)
      vld1_latch  <=  1'b1 ;     // 先把vld1脉冲转化成电平信号
    else if(c_latch_r == 1)
      vld1_latch  <=  1'b0  ;
  end



  always@(posedge  clk2 or  negedge rst2_n)
  begin
    if(rst2_n == 0)
    begin
      vld2_latch    <=  1'b0 ;
      vld2_latch_r  <=  1'b0 ;
      vld2_latch_2r <=  1'b0 ;
    end
    else
    begin     // 电平跨时钟
      vld2_latch    <=  vld1_latch   ;
      vld2_latch_r  <=  vld2_latch   ;
      vld2_latch_2r  <=  vld2_latch_r ;
    end
  end

  assign vld2 = vld2_latch_r &(~vld2_latch_2r)  ;  // 提取上升沿获得 vld2

  always@(posedge clk2 or negedge rst2_n )
  begin
    if(rst2_n == 0)
      vld2_feedback_latch  <=  1'b0 ;
    else if(vld2)
      vld2_feedback_latch  <=  1'b1 ;
    else if(~vld2_latch_r)
      vld2_feedback_latch <=  1'b0 ;
  end

  always@(posedge clk1 or negedge rst1_n )
  begin
    if(!rst1_n )
    begin
      c_latch    <=  1'b0 ;
      c_latch_r  <=  1'b0 ;
    end
    else
    begin
      c_latch    <=  vld2_feedback_latch  ;
      c_latch_r  <=  c_latch              ;
    end
  end

  assign  busy = vld1_latch | c_latch_r ;

  always@(posedge clk2 or negedge rst2_n)
  begin
    if(rst2_n == 0)
      data2 <= 8'b0 ;
    else if(vld2)
      data2 <= data1 ;
  end

endmodule 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值