上周在后台有这样一条留言:
“我遇到这样一个警告,最后会引起我的全局复位信号布不通,请问该如何处理?我是把所有的变量做成能复位的。”
这条留言提到了复位信号,也让我们看到滥用复位的后果。复位信号,实在是FPGA设计中最为常用的控制信号之一,也是被很多初学者甚至是富有经验的工程师所误用的信号之一。
什么情形下用复位信号
很多工程师习惯于对FPGA设计进行上电复位,总担心如果不复位,触发器就处于不定状态,导致系统跑飞。事实上,每个触发器都有明确的初始值,这个初始值与是否复位无关。因此,一旦系统上电,即使没有复位,对于FDSE和FDPE,其初始值为1,对于FDRE和FDCE,其初始值为0(FDSE、FDPE、FDRE和FDCE分别是什么,请查阅ug974)。Block RAM和DSP48内部触发器初始值为0。
结论1:如果只是上电复位,那么这种复位是不需要的
对于控制路径,例如状态机,这种复位通常是必需的。相比之下,数据路径的复位通常是不需要的。因为,老的数据总会被新数据“冲走”
结论2:通常,控制路径需要复位,数据路径不需要复位
从功能仿真的角度看,如果没有初始值,触发器输出会显示“X”,从而导致仿真无法继续。解决方法是在定义触发器时,给出初始值,如下图所示,而且这种方式是可综合的。
结论3:功能仿真时,为获得触发器初始值,可在定义该触发器时直接声明,无需复位
同步复位还是异步复位
如果必须使用复位,Xilinx建议使用同步复位。Xilinx FPGA中的触发器既支持同步复位,也支持异步复位,因此,常被误认为异步复位并不会带来不良后果。事实上,相比于同步复位,全局异步复位会增加时序分析的复杂度。另外,Block RAM和DSP48自带的触发器只支持同步复位,基于LUT的SRL(移位寄存器,例如,SRL16E,SRLC32E)不支持复位。看一个例子:采用异步复位的16x16bit乘法器(图片来源:Figure 3-3,ug949,V2019.1)。这个图片左侧代码为异步复位乘法器,右侧代码为同步复位乘法器。
异步复位乘法器的综合结果如下图所示(图片来源:Figure 3-2,ug949,V2019.1)。DSP48不支持异步复位,因此,这些触发器均为SLICE中的触发器,这就会额外消耗65个触发器和32个LUT。而使用同步复位,上述触发器会完美地映射到DSP48内部,设计的性能和功能都能达到最优。
全局复位还是局部复位
全局复位信号意味着该复位的扇出会很大,这会造成两个典型的不利结果。设想一下,从一个点出发要到达芯片其他位置的成千上万甚至几十万个地方,会占用很多布线资源,给布线带来压力,同时,也不利于时序收敛。这也就是为什么对于全局复位,要将其采用复位树的方式优化为局部复位。对于SSI器件更是如此,要确保每个SLR有自己的复位信号,避免一个复位信号全片飞。
移除不必要的异步复位
从代码角度移除不必要的异步复位并不是简单的把不需要复位的信号在always中注释掉,如下图所示(图片来源:Figure 3-5,ug949,V2019.1)。注释掉always中if分支的din_dly1和din_dly2,但else分支依然有该信号,工具会将其综合为使能信号,如图中右半部分所示。这里需要明确从代码风格的角度而言,异步复位的本质是复位信号不受时钟控制,因为会写成posedge clk or posedge rst。
对于上述代码,应该用两个always完成,体现的代码风格是同步复位寄存器、异步复位寄存器、不带复位的寄存器在不同的独立的always中描述,如下图所示(图片来源:Figure 3-6,ug949,V2019.1)。
总体而言,对于复位,要遵循以下几个原则
无需复位的,不要引入复位信号
能用同步复位的不用异步复位
非要异步复位的,保证同步释放
异步复位同步化,全局复位局部化