一开始接触到FPGA,肯定都知道”复位“,即简单又复杂。简单是因为初学时,只需要按照固定的套路——按键开关复位,见寄存器就先低电平复位一次,这样一般情况可以解决99%的问题,甚至简单的设计,就不可能有问题。复杂是因为复位本身是对大规模的硬件单元进行一种操作,必须要结核底层的设计来考虑问题。
1. 常见问题
自己在学习实践过程中,以及看到网友询问的,有关FPGA复位设计大概有以下几类问题:
我板子上没有设计按键复位怎么办?
怎么设计上电复位?不可能上电都要去按键吧
同步复位还是异步复位?各自优势是啥?
高电平复位还是低电平复位?
……
归根结底,就是怎样设计复位,我可能给不出完美的答案,但查阅了一些资料,总结了一些设计中考虑的因素,在加上参考文献,应该能解决99.9%的疑惑。
2. 常见的复位方式
我们习惯上通常使用的复位有三种方式:
硬件开关:复位信号接一个拨码开关或按键,或者RC电路
电源芯片:上电时候电源芯片产生,可以长时间维持,直到稳定。
控制芯片:控制芯片产生复位脉冲。
没有专门的电源或控制芯片,甚至连按键都忘记设计的情况下,有人提出可不可以拿内部计数器做一个所谓的上电“软”复位,如下代码所示,当cnt计数到一定的值时,产生一个复位脉冲信号。
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt else
begin
if(cnt cnt else
cnt end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rst_nr0 rst_nr1 end
else if(cnt == 23‘d50_00000)
begin
rst_nr0 rst_nr1 end
else
begin
rst_nr0 rst_nr1 end
end
没有设计外部硬件复位的同学又会反驳,我rst_n都没有!去掉复位,默认上电cnt = 0,可不可以?既然默认cnt = 0了,那其他所有的寄存器不都默认为0了吗?接着往下分析。
3. 合理的复位设计
从参考文献中得出有关复位设计的几条重要结论,有助于我们明确复位设计的方向,如下:
1. 低电平复位并不是最合理的处理方式;
2. 建议采用异步复位同步化(异步复位同步释放处理);
3. 全局复位并不是最佳方式;
4. 并不是所有时序电路都要加复位;
对上述结论分析,见后续总结。
3.1 复位电平
有关复位电平,实际上是跟FPGA芯片内部的触发器结构有关,在之前的博文有提到过。作为xilinx 7系列触发器,其 R 端口既可用作同步置位/复位端口,也可用作异步预设/清除端口,但无论哪种方式,都是高电平有效。Altera的是低电平有效。
当在7系列芯片上采用低电平复位,会有什么问题呢?如下:
如果RTL代码采用了低电平有效的复位模式,综合器将在复位信号驱动寄存器SR控制端之前的插入一个反相器(interver)。你必须使用一个查找表(look up table)来实现反向器,以利用LUT的输入端口。低电平有效的控制信号带来的额外的逻辑可能拉长了执行时间(runtime),将导致更低的FPGA资源利用率,也将影响时序和功耗。
到底我想说点什么呢?尽可能的在HDL代码或者实例化的模块中使用高电平有效的控制信号。如果在设计中,你不能够改变这些控制信号的极性,你需要在代码的顶层文件反转这些控制信号。采用这种方式描述电路的话,这些反向器将被吸收到I/O逻辑中,而不需要使用额外的FPGA逻辑、路径。
所以再次强调:复位电平的选择跟芯片结构有关!
3.2 异步复位同步化
异步复位同步化简单的说就是将异步复位信号在相关的时钟域模块中进行同步化处理。单纯的同步复位、异步复位以及其他的一些复位方式,都存在一些缺陷,比如抗噪声、存在亚稳态等问题,深入学习可以查阅相关文献。
异步复位同步化处理的结构代码及schemaTIc(xilinx 7系列)如下:
module rst_signal(
input clk,
input rst,
output sys_rst
);
reg r1_rst,r2_rst;
always@(posedge clk or negedge rst) begin
if(rst) begin
r1_rst r2_rst end else begin
r1_rst r2_rst end
end
assign sys_rst = r2_rst;
endmodule
可以看到,模块将系统输入的异步复位信号进行同步,产生了一个后续逻辑使用的同步化了的异步复位,随后即可将该复位信号sys_rst用于其他模块的复位。为了减少亚稳态对上述同步器中的两个寄存器的影响,这两个寄存器应该在FPGA中被放置的越靠近越好(相应的约束:set_property ASYNC_REG TRUE [get_cells [list r1_rst_reg r2_rst_reg]]),尽量减少布线延迟。
以上只是一个基本原理示意,在实际使用过程中还应注意以下两点:
外部输入异步复位信号应该增加滤波和去抖处理;
在复位之前,确保由 MMCM 或PLL 生成的时钟是稳定且被锁定的;
将异步复位信号分别引入不同的时钟域进行同步化
综上,复位处理的原理图如下所示:
3.3 恰到好处的复位
看似简单的复位,实际上不简单,来系数复位的N宗罪:
复位网络需要占用布线资源;
导致其余信号的布线信号受到影响,降低了它们布线的自由度;
增加的布线网络往往需要使用更高速率的芯片;
复位网络占用大量布线资源,使得Place&Route的时间大大增加;
复位信号需要占用大量的逻辑资源;
复位信号需要使用触发器的专用复位管脚;
可操作的复位信号往往导致D触发器的输入前增加额外的门操作或专用的复位信号输入;
增大整个设计 的尺寸;
额外的逻辑消耗降低了系统的性能;
阻止了使用高效特征,如Xilinx FPGA特有的SRL16E 移位寄存器。
简单的说,复位的存在会对FPGA的综合面积产生影响,主要体现在两个方面:第一、在编写代买的时候,习惯给所有时序电路都加上复位信号,这样往往会导致资源无形的浪费;第二,没有合理的设计复位电路会产生额外的资源消耗。
什么时候可以叫复位呢?一般判断原则是:
类似于移位寄存器这类直通型电路,只起到一个传输信号的作用,本身不会对信号产生任何影响。其传输信号正确与否,在于其输入端的信号是否正确。因此,此类电路自身无需加复位信号,而只需再起输入端口加复位信号(也就是数据来源处的电路),以控制输入数据。
4. 补充
4.1 所谓的上电初始化
当一个Xilinx的FPGA芯片被重新配置时,每一个单元都将被初始化。在某种意义上讲,这是一个上电之后的“终极的”全局复位操作,因为它不仅仅是对所有的触发器进行了复位操作,还初始化了所有的RAM单元。随着Xilinx FPGA芯片内部的嵌入式RAM资源越来越多,这种“终极的”全局复位操作越来越有意义。对所有的RAM单元进行预定义,在软件仿真和实际操作中都是非常有帮助的,因为这样避免了在上电时采用复杂的启动顺序来清除存储单元内容的操作。
所以在xilinx平台,开篇的“软”复位,实际上是没有多大意义的。
综上,采用以上总结的复位处理方法,应该能解决99.9%的问题了