写在前面
本系列整理数字系统设计的相关知识体系架构,为了方便后续自己查阅与求职准备。在FPGA和ASIC设计中,对于复位这个问题可以算是老生常谈了,但是也是最容易忽略的点。本文结合FPGA的相关示例,再谈一谈复位。
(本文长度约六千字,请耐心阅读,本人水平有限,如有纰漏与错误,欢迎留言讨论)
复位的用途
复位信号几乎是除了时钟信号外最常用的信号了,几乎所有数字系统在上电的时候都会进行复位,这样才能保持设计者确定该系统的系统模式的状态,以便于更好的进行电子设计,并且在任意时刻,确保使用者总能对电路系统进行复位,使电路从初始的状态开始工作。
总结下来用途如下:
- 使电路在仿真时具有可知的初始值 :通常在仿真时未给入初试状态又未经复位,那么这段代码的初始值是不定态,会经常会出现X或者Z,也就未知态。
- 使实际电路在复位后从确定的初始状态开始运行 :在数字系统中,很多电路都要求从给定的初始状态上开始运行,避免电路工作于不可预知的状态,并因此导致异常或者致命的故障。
- 使电路在陷入异常状态时能通过复位回到可控的初始状态 :通常一个设计者无法保证所设计的数字逻辑电路在任何情况下都不会发生异常或者出现故障,假如电路无法自己返回正常状态,可以使用复位电路,强制使电路从初始状态开始工作。
尽管复位极为重要,但是复位电路在设计中是最常忽视的方面。一个不正确设计的复位本身可以表现为一个不可重复的逻辑错误。
复位电平的选择
有关复位电平,实际上是与FPGA芯片内部的触发器结构有关,在之前的博文有提到过。作为xilinx 7系列触发器,其 R 端口既可用作同步置位/复位端口,也可用作异步预设/清除端口,但无论哪种方式,都是高电平有效。Altera的是低电平有效。
如果RTL代码采用了低电平有效的复位模式,综合器将在复位信号驱动寄存器SR控制端之前的插入一个反相器(interver)。你必须使用一个查找表(look up table)来实现反向器,以利用LUT的输入端口。低电平有效的控制信号带来的额外的逻辑可能拉长了执行时间(runtime),将导致更低的FPGA资源利用率,也将影响时序和功耗。
同步复位
同步低复位
同步低复位的代码如下:
module reset(
input clk,
input rst,
input [1:0] in,
output reg [1:0] out
);
always @ (posedge clk) begin
if (rst == 'b0)
out <= 'd0 ;
else
out <= in ;
end
endmodule
在使用vivado RTL 分析对应的电路结构如下:
使用vivado综合后结构如下:
由于xilinx的器件结构中的触发器为高复位,所以会引入LUT实现复位信号的功能,引入额外的逻辑资源。
同步高复位
同步高复位的代码如下:
module reset(
input clk,
input rst,
input [1:0] in,
output reg [1:0] out
);
always @ (posedge clk) begin
if (rst == 'b1)
out <= 'd0 ;
else
out <= in ;
end
endmodule
在使用vivado RTL 分析对应的电路结构如下:
使用vivado综合后结构如下:
由于xilinx的器件结构中的触发器为高复位,所以在使用同步高复位时,直接会将复位信号连接到复位输入端。
同步复位的优缺点
同步复位的优点:
-
有利于仿真器仿真;
-
设计的系统全部是同步时序电路,有利于时序分析,可综合出更高性能的电路;
由于同步复位信号是被时钟启动(Launch)和锁存(Latch),而启动和锁存的时钟彼此同步,所以复位信号的到达时间(Arrival Time)和所需时间(Required Time)就能很轻易地确定并进行正确的时序裕度(slack)分析。
-
可以滤除高于时钟频率的复位毛刺,保证系统更加稳定;
-
对于逻辑器件内部的资源存在同步复位的端口时,使用同步复位时会节省FPGA的逻辑资源。
同步复位的缺点:
- 对于逻辑器件的目标库内的 DFF 都只有异步复位端口的情况,如果采用同步复位的话,综合器就会在寄存器的数据输入端口插入组合逻辑,这样就会一方面额外增加 FPGA 内部的逻辑资源,另一 方面也增加了相应的组合逻辑门时延,因此较难综合出更高性能的电路。
- 复位信号的有效时长必须大于时钟周期,才能真正被系统识别并完成复位。同时还要考虑诸多其他因素(诸如时钟偏移、组合逻辑路径延时、复位延时等),所以复位信号有时需要脉冲展宽,用以保证时钟有效期间有足够的复位宽度(此处可当做单信号的跨时钟处理,类似单信号的跨时钟处理方式)。
上图为快时钟的同步信号传输给慢时钟后造成复位丢失,具体解决方法可参考单信号的快时钟域转慢时钟域的跨时钟处理方法。
某些情况不太适用同步复位
在一些情况中,在出于节省功耗的目的而使用门控时钟时,就可能出现问题。在复位信号发出时,时钟可能关闭。在这种情况下只能使用异步复位,并在时钟恢复前移除复位信号。
又如不同的芯片在复位的时候不能把自身的总线接口重置成三态或者输入状态,而是总线上有多个芯片同时通过接口将数据输出到总线,总线上将出现电平冲突,严重时可能导致接口损坏。因此,在这种时候,同步复位就不太适用。
如果 ASIC/FPGA有内部三态总线,需要时钟来产生复位。为了阻止芯片上电时内部三态总线出现竞争,芯片应当有下图所示的异步上电复位。
当然也可以使用同步复位信号,但是也必须使用复位信号直接撤销三态使能。
这种同步技术的优点是能简化复位–高阻这一路径的时序分析。
异步复位(完全异步)
异步低复位
异步低复位的代码如下:
module reset_async(
input clk,
input rst_n,
input [1:0] in,
output reg [1:0] out
);
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 'b0)
out <= 'd0 ;
else
out <= in ;
end
endmodule
在使用vivado RTL 分析对应的电路结构如下:
使用vivado综合后结构如下:
由于xilinx的器件结构中的触发器为高复位,所以会引入LUT实现复位信号的功能,引入额外的逻辑资源。
异步高复位
异步高复位的代码如下:
module reset_async(
input clk,
input rst,
input [1:0] in,
output reg [1:0] out
);
always @ (posedge clk or posedge rst) begin
if (rst == 'b1)
out <= 'd0 ;
else
out <= in ;
end
endmodule
在使用vivado RTL 分析对应的电路结构如下:
使用vivado综合后结构如下:
这里综合后也没有引入新的组合逻辑资源,在该层次看并没有什么影响,但是Xilinx FPGA综合规则中:有和没有异步复位的寄存器不能被包装在一个Slice,不同异步复位的寄存器不能被包装在一个Slice,这就导致在Slice中的资源无法充分利用从而造成资源浪费。
异步复位的优缺点
异步复位电路的优点:
- 大多数目标器件库的DFF都有异步复位端口,因此采用异步复位可以节省资源。
- 设计相对简单,异步复位信号识别方便,而且可以很方便的使用FPGA的全局复位端口GSR。
异步复位电路的缺点:
所有异步信号的缺点也同时等效是异步复位信号的问题,复位信号从本质讲就是一个频繁使用的控制信号,因此可以总结其缺点如下。
- 复位信号容易受到毛刺的影响;
- 因为是异步逻辑,无法避免地存在亚稳态问题。
- 静态定时分析比较困难,静态时序分析一般是针对同步设计的,都是基于时钟周期来分析时序的。
- 对于 DFT(可测性设计)设计,如果复位信号不是直接来自于 I/O 引脚,在 DFT 扫描 和测试时,复位信号必须被禁止,因此需要额外的同步电路。
复位信号的“建立时间”与“保持时间”
由于复位信号实现了对D触发器的控制(置位信号同理),所以但是本质上也是实现了控制相关物理电路进行开关实现置位或者复位,由于异步复位与系统时钟毫无关系,不考虑其余因素时,异步复位可以在任意时刻撤除。但复位信号恰好在时钟沿附近时,复位(置位)信号同样也要满足相应的“亚稳态窗口”的不能移除的需求,满足寄存器的“建立时间”与“保持时间”。
恢复时间: 如果复位信号在时钟有效沿之前撤消,并且离时钟有效沿非常接近,再加上时钟有效沿到达各个触发器有一定时间差(clock skew),那么极有可能一部份触发器仍处于复位状态中而对时钟沿没有响应,而一部份触发器对时钟有响应,那么从这一个时钟开始,电路的状态已经出错了。
移除时间: 如果复位信号在时钟有效沿之后撤消,并且离时钟有效沿非常接近,再加上时钟有效沿到达各个触发器有一定时间差(clock skew),那么极有可能一部份触发器从复位状态中恢复并响应了时钟,而一部份触发器没有响应,那么从这一个时钟开始,电路的状态已经出错了。
下图中异步复位信号有足够的恢复时间,异步复位能正常释放。
当异步复位信号在时钟上升沿附近撤除时,导致触发器的输出为亚稳态,亚稳态前文已经讨论过了,亚稳态在电路中的危害是显而易见的。
如果你想让某个时钟沿起作用,那么你就应该在 “恢复时间” 之前是异步控制信号变无效;如果你想让某个时钟沿不起作用,那么你就应该在 “去除时间” 过后使控制信号变无效。
我觉得这里其实本质上就是要避开触发器的建立时间和保持时间,避免在亚稳态窗口附近寄存器的双锁存结构和复位结构同时作用,这将会引入亚稳态。
异步复位同步释放
异步确立和同步释放的复位电路通常会提供比完全异步或完全同步复位更可靠的复位。
电路结构如下图所示:
根据结构可写出代码如下:
module reset_async_and_free_sync(
input clk,
input rst_n,
input [1:0] in,
output reg [1:0] out
);
reg rst_n_dly0,rst_n_dly1;
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 'b0)begin
rst_n_dly0 <= 'd0 ;
rst_n_dly1 <= 'd0 ;
end
else begin
rst_n_dly0 <= 'd1 ;
rst_n_dly1 <= rst_n_dly0;
end
end
wire rstn = rst_n_dly1;
always @ (posedge clk or negedge rstn) begin
if (rstn == 'b0)
out <= 'd0 ;
else
out <= in ;
end
endmodule
在使用vivado对应的电路结构如下:
使用vivado综合后结构如下:
异步复位、同步释放优点
异步复位、同步释放具有异步复位和同步的优点,主要是:
- 快速复位。只要复位信号一有效,电路就处于复位状态。
- 有效捕捉复位。即使是短脉宽复位也不会丢失。
- 有明确的复位撤销行为。复位的撤离是同步信号,因此有良好的撤离时序和足够的恢复时间。
过滤复位毛刺
异步复位对毛刺很敏感,这就意味着任何满足触发器最小复位脉冲宽度的输入都能引起触发器复位。如果复位线受到毛刺的影响,这就真的成为问题了。在设计中,可能没有足够高频的采样时钟来检测复位上的小毛刺;下面将会介绍过滤掉毛刺的方法。该方法需要一个数字延时来过滤毛刺。复位输入引脚也必须是施密特触发器引脚才有助于毛刺过滤。下图显示了复位毛刺滤波器的电路和时序图。
为了加人延时,一些生产商提供了用于延迟且能够手动实例化的宏单元。如果没有这样的宏单元,设计人员就需要在优化后的已综合设计中手动加入延时。第二种方法需要创建一个包含较慢缓冲器的模块,再多次实例化该模块以达到所期望的延迟。基于这种思想,可以产生许多变种的解决办法。
由于该方法使用了延迟链,因此一个缺点是所产生的延迟会随着温度、电压和工艺而变化。必须注意确保延迟在所有PVT环境下都能满足设计要求。
不“复位” 可以吗?
如果针对Xilinx FPGA的设计应用,我觉得不进行 “复位” 是可以的,不必要的数据信号可以不进行“复位”,这样可以节省资源,在xilinx的白皮书中也是这样建议的:
当一个Xilinx的FPGA芯片被重新配置时,每一个单元都将被初始化。在某种意义上讲,这是一个上电之后的“终极的”全局复位操作,因为它不仅仅是对所有的触发器进行了复位操作,还初始化了所有的RAM单元。随着Xilinx FPGA芯片内部的嵌入式RAM资源越来越多,这种“终极的”全局复位操作越来越有意义。对所有的RAM单元进行预定义,在软件仿真和实际操作中都是非常有帮助的,因为这样避免了在上电时采用复杂的启动顺序来清除存储单元内容的操作。
通常可以将设计分为两部分,控制路径和数据路径
- 部分数据路径的初始值并不重要,此时的重置是不必要的。
- 只在设计中需要获得有效帧信号或设计回到已知良好状态的部分使用外部复位。
虽然在这里提到了不进行复位,但是不得不注意的是Xilinx的FPGA在上电后会对芯片内的资源进行复位,所以即使相关寄存器不进行复位,在芯片上电后也是能知道恢复到了芯片设置的默认状态的。在xilinx平台,部分复位设计实际是没有多大意义的。
无复位电路
编写代码使得输入做一级寄存处理,然后再将寄存输出的结果输出给out寄存器,此时对out这个寄存器不做复位处理。编写代码如下:
module noreset(
input clk,
input rst_n,
input [1:0] in,
output reg [1:0] out
);
reg [1:0] in_r;
always @ (posedge clk) begin
if (rst_n == 'b0)begin
in_r <= 'd0 ;
//out <= 'd0 ;
end
else begin
in_r <= in;
out <= in_r;
end
end
endmodule
在使用vivado RTL分析后对应的电路结构如下,从结构图中可以看到,第二级的输出寄存器没有添加相关复位信号控制逻辑资源。
使用vivado综合后结构如下,对输出寄存器没有设置复位,在综合分析时会自动设置复位信号为无效。
复位网络
在一个设计中,无论是同步复位还是异步复位,其扇出数量往往仅次于时钟网络。复位网络通常会被布线在全局网络上,在布线的时候需要控制各个路径的时钟偏移保持在大致相等的水平上,使复位能“同时”撤离。
下图为一个树状全局复位网络。
当复位信号驱动的模块和信号过多时,会导致扇出过大,从而使得布局布线变得困难。下图为一个全局复位网络的例子,从图中可以看出,复位信号扇出很大,资源分布散乱,从而给布局、布线带来很大的困难,增加了EDA的编译负担。
考虑下图的复位方案也会存在,由于rst_ol和rst_o2在撤离时可能存在一个时钟周期的偏差,因此,在实际电路系统中,该方式有可能导致复位失败,与前文提到的亚稳态的多个同步器道理相同。
比较合理的设计如下图:
图中的复位电路首先用两级寄存器对复位进行同步,得到根复位信号后再对其用复位同步器进行分发。由于同步后的根复位信号不会带来亚稳态的问题,因此,在分发根复位信号时再次使用两级寄存器对根复位信号进行同步和分发是很安全的。
根复位信号经过复位同步器分发后,各个子复位网络是各自独立的,并且扇出的数量比根复位网络要小很多。在布局布线的时候,这些子复位网络需要一一进行约束和时序分析。一般情况下,在设计的顶层代码中划分专门的复位模块,由该模块统一处理电路所需要的所有复位信号,使复位的方案更加清晰安全。在设计中,将复位电路的处理分散到各个底层模块是不安全的,有可能导致不同模块间的复位行为不一致,使电路因复位失败而工作在异常的状态上。
上图为使用模块化的复位的布局布线的结果,可以看到,复位信号经过模块化的处理后,降低了扇出,更有利于提高电路的工作性能。
多时钟域的复位
在多时钟域电路中,合理的复位处理方式应如下图所示。各个时钟域的复位信号由各自的时钟进行同步。在每个时钟域内,电路总是能被正确地复位。
小结
本文分析了复位的用途,通过同步复位和异步复位两个示例表示了在Xilinx FPGA中的不同复位的编写方法综合出的实际电路,指导我们在实际应用中去合理的进行复位设计,同时引入了恢复时间和去除时间的概念,进一步解释了在复位时,复位信号和时钟信号应该遵守的相关约定,进而结合异步复位的优点和同步复位的优点,介绍了异步复位同步释放的复位设计方法,在最后介绍了合理的复位网络设计与多时钟域的复位设计。根据本文的分析,可以小结一下关于复位的相关操作的注意事项以及复位设计的小技巧:
- 异步确立和同步释放的复位电路通常提供比完全异步或完全同步复位更可靠的复位。
- 复位电平的选择跟芯片结构有关,根据具体使用的芯片属性选择合适的复位电平。
- 建议采用异步复位同步化(异步复位同步释放处理)。
- 全局复位并不是最佳方式,可使用模块化方式去处理复位信号。
- 并不是所有时序电路都要加复位,但对于控制信号以及必须知道初始状态的情况,必要的复位是不可少的。
- 对每个独立的时钟区域必须利用一个分开的复位同步器。
Reference
- FPGA-xilinx系列芯片的复位,你真的明白吗?
- FPGA同步复位、异步复位、异步复位同步释放 - 孤独的单刀
- 移除时间和恢复时间
- FPGA基础学习(9) – 复位设计 - 肉娃娃
- wp272: Get Smart About Reset: Think Local, Not Global
- 正点原子逻辑设计指南
- 高级FPGA设计:结构、实现和优化
- 硬件架构的艺术
- 《FPGA实战演练. 高级技巧篇》——王敏志编著