跨时钟域同步3---多bit信号同步(延迟采样法/慢到快)

一、有din_en信号&&频率相差较小

假设两个异步时钟频率比为 5, 我们可以先用延迟打拍的方法对数据使能信号进行 3 级打拍缓存以检测其上升沿,此时得到的上升沿信号刚好在数据使能信号的中间时刻附近,然后就可以在快时钟域对慢时钟域的数据信号进行采集了。具体对数据使能信号打几拍,需要根据快慢时钟的频率比来决定。

该方法的基本思想就是选择合适的时刻(例如数据的中间时刻)去采集信号,而不用同步多位宽的数据信号,可节省硬件资源。

module delay_sample(
    input               rstn,
    input               clk1,
    input [7:0]         din,
    input               din_en,

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

   //sync din_en
   reg din_en_r1 ;
   reg din_en_r2 ;
   reg din_en_r3 ;
   
   always @(posedge clk2 or negedge rstn) begin
     if (!rstn) begin
         din_en_r1 <= 0;
         din_en_r2 <= 0;
         din_en_r3 <= 0;
     end
     else begin
         din_en_r1 <= din_en;
         din_en_r2 <= din_en_r1;
         din_en_r3 <= din_en_r2;
     end    
   end

   //检测din_en上升沿
   wire din_en_pos = din_en_r2 && !din_en_r3 ;

   
   reg [7: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

在这里插入图片描述
由图可知,快时钟是在慢时钟域的数据有效时间段的中间时刻左右对数据进行采样的,这样采到的数据非常的安全。

二、有din_en信号&&频率相差较大

如果频率相差较大,那在设计一个计数器,在din_en的上升沿附近开始计数,根据频率比确定计数值到达多少时刚好处于din_en有效期的中间时刻附近,当计数到此值时对数据进行采样即可。

三、无din_en或din_en一直拉高

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

解决方法就是,在快时钟域对慢时钟信号的边沿进行检测。如果两个时钟的频率相差较小,可能还需要对数据进行延迟缓存,以保证采集到的是当拍时钟的数据(因为当频率相近时,第一个时钟边沿不容易被直接采样到,可能到下一个数据有效期间才能被采样到,所以应该将上一个数据延迟到此刻才能正确采样);如果两个时钟的频率相差较大,数据采样时刻可以通过计数的方法获得,而不用对数据进行缓存。

module delay_cnt_sample(
    input               rstn,
    input               clk1,
    input [7:0]         din,
    input               din_en,

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

   reg clk1_r1 ;
   reg clk1_r2 ;
   reg clk1_r3 ;
   
   
   always @(posedge clk2 or negedge rstn) begin
     if (!rstn) begin
         clk1_r1 <= 0;
         clk1_r2 <= 0;
         clk1_r3 <= 0;
     end
     else begin
         clk1_r1 <= clk1;
         clk1_r2 <= clk1_r1;
         clk1_r3 <= clk1_r2;
     end    
   end

   wire clk1_pos = clk1_r2 && !clk1_r3 ;

   //delay counter
   reg [5:0] cnt ;
   always @(posedge clk2 or negedge rstn) begin
      if (!rstn)                
            cnt <= 0 ;
      //每检测到新的慢时钟上升沿并且din_en有效,那么就预示着新的数据到来了,就要重新计数了
      else if (clk1_pos && din_en)
            cnt <= 0 ;
      else if (cnt != 6'b111111)    
            cnt <= cnt + 1'b1 ;
      else
            cnt <= cnt ;
   end

   //sync data
   reg [7:0]            dout_r ;
   reg                  dout_en_r ;
   always @(posedge clk2 or negedge rstn) begin
      if (!rstn)
        dout_r         <= 'b0 ;
      else if (din_en && cnt == 33)
        dout_r         <= din ;
   end
   
   
   always @(posedge clk2 or negedge rstn) begin
      if (!rstn)        
            dout_en_r      <= 1'b0 ;
      else if (din_en && cnt == 33)
            dout_en_r      <= 1'b1 ;
      else              
            dout_en_r      <= 1'b0 ;
   end
   
   assign       dout    = dout_r ;
   assign       dout_en = dout_en_r ;

endmodule

在这里插入图片描述
由图可知,快时钟是在慢时钟域的数据有效时间段的中间时刻左右对数据进行采样的,这样采到的数据非常的安全。

三、总结

无论是考虑打拍需要打多少拍,还是计数需要计多少次,都需要根据快慢时钟的实际频率比来决定。大多数情况下,我们的目标是保证在数据有效时间段的中间时刻去采样,这样采出的数据才非常准确安全。

posted @ 2021-10-29 22:29  耐心的小黑  阅读(1161)  评论(0编辑  收藏  举报