【CDC跨时钟域信号处理】快时钟域到慢时钟域-单bit

快时钟域到慢时钟域分两种情况:
1、允许采样丢失:直接采用同步器即可。
2、不允许采样丢失:原理是保证快时钟域的信号宽度满足一定的条件,使得慢时钟域有足够的时间采样到。
对于情况2有两种方法解决:①信号展宽+边沿检测②握手,且①比②要优先被选择。因为握手资源消耗较大,一般不用。

方法一

脉冲信号展宽+边沿检测,脉冲信号转换成电平信号再进行边沿检测

  • 电路图:
    【CDC跨时钟域信号处理】快时钟域到慢时钟域-单bit_第1张图片
  • 代码:(verilog是描述电路的语言,所以要心中有电路,代码就好写了)
module pulse_detect(
    input              clk_fast    , 
    input              clk_slow    ,   
    input              rst_n       ,
    input               data_in     ,
    output           dataout
);
  reg data_in_fast;
  reg [2:0] data_slow;
  //将脉冲信号在快时钟域展平为电平信号。即展宽脉冲信号。在两次脉冲信号之间为电平信号。
  always@(posedge clk_fast or negedge rst_n)begin
      if(!rst_n)
          data_in_fast<= 0;
      else
          data_in_fast<= data_in ? (~data_in_fast) : data_in_fast;
  end
  //将展宽的脉冲信号在慢时钟域打两拍,并检测边沿。
  always@(posedge clk_slow or negedge rst_n)begin
      if(!rst)
          data_slow <= 3'b0;
      else
          data_slow <= {data_slow[1:0],data_in_reg};
  end   
  
  assign dataout = data_slow[2] ^ data_slow[1]; 
endmodule

  • 波形
    【CDC跨时钟域信号处理】快时钟域到慢时钟域-单bit_第2张图片

方法二

握手+边沿

  • 电路图:
    【CDC跨时钟域信号处理】快时钟域到慢时钟域-单bit_第3张图片
  • 代码1:
module pulse_detect(
  input         clk_fast  , 
  input         clk_slow  ,   
  input         rst_n    ,
  input        data_in    ,

  output         dataout
);
    //握手方式
    reg fast_req;//fast时钟域的请求信号
    reg slow_ack;//slow时钟域的应答信号
    reg [2:0] slow_req;//slow时钟域的请求信号
    reg [2:0] fast_ack;//fast时钟域的应答信号
    
    //fast时钟域
    
    //将slow时钟域的应答信号打三拍,送到fast时钟域
    always@(posedge clk_fast or negedge rst_n)begin
        if(!rst_n)
            fast_ack <= 3'd0;
        else
            fast_ack <= {fast_ack[1:0],slow_ack};
    end
    
    //生成请求信号fast_req
    always@(posedge clk_fast or negedge rst_n)begin
        if(!rst_n)
            fast_req <= 0;
        else if(data_in)
            fast_req <= 1'b1;
        else
            //快时钟域中没有输入数据时
            //如果慢时钟域应答了,01x,则快时钟域此时不请求,否则,快时钟域的请求信号维持上一时钟的状态
            fast_req <= ((fast_ack[1]) & (~fast_ack[2])) ? 1'b0 : fast_req;        
    end
    
    //slow时钟域
    
    //将fast时钟域的请求信号打三拍,送到slow时钟域
    always@(posedge clk_slow or negedge rst_n)begin
        if(!rst_n)
            slow_req <= 0;
        else
            slow_req <= {slow_req[1:0],fast_req};
    end
    
    //生成应答信号slow_ack
    always@(posedge clk_slow or negedge rst_n)begin
        if(!rst_n)
            slow_ack <= 0;
        else if(slow_req[1] & (~slow_req[2])) 
            //如果快时钟域的请求信号由0变为1,01x,即发出请求信号,则慢时钟域进行应答
            slow_ack <= 1'b1;
        else
            //如果slow请求信号10x,即慢时钟域请求信号无效时,慢时钟域不应答
            //否则慢时钟域应答信号不变
            slow_ack <= (slow_req[2]&(~slow_req[1])) ? 1'b0 : slow_ack;
    end
    //当慢时钟域01x,发出请求信号时,输出为1.
    assign dataout = (~slow_req[2]) & (slow_req[1]);
    
endmodule
  • 代码2:
