本文关注点:
什么样的约束描述方式是最优的
什么样的约束描述顺序是最优的
关于如何缩短Vivado编译时间,可以先看这里“如何缩短Vivado运行时间”
常有工程师会抱怨,自己的Vivado工程从综合到生成bit文件太耗时,尤其是在调试阶段,一天跑不出一个版本,压力骤增。抛开FPGA芯片本身容量大、设计复杂等因素,还有一个重要因素不可忽略,那就是时序约束。糟糕的时序约束会严重影响编译时间。这里,我们从如下几个方面优化时序约束,从而从约束角度降低编译时间。
优化约束描述方式
通常情况下,一个设计中的pin的个数明显高于cell的个数(关于cell和pin的概念可以看这里“一张图看懂cell, pin, net, port”)。这一点很好理解,毕竟一个Module/Entity至少包含一个输入/输出管脚。因此,在DCP文件中搜索pin比搜索cell要耗时。一个好的解决方案是利用cell和pin的附属关系来搜索pin,简言之,先找到cell,再找pin,同时利用-filter选项提高效率。举个例子:在如下图所示的描述方式中,第2行对应的脚本直接通过pin的名字搜索相应的pin;第4行对应的脚本则是先找到目标pin附属于的cell,再通过pin的REF_PIN_NAME(注意和NAME是不一样的)找到相应的pin,显然第4行要比第2行省时高效。第6行与第4行等效,只是在寻找cell时用-filter选项过滤了一下。
基于此方式,我们看一个具体的应用案例。在如下图所示的案例中,第9~10行所描述的set_max_delay约束可以替换为第12~13行所示的方式。这里,set_max_delay约束的时序路径起点是某个cell的CLK管脚,因此,较为高效的方式是先找到这个cell,再通过cell结合pin的REF_PIN_NAME过滤出目标pin。
避免使用all_registers
all_registers会返回设计中所有的寄存器或者寄存器的pin(结合-data_pins或-clock_pins),这其中也包括BRAM和DSP48,因为两者内部也有寄存器。由此可见,all_registers返回对象的数目是很大的,尤其是当设计本身就很大时。如果设计中不得不使用某个时钟域的时序单元,那么可以用get_clocks代替。我们看一个案例,如下图所示。在这个案例中,第16行所示多周期路径约束的目的端为时钟clk1所驱动的时序单元,这里all_registers使用了-clock选项。一个更优的方式是将其替换为get_clocks,如第18行所示。这样,该约束只需要引用一个时钟对象,而非成百上千的寄存器。
优化约束的描述顺序
在加载时序约束时,时序引擎会分析每条约束的有效性,并以Message的形式打印出约束存在的潜在问题,例如所需对象不存在或者无效的目的端等。Xilinx给出了如下表所示的约束分类。第1列约束会影响TimingGraph;第2列约束不会影响TimingGraph;第3列约束则需要更新后的TimingGraph。
鉴于此,Xilinx推荐的约束描述顺序如下图所示。
我们来看一个案例:原本的约束描述顺序如下图所示。set_disable_timing放在了第30行,set_case_analysis放在了第33行,而两条create_clock则放在了这两条约束的后面。
根据Xilinx推荐的约束描述顺序,我们进行更新,更新后的结果如下图所示。读者可以体会一下两者的差异。