FPGA中复位设计总结

  1. 复位
    首先FPGA的复位,对于大部分设计而言,是很简单的。简单在于,只要按照固定方法:按键按下,复位有效一次,相关信号复位。这能解决大部分的问题。而对于其他大规模的复位设计,则需要对细节深究。

  2. 方式
    a. 外部复位:包括硬件按键复位,电源等芯片产生复位信号,软件复位。这些信号往往相对系统内的各个时钟会是异步的。
    b. 内部复位:内部自我产生的复位,形式表现于上电后自复位或者识别到某种内部命令后的复位。这些复位往往相对于某一时钟域是同步的。当存在多个时钟域的时候,相对于某一时钟,也可能是异步复位信号。
    作为一个正常的系统,上电自动复位和手动的按键复位都是必须的,相辅相成。

  3. 复位特性
    a. 复位网络需要占用布线资源;
    b. 导致其余信号的布线信号受到影响,降低了它们布线的自由度;
    c. 增加的布线网络往往需要使用更高速率的芯片;
    d. 复位网络占用大量布线资源,使得Place&Route的时间大大增加;
    e. 复位信号需要占用大量的逻辑资源;
    f. 复位信号需要使用触发器的专用复位管脚;
    g. 可操作的复位信号往往导致D触发器的输入前增加额外的门操作或专用的复位信号输入;
    h. 增大整个设计的尺寸;
    i. 额外的逻辑消耗降低了系统的性能;
    j. 阻止了使用高效特征,如Xilinx FPGA特有的SRL16E 移位寄存器。

    简单的说,复位的存在会对FPGA的综合面积产生影响,主要体现在两个方面:
    第一、在编写代码的时候,习惯给所有时序电路都加上复位信号,这样往往会导致资源无形的浪费;
    第二,没有合理的设计复位电路会产生额外的资源消耗。

  4. 同步复位
    复位信号可以理解为一个普通的数据信号,它只有在时钟的跳变沿才会其作用,一般只要复位信号持续时间大于一个时钟周期,就可以保证正确复位。具体的代码实现如下:
    always@(posedge clk)
    if(~rst_n)
    q<=0;
    else
    q<=d;
    优点:
    a. 有利于仿真
    b. 整个电路都是同步电路,时序分析较为简单,综合得到的时钟频率也较高
    c. 有利于消除毛刺信号
    缺点:
    a. 复位信号的有效时长必须大于时钟周期,才能真正的被系统识别并完成复位任务。
    b. 由于大多数的逻辑器件的目标库内的DFF都只有异步复位端口,所以,倘若采用同步复位的话,综合器就会在寄存器的数据输入端口插入组合逻辑,耗费较多的逻辑资源。

  5. 异步复位
    异步复位表现在,复位不随时钟沿限制。任何时刻有效,则产生复位。具体的代码实现如下:
    always@(posedge clk,negedge rst_n)
    if(!rst_n)
    q<=0;
    else
    q<=d;
    优点:
    a. 大多数的逻辑器件的目标库内的DFF都有异步复位端口,可以节省资源
    b. 设计相对简单
    c. 异步复位信号识别方便,而且可以很方便的使用FPGA的全局复位端口
    缺点:
    a. 在复位信号释放(reset)的时候容易出现问题。具体来说:倘若复位释放时恰恰在时钟有效沿附近,就很容易使寄存器输出出现亚稳态,从而导致亚稳态,具体亚稳态下面一节具体分析。
    b. 复位信号容易受到毛刺的影响。

  6. 异步复位出现亚稳态详解
    例子:
    always @ (posedge clk or negedge rst_n)
    if(!rst_n)
    b <= 1’b0;
    else
    b <= a;

     always @ (posedge clk or negedge rst_n)
     if(!rst_n) 
         c <= 1'b0;
     else 
         c <= b;
    

    正常情况下,clk的上升沿c更新为b,b更新为a。一旦进入复位,b,c都清零;但是我们不能确定复位信号rst_n会在什么时候结束。如果结束于b、c寄存器的{launch edge –setup,launch edge+hold}时间之外,那么一切都会正常。但如果恰恰相反,rst_n的上升沿出现在了clk上升沿的建立和保持时间区间内,此时clk检测到的rst_n的状态就会是一个亚稳态(是0是1不确定)。其实,说到底就是异步复位(以复位低电平有效)只会对下降沿敏感,对上升沿不敏感。这就导致复位释放的时候,也就是上升沿的时候会有不稳态。
    如果此时认为rst_n为0,那么b、c依然保持复位清零,而如果认为rst_n为1,那么就跳出复位。因为此时rst_n的不确定性,就可能出现4种情况,即b和c都复位或者都跳出复位,再或者一个复位一个跳出复位。那么后者就会造成一个系统工作不同步的问题,在这个简单的两级异步复位实例中这种危害表现的并不明显,但是我们试想一个大的工程项目里众多的寄存器出现如此情况又会是如何一番景象呢?
    为了解决这个问题,一个常见的方法就是采用异步复位,同步释放的机制。

  7. 复位的基本方案
    都知道异步复位是和时钟没有关系的。但需要注意的是,这只是局限于复位开始发生的时刻。根据条件always @ (posedge clk or negedge rst_n)来看,异步复位它所表达的是,一旦复位信号从无效到了有效,无论时钟什么状态都代表复位开始,执行下面的语句。这是异步复位不受时钟约束的具体内容,也就是说,异步复位的优势其实是复位开始的时刻不受时钟限制。
    而开始复位后,什么时候结束复位,其实是受到时钟约束的。因为一个复位周期,一般只有一次上升沿或者下降沿,一旦这个沿有效了,就不会再次出现边沿信号,而复位信号也就随之进入了漫长的高低电平态。此时信号会在复位有效的这段时间里一直处于复位状态,但是一旦复位信号无效的时候,因为检测信号无效的状态是由时钟上升沿来检测的,于是就会出现时钟沿和异步复位都在某个不稳定的边沿,造成了竞争冒险。
    以上分析都是基于前人的分析得出的。但是转念一想,既然复位释放的时候会出现竞争冒险,在复位开始的时候,虽然复位是异步的不受时钟影响,但会不会也出现刚好复位有效,时钟沿也来临的情况。实时上确实存在的,异步复位的时候也是有竞争冒险。我的理解是,虽然此时会有不稳态,但是紧接而来就是稳定的复位信号,信号全部归置,这一切都不超过一个时钟周期,所以异步复位是不会有太大问题。反而异步释放发的时候,当前会是不稳态,而且后面时钟里也都是工作状态,这就给时序带来了不确定性。
    综上,对于设计而言,异步复位是可接收的,但是异步复位释放是不能接收的。同步复位虽然解决了当时钟的有效沿来临的时候rst_n的边沿也正好来临所出现的冒险与竞争,但是只能检测大于一个时钟周期的复位。而且从综合的电路上可以看出,多了一个组合逻辑MUX,如果设计中所有的复位都是这样的,那会增加很多的资源,导致芯片面积很大。所以,两全其美的复位方式就是,采用异步复位,同步释放的机制。

  8. 异步复位同步释放
    所谓异步复位,同步释放就是指在复位信号到来的时候不受时钟信号的同步,而是在复位信号释放的时候受到时钟信号的同步。简单点说就是,复位开始的时候用异步处理,复位结束的时候用同步处理。而同步处理异步信号的最常用方法就是打两拍,释放异步复位用的这个方法。
    摘自网上的异步复位同步释放原理分析:
    异步复位
    当rst_async_n有效时,第一个D触发器的输出是低电平,第二个D触发器的输出rst_sync_n也是低电平,异步复位端口有效,输出被复位。
    同步释放
    假设rst撤除时发生在clk上升沿,则D触发器1可能输出高电平,也可能输出亚稳态,也可能输出低电平。但此时第二级触发器不会立刻变成亚稳态,它传递的是上一个周期里触发器1的值,也就是说第二级触发器的输出为1。
    而对触发器1的输出而言,若稳定后Q1为0,那么r2_rst在下一个周期被拉高,实现同步释放;如果稳定后的Q1为1,那么下个周期r2_rst仍为1,但此时由于rst事实上已经为0,所以Q1在下一个周期必为0,那么rst在下一个时钟上升沿到来时被低,也实现了同步释放,只是晚了一个周期而已。
    当然上面这些其实很难理解,我总是把这些归结为,复位信号是异步的,打两拍就行。
    下面是异步复位同步释放的RTL代码实现:

      module rst_signal(
         input       clk,
         input       rst,
         output      sys_rst 
      );
         reg         r1_rst,r2_rst;
       
         always@(posedge clk or posedge rst) begin
             if(rst) begin
                 r1_rst <= 1'b1;
                 r2_rst <= 1'b1;
             end else begin
                 r1_rst <= 1'b0;
                 r2_rst <= r1_rst;
             end
         end
      
         assign sys_rst = r2_rst;
         
     endmodule
    
      //使用 sys_rst 做同步复位,复位信号必须加到敏感列表中
         always @ (posedge clk or negedge sys_rst) begin
             if (!sys_rst) dout <= 1'b0; //同步复位
             else          dout <= din;
         end
    

    可以看到,模块将系统输入的异步复位信号进行同步,产生了一个后续逻辑使用的同步化了的异步复位,随后即可将该复位信号sys_rst用于其他模块的复位。
    需要注意得是,即使是异步复位同步释放,但他也是包括了异步复位的过程,只是复位释放的时候是变成同步的了。所以该复位也依旧必须要放在敏感列表中。
    为了减少亚稳态对上述同步器中的两个寄存器的影响,这两个寄存器应该在FPGA中被放置的越靠近越好(相应的约束:set_property ASYNC_REG TRUE [get_cells [list r1_rst_reg r2_rst_reg]]),以此尽量减少布线延迟。
    关于“”ASYNC-REG“”这个约束,主要用于异步信号中。在异步跨时钟域场合,对于控制信号(通常位宽为1-bit)常使用双触发器方法完成跨时钟域操作。此时对于两个触发器需要使用综合属性ASYNC_REG,有两个目的:
    -表明触发器1接收的数据是来自于与接收时钟异步的时钟域
    -表明触发器2是同步链路上的触发器
    从而,保证1号、2号触发器在布局时会被放置在同一个SLICE内,减少线延迟对时序的影响。但是,关于这个约束到底有多大的影响,我还没搞明白,因为平时很多设计中,并不作约束,暂时也没有发现太多问题。
    关于这个综合属性的具体解释见链接:https://cloud.tencent.com/developer/article/1530601

  9. 何时复位
    不管何种复位都是需要耗费大量资源的。那么究竟如何安排复位呢。
    对于数据通路(数字系统一般分为数据通路和控制通路,数据通路一般是对输入的数据进行处理,控制通路则是对运行的情况进行操作),在实际电路中,只要输入是有效数据(开始的时候可能不是有效的),输出后的状态也是确定的;在仿真的时候,也是输入数据有效了,输出也就确定了。也就是说,初始不定态对数据通路的影响不明显。
    对于控制通路,在实际电路中,只要控制通路完备(比如说控制通路的状态机是完备的),即使初始状态即使是不定态,在经过一定的循环后,还是能回到正确的状态上;然而在仿真的时候就不行了,仿真的时候由于初始状态为未知态,控制电路一开始就陷入了未知态;仿真跟实际电路不同,仿真是“串行”的,仿真时控制信号的初始不定态会导致后续的控制信号结果都是不定态,也就是说,初始的不定态对控制通道是致命的。
    复位信号很重要,但是并不是每一部分的电路都需要复位电路,一方面是复位电路也消耗逻辑资源、占用芯片面积,另一方面是复位信号会增加电路设计的复杂性(比如要考虑复位的策略、复位的布局布线等等)。
    当某个电路的输出在任何时刻都可以不受到复位信号的控制就有正确的值时,比如说数据通路中的对数据进行处理的部分。在某些情况下,当流水线的寄存器(移位寄存触发器)在高速应用中时,应该去掉某些寄存器的复位信号以使设计达到更高的性能,因为带复位的触发器比不带复位的触发器更复杂,反应也更慢。

  10. 上电初始化
    官方有如此叙述:
    当一个Xilinx的FPGA芯片被重新配置时,每一个单元都将被初始化。在某种意义上讲,这是一个上电之后的“终极的”全局复位操作,因为它不仅仅是对所有的触发器进行了复位操作,还初始化了所有的RAM单元。随着Xilinx

    FPGA芯片内部的嵌入式RAM资源越来越多,这种“终极的”全局复位操作越来越有意义。对所有的RAM单元进行预定义,在软件仿真和实际操作中都是非常有帮助的,因为这样避免了在上电时采用复杂的启动顺序来清除存储单元内容的操作。
    所以在xilinx平台,上电时自己产生的“软”复位,实际上是没有多大意义的。

  11. 小结
    综合各方资料,对于复位有以下设计结论
    a. 不同的器件不一样,对于x家器件,建议高电位复位;
    b. 建议采用异步复位同步化(异步复位同步释放处理);
    c. 全局复位并不是最佳方式;
    d. 并不是所有时序电路都要加复位。
    简单总结就是,Xilinx的FPGA 应该尽量避免全局复位,有些部分的设计都可以不用复位,必需要复位的设计而采用同步 高复位。

  12. 复位的最终方案
    上面的都只是一个基本原理示意,在实际使用过程中还应综合以下几点:
    ◦ 外部输入异步复位信号应该增加滤波和去抖处理;
    ◦ 在复位之前,确保由 MMCM 或PLL 生成的时钟是稳定且被锁定的;
    ◦ 将异步复位信号分别引入不同的时钟域进行同步化;

  13. 参考代码
    `timescale 1ns / 1ps
    //
    // Company:
    // Engineer:
    //
    // Create Date: 2022/03/18 17:11:28
    // Design Name:
    // Module Name: reset_manage
    // Project Name:
    // Target Devices:
    // Tool Versions:
    // Description:
    //
    // Dependencies:
    //
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    //
    //

    module reset_manage(
    
           input           clk1,     
           input           clk2,
    
    
           input           soft_reset,syn to clk1
           input           hardware_reset,///asyn
    
    
           output          reset,
           output          opd_reset,
           output          reset_ps
           
        );
    
    
            reg    [7:0]                         reset1_cnt = 0;
            reg                                   reset1_r = 1;
            reg    [15:0]                         reset2_cnt = 0;
            reg                                   reset2_r = 1;
    
            wire                             syn_reset1,syn_reset2;
            wire                             asyn_reset;
    
    
    
    
            assign                           asyn_reset=soft_reset||hardware_reset;
    
    
    将外部硬件复位和软件复位合成一个异步复位,在两个时钟域下分别对其进行同步释放
            reg                              rst1_1,rst1_2;
              always@(posedge clk1 or posedge asyn_reset) begin
                if(asyn_reset) begin
                    rst1_1 <= 1'b1;
                    rst1_2 <= 1'b1;
                end else begin
                    rst1_1 <= 1'b0;
                    rst1_2 <= rst1_1;
                end
            end
        
            assign syn_reset1 = rst1_2;
    
    
    
    
    
            reg                              rst2_1,rst2_2;
              always@(posedge clk2 or posedge asyn_reset) begin
                if(asyn_reset) begin
                    rst2_1 <= 1'b1;
                    rst2_2 <= 1'b1;
                end else begin
                    rst2_1 <= 1'b0;
                    rst2_2 <= rst2_1;
                end
            end
        
            assign syn_reset2 = rst2_2;
    

    //用户可继续自己定义增加复位时间宽度
    //异步复位同步释放信号 reset需要加在敏感列表
    always@(posedge clk1 or posedge syn_reset1)
    begin
    if(syn_reset1)
    reset1_cnt<=0;
    else
    if(reset1_cnt < 100)
    reset1_cnt <= reset1_cnt + 1;
    else
    reset1_cnt <= reset1_cnt;
    end

            always@(posedge clk1 or posedge syn_reset1) 
            begin
                if(syn_reset1)
                 reset1_r <= 1;
                else
                    if(reset1_cnt == 10)//steady state clk 不对CLK的lock判断,直接延迟十个时钟周期 作为时钟恢复稳定的时间
                        reset1_r <= 1;
                    else if(reset1_cnt == 80)//reset for 70 clk
                        reset1_r <= 0;
                    else
                        reset1_r <= reset1_r;    
            end
    
    
    
            always@(posedge clk2 or posedge syn_reset2)
            begin
                if(syn_reset2)
                 reset2_cnt <= 0;
                else
                    if(reset2_cnt < 40050)
                        reset2_cnt <= reset2_cnt + 1;
                    else
                        reset2_cnt <= reset2_cnt;        
            end   
    
    
            always@(posedge clk2 or posedge syn_reset2) 
            begin
            if(syn_reset2)
                 reset2_r <= 1;
                else
    
                    if(reset2_cnt == 20)//steady state opd_clk
                        reset2_r <= 1;
                    else if(reset2_cnt == 40030)//reset for 4010 opd_clk
                        reset2_r <= 0;
                    else
                        reset2_r <= reset2_r;    
            end
    
    
    
    
    
    
    
    assign   reset_ps=reset1_r;//如果有其他同步的复位 直接或一下就可
    
    assign   reset=reset1_r;
    
    assign   opd_reset=reset2_r;
    
    // assign   reset_ps=reset1_r;
    
    // assign   reset=reset1_r || soft_reset;
    
    // assign   opd_reset=reset2_r;
    
    
    
    
    对于多个复位有时序要求可参考下面
    
    // module CLOCK_RESET(
    //        input rst_n,
    //      input aclk,
    //      input bclk,
    //      input cclk,
    //      output reg  arst_n,
    //      output reg  brst_n,
    //      output reg  crst_n
    //        );
    
    
    // reg arst_n0,arst_n1;
    // reg brst_n0,brst_n1;
    // reg crst_n0,crst_n1;
    
    
    // always @(posedge aclk or negedge rst_n) 
    //   if(rst_n==0) begin
    //     arst_n0<=1'b1;
    //    arst_n1<=1'b0;
    //    arst_n<=1'b0;
    //   end
    //   else begin
    //     arst_n<=arst_n1;
    //     arst_n1<=arst_n0;
    //   end  
      
    // always @(posedge bclk or negedge rst_n) 
    //   if(rst_n==0) begin
    //    brst_n1<=1'b0;
    //    brst_n<=1'b0;
    //   end
    //   else begin
    //     brst_n<=brst_n1;
    //     brst_n1<=arst_n;
    //   end  
         
        
    // always @(posedge cclk or negedge rst_n) 
    //   if(rst_n==0) begin
    //    crst_n1<=1'b0;
    //    crst_n<=1'b0;
    //   end
    //   else begin
    //     crst_n<=crst_n1;
    //     crst_n1<=brst_n;
    //   end  
       
    
    
    endmodule
    

附1:网上的上电自动复位代码
原理上很简单,写一个复位模块,等待一段稳定时间,将复位信号拉低一段足够长的时间,再将复位信号拉高。
如下Verilog源码,外部按键复位也将作为模块的一个引脚输入,用于异步的全局复位操作,正常的复位操作要进行,必须要求外部有一个短暂的脉冲作用在rst_n信号上,这可以通过按键电路中的RC电路实现。
/**************************************
* 功能:上电复位模块
* 输入参数:
* clk: 50M 时钟输入
* rst_n:外部按键全局复位信号
* 输出参数:
* sys_rst_n:系统全局同步复位信号
***************************************/
module reset
(
input clk,
input rst_n,
output sys_rst_n
);
//------------------------------------------
// Delay 100ms for steady state
reg [22:0] cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 0;
else
begin
if(cnt < 23’d50_00000) //100ms
cnt <= cnt+1’b1;
else
cnt <= cnt;
end
end
//------------------------------------------
//rst_n synchronism
reg rst_nr0;
reg rst_nr1;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rst_nr0 <= 0;
rst_nr1 <= 0;
end
else if(cnt == 23’d50_00000)
begin
rst_nr0 <= 1;
rst_nr1 <= rst_nr0;
end
else
begin
rst_nr0 <= 0;
rst_nr1 <= 0;
end
end
assign sys_rst_n = rst_nr1;
endmodule
附2:对异步复位同步释放的解释
看了网上不少关于异步复位同步释放的解释,都没有到点子上。千篇一律的回答:“异步复位会出现亚稳态是因为clk的上升沿和rst释放同时发生,此时触发器输出状态不定,即亚稳态。”那做同步释放处理的目的不就是让rst释放正好和clk同步吗?自相矛盾。给出我的理解:异步复位会出现亚稳态是因为无法保证clk的上升沿时,rst释放信号能够满足其建立保持时间,触发器无法判断rst有效还是无效,因而出现亚稳态;而同步释放的处理机制可以保证rst释放信号的建立时间点与clk的posedge同步,在当前clk的posedge检测到rst稳定的低电平,在下一个clk的posedge可以检测到已经稳定的rst高电平,即满足了rst的建立保持时间要求,从而不会出现亚稳态。

加菲回复凉桑02-06
“那做同步释放处理的目的不就是让rst释放正好和clk同步吗?自相矛盾。”
并不矛盾,同步到时钟沿,但是给下一个时钟沿使用的。
所有的信号都是被当前时钟沿采样,下一个时钟沿使用。
橘子洲头泛轻舟回复加菲02-19
哦哦,那“所有的信号都是被当前时钟沿采样,下一个时钟沿使用”也就可以保证“rst释放信号的建立保持时间点与clk的posedge同步”是吧?

参考资料:
https://www.cnblogs.com/rouwawa/p/10313140.html
https://zhuanlan.zhihu.com/p/142377567
https://blog.csdn.net/qq_40268672/article/details/123025261
https://zhuanlan.zhihu.com/p/110866597
https://www.cnblogs.com/alifpga/p/9509237.html
https://cloud.tencent.com/developer/article/1830029

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值