通常同步电路由两种复位方式,即同步复位和异步复位。同步复位同步于寄存器的时钟域,异步复位则是立即自然地作用于寄存器,与其寄存器所在的时钟域之间没有确定的时序关系。同步化的异步复位是FPGA电路设计时复位电路的首选。
1 同步复位
1.1 同步复位在外部的情况
代码:
module sync_reset_ext( input clock, input reset_n, input data_a, input data_b, output out_a, output out_b ); reg reg1,reg2; assign out_a = reg1; assign out_b = reg2; always @(posedge clock) begin if(!reset_n) begin reg1 <= 1'b0; reg2 <= 1'b0; end else begin reg1 <= data_a; reg2 <= data_b; end end endmodule
Vivado工具综合出来的RTL如下图所示:
这里用到的是MUX(也可以使用与门来实现),这种方式需要考虑额外的门延迟,复位信号数据经过MUX逻辑电路使得复位信号到达时间(Data Arrival Time)会增加,在这种情况下,建立时间的slack可能为负值。同步复位总是比异步复位来的慢,在FPGA中不是最好的复位方式。
1.2 同步复位在内部的情况
代码:
module sync_reset( input clock, input reset_n, input data_a, input data_b, output out_a, output out_b ); reg reg1,reg2; reg reg3,reg4; wire rst_n; assign out_a = reg1; assign out_b = reg2; assign rst_n = reg4; always @(posedge clock) begin if(!rst_n) begin reg1 <= 1'b0; reg2 <= 1'b0; end else begin reg1 <= data_a; reg2 <= data_b; end end always @(posedge clock) begin reg3 <= reset_n; reg4 <= reg3; end endmodule
Vivado综合出来的RTL如下图:
跟1.1的区别是,复位信号打了两拍。为什么要这么做?因为大部分时候,输入到FPGA的复位信号是异步信号,在这种情况下,复位信号必须在内部先同步后再送达寄存器。既然输入的是异步信号,那么常常有可能该异步信号的脉宽比较小(小于一个时钟周期)。这种异步信号比时钟脉宽小的缺点是,异步信号同步时必须保证脉宽至少是一个时钟周期长,。才能保证异步复位信号在第一个同步寄存器被捕获;优点是,脉宽段可以增加其抗噪性能,在异步输入上的一些虚假脉冲,例如毛刺,很难在第一个寄存器被捕获,也就不会误触发同步复位。
2 异步复位
异步复位最大的优点,是没有像同步复位那样插入到数据路径中,因此异步复位对寄存器之间的数据到达时间不会产生负面影响。另一个优点是,异步复位是立即生效的。
代码:
module aync_reset( input clock, input reset_n, input data_a, output out_a ); reg reg1,reg2,reg3; assign out_a = reg3; always @(posedge clock,negedge reset_n) begin if(!reset_n) begin reg1 <= 1'b0; end else begin reg1 <= data_a; end end always @(posedge clock) begin reg2 <= reg1; reg3 <= reg2; end endmodule
Vivado综合出来的电路图:
但是,异步复位在复位被释放的时候可能会出现问题。异步复位在释放的时候,可以出现在时钟的任何一个时候,复位信号在撤除是,复位信号的边沿可能会落入时钟的一个亚稳态区域。亚稳态带来的后果是寄存器的输出需要花费更多的时间来恢复稳定或者正确的状态。这个额外的增加的时间可能会导致寄存器下一级的建立时间失败,从而导致系统错误。那么,如果这个异步复位信号,能够被同步到clock时钟域下,那么这个问题也就解决了,也就是接下来要说的异步复位同步释放的设计。
3 异步复位同步化
代码:
module sync_async_reset( input clock, input reset_n, input data_a, input data_b, output out_a, output out_b ); reg reg1,reg2; reg reg3,reg4; wire rst_n; assign out_a = reg1; assign out_b = reg2; assign rst_n = reg4; always @(posedge clock,negedge reset_n) begin if(!reset_n) begin reg3 <= 1'b0; reg4 <= 1'b0; end else begin reg3 <= 1'b1; reg4 <= reg3; end end always @(posedge clock,negedge rst_n) begin if(!rst_n) begin reg1 <= 1'b0; reg2 <= 1'b0; end else begin reg1 <= data_a; reg2 <= data_b; end end endmodule
第一个always块用来产生同步复位输出rst_n,作为第二个always块的异步复位,第二个将这个已经同步化了的复位信号当做异步复位使用。两个进程模块的复位信号都位于各自的敏感列表中。同步器中的寄存器级数(打拍级数)可以视情况而定,但是要确保同步后的异步复位信号至少有一个时钟周期的长度。
Vivado综合出来的RTL如下图所示: