FPGA设计中的异步复位、同步释放思想

1.一个简单的异步复位例子:

module test(
  input clk,
  input rst_n,
  input data_in,
  output reg out
);

always@(posedge clk or negedge rst_n)
  if(!rst_n)
    out <= 0;
  else
    out <= data_in;

endmodule

综合结果如下:

我们可以看到,FPGA的寄存器都有一个异步清零端(CLR),在异步复位设计中,低电平有效的rst_n复位信号就可以直接连在这个端口上。(如果是高有效的复位,综合时会把它取反后接在这个端口上)

 2.一个同步复位的例子:

module test(
  input clk,
  input rst_n,
  input data_in,
  output reg out
);

always@(posedge clk)
  if(!rst_n)
    out <= 0;
  else
    out <= data_in;

endmodule

 综合结果如下:

我们可以看到,该电路中没有用到寄存器的CLR端,而是把rst_n复位信号作为输入逻辑的使能信号,那么这样的同步复位势必会额外增加FPGA内部的资源消耗。

3.那么,同步复位和异步复位到底孰优孰劣?

  实际上,同步复位和异步复位各有千秋。

  对于同步复位:

    优点:只在时钟上升沿(或下降沿)判断系统复位是否出现,降低了亚稳态的概率;

    缺点:就是之前说过的,额外增加了资源消耗;

  对于异步复位,它的优缺点刚好和同步复位相反。这里举个例子来说明异步复位带来的亚稳态问题:

module test(
    input clk,
    input rst_n,
    input a,
    output reg c
);
 
reg b;
always@(posedge clk or negedge rst_n)
       if(!rst_n) begin
            b <= 1'b0;
            c <= 1'b0;
       end
       else begin
            b <= a;
            c <= b;
        end         

endmodule      

综合结果如下:

  正常情况下,clk的上升沿c更新为b,b更新为a。一旦进入复位,b,c都清零;但是我们不能确定复位信号rst_n会在什么时候结束。如果结束于b_reg0和c_reg0的{launch edge –stup,launch edge+hold}时间只外,那么一切都会正常。但如果恰恰相反,会出现什么情况呢? rst_n的上升变化出现在了clk上升的建立保持时间上,此时clk检测到的rst_n的状态就会是一个亚稳态(是0是1不确定)。从代码里我们看到如果此时b_reg0和c_reg0认为rst_n为0,那么依然保持复位清零,而如果认为rst_n为1,那么就跳出复位。因为此时的rst_n的不确定性,就可能出现4种情况,即b_reg0和c_reg0都复位或者都跳出复位,再或者一个复位一个跳出复位。那么后者就会造成了系统工作不同步的问题,在这个简单的两级异步复位实例中这种危害表现的并不明显,但是我们试想一个大的工程项目里众多的寄存器出现如此情况又会是如何一番景象呢?

4.如何兼顾它们俩的优点,同时避免它们的缺点?

   答案是:异步复位,同步释放!

module test(
  input clk,
  input rst_n,
  input a,
  output reg c
);
 
reg b,rst_nr;
always@(posedge clk)
    rst_nr <= rst_n;  
            
always@(posedge clk or negedge rst_nr)
         if(!rst_nr) begin
        b <= 1'b0;
        c <= 1'b0;
      end
     else begin
       b <= a; 
       c <= b;
     end
                    
endmodule

综合结果如下:

如此一来,既解决了同步复位的资源消耗问题,也解决了异步复位的亚稳态问题。其根本思想,也是将异步信号同步化。

5.最终的改进型终极异步复位、同步释放电路

代码如下:

module test ( 
  input clk, 
  input rst_n, 
  output reg rst_out 
); 
reg R1; 
    
always@(posedge clk or negedge rst_n)
  if(!rst_n) begin
    R1 <= 1'b0;
    rst_out <= 1'b0;
  end
  else begin
    R1 <= 1'b1;  //这里写成1,而不写成rst_n,是因为根据实际综合效果来看,这样做更节省资源,具体原因参考同步复位
    rst_out <= R1;
  end

//可选
//wire rst_o;
//assign rst_o = rst_out;

endmodule

 综合结果如下:(这个例子只是单纯的复位电路,没有功能电路

  加上此复位电路以后,假设第一级D触发器clk上升沿时rst_n正好撤除,则D触发器1输出高电平“1”,此时第二级触发器也会更新输出,但是输出值为前一级触发器次clk来之前时的Q1输出状态。显然Q1之前为低电平,顾第二级触发器输出rst_out保持复位低电平,直到下一个clk来之后,才随着变为高电平。即同步释放。

只做一级触发器来同步,效果如何?

  第一级触发器的输出,永远存在亚稳态的可能。亚稳态导致系统不会复位初始化到已知状态。 
  当第一级触发器采样异步输入之后,允许输出出现的亚稳态可以长达一个周期,在这个周期内,亚稳态特性减弱。在第二个时钟沿到来时,第二级同步器采样,之后才把该信号传递到内部逻辑中去。第二级输出是稳定且已被同步了的。如果在第二级采样时保持时间不够,第一级的输出仍然处于很强的亚稳态,将会导致第二级同步器也进入亚稳态,但这种故障出现的概率比较小。

  一般情况下,两级同步器总体的故障概率是一级同步器故障概率的平方。在大部分的同步化设计中,两级同步器足以消除所有可能的亚稳态了。

 两级触发器做同步,是非总线信号的最常见异步处理方法。总线信号的异步处理方法,最常见的是异步fifo实现。

6.那么,到底什么情况下需要做“异步复位、同步释放”处理?

答案:一般来说,同步系统,都使用异步复位。这是因为同步复位的电路实现,比异步复位的电路实现,要浪费更多电路资源。 

  未在本模块时钟域做过“异步复位,同步释放”处理的复位信号,提供给本模块做异步复位使用时,都需要做“异步复位,同步释放”处理。常见于系统内两部件不在同一时钟域的情况下。

  工程实践中,确实见过由于未做异步复位的同步处理,而出现大概率系统死机现象(复位的作用域是很大的)。

  通常对异步复位电路的时序分析称为 恢复时间检查移除时间检查

想了解更多,可以参考该链接http://www.eefocus.com/coyoo/blog/13-12/301045_9c39f.html

原文地址:https://www.cnblogs.com/ylsm-kb/p/9069190.html