FPGA跨时钟域的处理方法

一、时钟域
如果一个设计全局只使用了一个时钟,那么此设计有一个时钟域。如果在一个设计中有两个时钟去控制不同的接口,那么就称这个设计中有两个时钟域。如下图所示FPGA跨时钟域的处理方法_第1张图片
当时钟不匹配时,就要进行同步化,否则就可能出现亚稳态,从而造成整个设计不稳定。
二、亚稳态
亚稳态其实就是数据的转变没有符合时钟采样所需要的setup/hold时间,在时钟的上升沿或下降沿到来时正好采到数据的变化状态。此时,由于数据并没有稳定,所以会导致采到的数据不停变化,而不是逻辑0或逻辑1.此时采到的数据会一直抖动,直至隔一段时间稳定。产生亚稳态的时序图如下:FPGA跨时钟域的处理方法_第2张图片
处理亚稳态的方法:
1、相位控制
2、多级寄存器
3、异步FIFO缓存
多级触发寄存器处理
如果信号来自同一时钟域,则不需要多级触发器处理,如果来自两个时钟域,那么分为两种情况:
1、从快到慢
2、从慢到快
此处的快慢指时钟的频率,首先说从快到慢的处理方法:
其实此处也分信号所来的时间长短与慢的时钟的频率关系。
有以下两种可能FPGA跨时钟域的处理方法_第3张图片
从快到慢
右图中的pulse_a信号是不会被clk_b采集到的,因为pulse_a信号的保持时间小于clk_b的时钟周期,所以要从clk_a的时钟域下将此信号采集过来就必须对其进行展宽,否则极有可能采集不到。

 module  Sync_Pulse(
                                         input              clk_a,
                                         input              clk_b,
                                         input              rst_n,
                                         input              pulse_a_in,
                                         output           pulse_b_out,
                                         output           b_out
                                          );
  /**************************************************************************************/
  reg                          signal_a;
  reg                          signal_b;
  reg                          signal_b_s;
  reg                          signal_b_ss;
  reg                          signal_b_a1;
  reg                          signal_b_a2;

//在时钟域clk_a下,生成展宽信号signal_a
    always @ (posedge clk_a or negedge rst_n)
        begin
            if (rst_n == 1'b0)
                signal_a <= 1'b0;
            else if (pulse_a_in)            //检测到到输入信号pulse_a_in被拉高,则拉高signal_a
                signal_a <= 1'b1;
            else if (signal_b_a2)           //检测到signal_b1_a2被拉高,则拉低signal_a
                signal_a <= 1'b0;
            else;
        end
//在时钟域clk_b下,采集signal_a生成signal_b

always@(posedge clk_b or negedge rst_n)begin
            if(rst_n == 1'b0)begin
                signal_b <= 0;
           end
           else begin
               signal_b <= signal_a;
           end
end
//多级触发器将clk_b抓到的signal_b信号打两拍输出
always@(posedge clk_b or negedge rst_n)begin
       if(rst_n == 1'b0)begin
       signal_b_s  <= 1'b1;
       signal_b_ss <= 1'b1;
       end
       else begin
       signal_b_s  <= signal_b;
       signal_b_ss <= signal_b_s;
       end
end
//在时钟域clk_a下,采集signal_b_s,用于反馈来拉低展宽信号signal_a
always@(posedge clk_a or negedge rst_n)begin
  if(rst_n == 1'b0)begin
      signal_b_a1 <= 1'b0;
      signal_b_a2 <= 1'b0;
  end
  else begin    //对signal_b_s打两拍,因此处涉及到跨时钟域
      signal_b_a1 <= signal_b_s;
      signal_b_a2 <= signal_b_a1;
  end
end
assign   pulse_b_out = signal_b_s & (~signal_b_ss);
assign   b_out            = signal_b_s;

endmodule

从慢到快
FPGA跨时钟域的处理方法_第4张图片
在clk_a时钟看来,pulse_b信号是一个非常宽的信号,那么在clk_b时钟域下的pulse_b必然能够被clk_a采到。如果pulse_b是clk_b时钟域下的组合逻辑所产生的信号,那么就得先用DFF在clk_b时钟抓一拍,之后再用DFF抓两次或者更高次向clk_a时钟域传递。

module  sys_clk(
                  input    clk_a,
                  input    clk_b,
                  input    pules_in,
                  input    rst_n,
                  output   pules_out 
                 );
reg         pules_b;
reg         pules_a1;
reg         pules_a2;
reg         pules_a3;
wire        pules_a_pos;
wire        pules_a_neg;
always@(posedge clk_b or negedge rst_n)begin
if(rst_n == 1'b0)begin
   pules_b <= 1'b0;
end
else  begin
   pules_b <= 1'b1;
end
end
always@(posedge clk_a or negedge rst_n)begin
if(rst_n == 1'b0)begin
  pules_a1 <= 1'b0;
  pules_a2 <= 1'b0;
  pules_a3 <= 1'b0;
end
else begin
  pules_a1 <= pules_b;
  pules_a2 <= pules_a1;
  pules_a3 <= pules_a2;
end
end
assign  pules_out = pules_a2;
assign pules_a_pos = pules_a2 & (~pules_a3); //输出信号的上升沿检测
assign pules_a_neg = pules_a3 & (~pules_a2); //下降沿检测

endmodule

需要强调的是,此时的pules_必须为clk_b时钟域下的信号**

在设计中可以简单的牢记以下五条原则:
1. 再全局时钟的跳变沿最可靠。
2. 来自异步时钟域的输入需要寄存一次以同步化,再寄存一次以减少亚稳态带来的影响。
3. 不需要用到跳变沿的来自同一时钟域的输入,没有必要对信号进行寄存。
4. 需要用到跳变沿的来自同一时钟域的输入,寄存一次即可。
5. 需要用到跳变沿的来自不同时钟域的输入,需要用到3个触发器,前两个用以同步,第3个触发器的输出和第2个的输出经过逻辑门来判断跳变沿

你可能感兴趣的:(FPGA跨时钟域的处理方法)