module Sync_Pulse (
                input  src_clk,
                input  dst_clk,
                input  rst_n,
                input  src_pulse,
   
                output  dst_pulse
                  );
  
    reg req_state_dly1, req_state_dly2,dst_req_state,src_sync_req;
    reg ack_state_dly1,src_sync_ack;
    wire dst_sync_ack;
     
    always @ (posedge src_clk or negedge rst_n) begin
            if (rst_n == 1'b0)
                src_sync_req <= 1'b0;
            else if (src_pulse)           
                src_sync_req <= 1'b1;
            else if (src_sync_ack)           
                src_sync_req <= 1'b0;
            else;
     end
    
   
    always @ (posedge dst_clk or negedge rst_n) begin
            if (rst_n == 1'b0)
            begin
                req_state_dly1 <= 1'b0;
                req_state_dly2 <= 1'b0;
                dst_req_state <= 1'b0;
            end else begin
                req_state_dly1 <= src_sync_req;
                req_state_dly2 <= req_state_dly1;        
                dst_req_state <= req_state_dly2;
             end
    end
    
    assign dst_sync_ack = req_state_dly2;
    
    always @ (posedge src_clk or negedge rst_n) begin
            if (rst_n == 1'b0) begin
                    ack_state_dly1 <= 1'b0;
                    src_sync_ack <= 1'b0;
            end
            else begin
                     ack_state_dly1 <= dst_sync_ack;
                     src_sync_ack <= ack_state_dly1;
            end
     end
 
    assign  dst_pulse =   dst_req_state & (~req_state_dly2);
 
endmodule

对上述代码增加同步失败的指示信号

module handshake_pulse_sync
(
        input src_clk , //source clock
        input src_rst_n, //source clock reset (0: reset)
        input src_pulse , //source clock pulse in
       
        output src_sync_fail , //source clock sync state: 1 clock pulse if sync fail.
        input dst_clk , //destination clock
        input dst_rst_n , //destination clock reset (0:reset)
        output dst_pulse //destination pulse out
);
        //INTER DECLARATION        
        wire dst_pulse ;
        wire src_sync_idle ;
        reg src_sync_fail ;
        reg src_sync_req ;
        reg src_sync_ack ;
        reg ack_state_dly1, ack_state_dly2 ;
        reg req_state_dly1, req_state_dly2 ;
        reg dst_req_state ;
        reg dst_sync_ack ;
              
        assign src_sync_idle = ~(src_sync_req | src_sync_ack );
        
        //report an error if src_pulse when sync busy ;
        always @(posedge src_clk or negedge src_rst_n) begin
            if(src_rst_n == 1'b0)
                    src_sync_fail <= 1'b0 ;
            else if (src_pulse & (~src_sync_idle))
                    src_sync_fail <= 1'b1 ;
            else
                    src_sync_fail <= 1'b0 ;
        end
        
        
        //set sync req if src_pulse when sync idle ;
        always @(posedge src_clk or negedge src_rst_n) begin
                if(src_rst_n == 1'b0)
                        src_sync_req <= 1'b0 ;
                else if (src_pulse & src_sync_idle)
                        src_sync_req <= 1'b1 ;
                else if (src_sync_ack)
                        src_sync_req <= 1'b0 ;
        end
                
        always @(posedge src_clk or negedge src_rst_n) begin
                if(src_rst_n == 1'b0) begin
                        ack_state_dly1 <= 1'b0 ;
                        ack_state_dly2 <= 1'b0 ;
                        src_sync_ack <= 1'b0 ;
                end
                else begin
                        ack_state_dly1 <= dst_sync_ack ;
                        ack_state_dly2 <= ack_state_dly1 ;
                        src_sync_ack <= ack_state_dly2 ;
                end
        end
        
        always @(posedge dst_clk or negedge dst_rst_n) begin
                if(dst_rst_n == 1'b0) begin
                        req_state_dly1 <= 1'b0 ;
                        req_state_dly2 <= 1'b0 ;
                        dst_req_state <= 1'b0 ;
                end
                else begin
                        req_state_dly1 <= src_sync_req ;
                        req_state_dly2 <= req_state_dly1 ;
                        dst_req_state <= req_state_dly2 ;
                end
        end
        
        //Rising Edge of dst_state generate a dst_pulse;
        assign dst_pulse = (~dst_req_state) & req_state_dly2 ;
        //set sync ack when src_req = 1 , clear it when src_req = 0 ;
        
        always @(posedge dst_clk or negedge dst_rst_n) begin
                if(dst_rst_n == 1'b0)
                        dst_sync_ack <= 1'b0;
                else if (req_state_dly2)
                        dst_sync_ack <= 1'b1;
                else
                        dst_sync_ack <= 1'b0;
        end
endmodule

电路分析:
将src时钟域的脉冲信号打一拍之后,在dst时钟域内打三拍进行同步,其中第二拍的结果作为dst时钟域的应答信号,第二拍和第三拍的结果做边沿检测,以保证在dst时钟域输出接收到的脉冲信号。

其中第二拍的应答信号,在src时钟域经过两级同步作为src的应答信号,当输入脉冲信号时,src的请求信号有效。当src的应答信号有效时,请求信号无效。

缺点:上述操作无法检测到脉冲连续输入时产生的错误,即无法检测同步失败。如:在当前脉冲信号同步过程中又发出了新的脉冲信号的情况。

快时钟域至少展宽脉冲宽度到大于慢时钟域的周期长度。

你可能感兴趣的:(信号处理,fpga开发)