赞
踩
今天翻了翻高亚军的《vivado从此开始》这本书,看着还可以,然后关注了他的公众号,里面有些总结写得不错,收藏一下
https://mp.weixin.qq.com/s/UjKSC95wvkHzS_rrxAcMXA
https://mp.weixin.qq.com/s/j_O4AtElwh3zaP0IIQOdHg
毋庸置疑,create_clock是最基本、最简单的时序约束命令,而且在FPGA设计中必然会用到。但看似简单的命令,却也常常被用错。这里我们就来回答一些常见的问题。
问题1:什么样的时钟需要用create_clock约束?
有三类时钟时钟需要用create_clock进行约束,分别是:
从全局时钟管脚进来的主时钟(Primary Clock)
7系列FPGA高速收发器输出时钟(RXOUTCLK/TXOUTCLK)
用于接口约束的虚拟时钟(Virtual Clock)
问题2:什么是虚拟时钟(VirtualClock)?
虚拟时钟,顾名思义,在实际设计中并不存在的时钟,主要用于输入、输出接口的约束。这里给出一个虚拟时钟案例,如下图所示。
问题3:如何约束高速收发器的输出时钟?
这里要分两种情况进行讨论。对于7系列FPGA,需要约束GT的RXOUTCLK和TXOUTCLK,具体约束如下图所示。事实上,只有在这种情形下create_clock命令才会和get_pins命令结合使用(通常是和get_ports一起使用)。
对于UltraScale系列FPGA,只用约束参考时钟即可。Vivado会自动推断GT的生成时钟约束。
问题4:如何约束差分时钟?
对于差分时钟,一定会用到IBUFGDS,只用约束P端。工具会据此自动将此约束传播到IBUFGDS的输出端口。如果既约束P端又约束N端,工具会认为这是两个独立的时钟,进而分析相应的内部路径,最终导致错误的时序需求(Requirement)。
问题5:create_clock的时钟定义在BUFG的输出端会有什么影响?
在Vivado中,这两条约束是不同的,因为它们定义了不同的零时刻起点。这个零时刻起点是用来计算Clock Latency和Uncertainty的,而这两个值又是用来计算Slack的。换言之,零时刻起点直接影响了Slack的计算。通常,Vivado会忽略零时刻起点上游所有时钟树的延迟。如果在BUFG的输出端定义主时钟,那就意味着只有部分Latency会被使用。如果该时钟与其他时钟有交互,那么它们之间的Clock Skew将不再准确,最终影响Slack的准确度。
结论:
对于从全局时钟管脚进来的时钟,create_clock会和get_ports结合使用;
对于7系列高速收发器的输出时钟,create_clock会和get_pins结合使用;
对于虚拟时钟,create_clock会单独使用。
本文关注点:
什么样的约束描述方式是最优的
什么样的约束描述顺序是最优的
关于如何缩短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选项过滤了一下。
基于此方式,我们看一个具体的应用案例。在如下图所示的案例中,第910行所描述的set_max_delay约束可以替换为第1213行所示的方式。这里,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推荐的约束描述顺序,我们进行更新,更新后的结果如下图所示。读者可以体会一下两者的差异。
时序收敛是FPGA设计都要面临的问题。要解决时序收敛就要找到导致时序违例的根本原因。时序违例以同一时钟域内的触发器到触发器的建立时间违例最为普遍,优先解决这类时序违例将加速时序收敛。
典型的同步逻辑单元时序模型如下图所示。发送时钟与接收时钟为同一时钟。图中Tclka为时钟到发送触发器的延迟,Tclkb为时钟到接收触发器的延迟,Tco为发送触发器时钟到输出延迟,Tdata为数据延迟,它包括逻辑延迟Tlogic和布线延迟Tnet,Tsu是接收触发器所需的建立时间。
在此模型的基础上,可确定Setup Slack,如下图所示。Setup Slack等于数据需求时间(Data Required Time)减去数据到达时间(Data Arrival Time)。
对于前述时序模型,Setup Path Requirement为一个时钟周期,以Tperiod表示,可得如下图所示公式。该公式清晰地显示了造成时序违例的“贡献者”:Tdata、Setup Uncertainty 和Clock Skew,因为其他二者Tsu、Tco均为固定常数。进一步,Tdata可分解为Tlogic和Tnet。其中Tlogic和逻辑级数相关;而导致Tnet过大的原因则较多:可能是布线策略不合适、路径中存在扇出较大的网线、布线拥塞、控制集较多导致驱动和负载相隔较远等。Tlogic通常需要在综合或者opt_design阶段解决,而Tnet则要在布局布线阶段解决。
结论
-优先解决内部触发器到触发器的时序路径出现的时序违例
-对于这类路径,Setup Slack等于数据需求时间(Data Required Time)减去数据到达时间(Data ArrivalTime)
-导致这类时序路径出现时序违例的可能“贡献者”为Tdata、Setup Uncertainty 和Clock Skew
通过Report Utilization查看资源利用率报告时,会生成如下图所示的一个表格。在这个表格中Resource对应的列会有LUT和LUTRAM,那么两者到底有什么区别呢?是包含关系吗?不过,可以断定的是LUTRAM是指将LUT用做分布式RAM/ROM,换句话说是指SLICEM中的LUT被用做了存储单元,那么这里的存储单元是否包含移位寄存器(SRL)呢?
为了验证我们看如下一个设计。这个设计包含三个模块:5-bit计数器(会消耗1个LUT),32x1(深度为32,宽度为1)的分布式RAM(会消耗1个SLICEM中的LUT),深度为32的移位寄存器(会消耗1个SLICEM中的LUT)。对每个模块采用OOC综合方式,可以验证上述对每个模块LUT利用率推断的正确性,如下图所示。
打开布线后的DCP,执行report_utilization命令,结果如下图所示。可以看到该设计共消耗了3个LUT,2个LUTRAM。
事实上,从Available对应列可以看到LUT对应的数据为41000,而该数据指的是FPGA中所有LUT的个数。
结论:
-资源利用率中的LUT是指设计中消耗的所有LUT,包括用做逻辑函数发生器的LUT(SLICEL中的LUT),也包括用做存储单元的LUT(SLICEM中的LUT)
-资源利用率报告中的LUTRAM是指设计中消耗的SLICEM中的LUT(用做分布式RAM/ROM或移位寄存器)
时钟是整个FPGA设计的“发动机”,FPGA内部逻辑正是在时钟的驱动下运作,因此,管理好时钟拓扑结构尤为重要,而时钟的拓扑结构又与相应的具体芯片型号相关。这里我们介绍一下UltraScale系列FPGA与7系列FPGA在时钟资源方面的主要差异。
时钟管脚
7系列FPGA的时钟管脚分为SRCC(Single-region clock-capable I/O)和MRCC(Multi-region clock-capableI/O),两者的主要差异在于驱动局部时钟资源的类型不同,也就是SRCC可驱动BUFR,不能驱动BUFMR,而MRCC则即可驱动BUFR也可驱动BUFMR。在UltraScale系列FPGA中,GC(Global Clock)管脚取代了SRCC和MRCC,换言之,GC不再有SRCC和MRCC。
时钟区域
这里所说的时钟区域(Clock Region)是FPGA内部结构,包括相应的CLB、DSP、BRAM、互联资源等。7系列FPGA中,时钟区域的宽度(水平方向)为芯片宽度的一半,而UltraScale不再有这种限制。如下图所示的UltraScale时钟区域(图片来源,ug949,figure3-37),共有6x6即36个,如果是7系列FPGA,则是2x6即12个。
时钟缓冲器
7系列FPGA中有所谓的局部时钟缓冲器,例如BUFR、BUFMR、BUFIO等,而UltraScale系列FPGA中不再包含这类时钟缓冲器,其功能由新增的BUFGCE_DIV代替。同时,需要明确BUFGCE_DIV并非局部时钟资源,而是全局时钟资源。BUFGCE_DIV具有分频功能,对于简单的分频,相比于MMCM/PLL,BUFGCE_DIV在功耗上更具优势,而且如果分频时钟和源时钟有跨时钟域路径,那么相应的Clock Skew会更小。看一个案例,设计中需要两个时钟,一个600MHz,一个300MHz,可采用如下图所示方案。MMCM生成时钟为600MHz,实例化两个BUFGCE_DIV,其分频因子分别为1和2,从而可分别生成一个600MHz时钟和一个300MHz时钟,而且,这两个生成时钟也是同相的。
此外,在使用高速收发器生成的时钟时,可以使用BUFG_GT。该时钟缓冲器也具有分频功能,也可以驱动FPGA内部其他逻辑资源。
设计中如果大量使用Block RAM,可通过一些综合属性管理RAM的实现方式以满足系统对性能与功耗的需求。以32Kx32bit RAM为例,目标芯片为UltraScale,通过使用综合属性cascade_height来管理Block RAM的级联高度,如下图所示。
当级联高度分别为1、32和8时,其实现方式如下图所示。不难理解,若级联高度为1,在同一时间32个Block RAM均处于激活状态;若级联高度为32,同一时间只有1个Block RAM处于激活状态,但级联长度最长,尽管有专用级联走线,但毕竟要穿越时钟域,有可能无法实现;若级联高度为8,同一时间有4个Block RAM处于激活状态。相比之下,级联高度为1时,Fmax最大,功耗也最高;级联高度为32时,Fmax最小,但功耗也最小;级联高度为8时,Fmax和功耗均介于前两者之间,可在功耗与性能之间获得折中。
还有一个综合属性ram_decomp,可进一步帮助降低系统功耗。以8Kx36bit RAM为例,采用如下图所示的四种实现方式。其中,第4种实现方式同时使用了cascade_height和ram_decomp两个综合属性。
相比下来,第4种实现方式可获得性能和功耗的折中。第1种和第3种实现方式是一致的,均获得较低的功耗。第2种方式功耗最高。
当两个综合属性同时作用于同一个信号上时,可采用如下方式。注意在System Verilog中,两个综合属性之间需要用逗号隔开。
结论:
-使用大深度的RAM时,可通过综合属性cascade_height和ram_decomp管理RAM的实现方式,以获得性能与功耗的折中
以UltraScale芯片为例,每个Block RAM为36Kb,由两个独立的18Kb Block RAM构成,如下图所示。
每个18Kb Block RAM架构如下图所示。从图中可以看出,Block RAM本身会对输入控制信号(addr, we, en)和输入数据(din)进行寄存(这些寄存器是可选的且在Block RAM内部),同时对输出也可寄存(该寄存器也是可选的)。从而,从输出到输出的最大Latency为2。采用手工编写RTL代码的方式使其映射为Block RAM时,可按照Block RAM的架构描述。需要注意如果需要复位,输出寄存器是带有复位端口的,但仅支持同步高有效。实验证明,只要按照该结构描述,所有的寄存器会映射到Block RAM架构中,不会消耗额外的寄存器。
(图片来源:ug573,figure 1-5)
这两个18Kb的Block RAM可形成如下图所示的4种配置方式。
可通过IP Core (Block Memory Generator)或者XPM_MEMORY的方式使用BlockRAM,但无论通过哪种方式,都需要注意对Block RAM的输出要做寄存处理,尤其是在高速设计中。在使用IP Core时,会有如下图所示的选项,其中的Primitives Output Register为Block RAM自带的寄存器,而CoreOutput Register为SLICE中的寄存器。
进一步说明,这两个寄存器的位置如下图所示。图中的Embedded Output Registers即对应Primitives OutputRegister。
(图片来源pg058,figure 3-17)
类似的,在使用FIFO IP Core时,也会有一个选项Output Registers,其对应的Embedded Registers即为Block RAM自带的寄存器。
Block RAM自带的寄存器(Embedded Registers)对系统性能有很大影响,以VirtexUltraScale Plus芯片为例,在使用该寄存器和未使用该寄存器时,Tco(时钟到输出延迟)的变化还是很大的,这意味着Fmax会因此而发生较大变化。
(图片来源ds923,table 28)
在综合后的网表中,选中设计中的Block RAM,在其Property窗口中,查看DOA_REG和DOB_REG,若其为0,则说明未使用Embedded Registers。
当设计要求Latency为2时,Primitives Output Register和Core Output Register选取其一即可。若系统时钟频率较高,可选择Core Output Register,因为该寄存器的Tco小于Primitives Output Register的Tco,同时CoreOutput Register的布局可兼顾下级时序路径的时序要求;若当前设计触发器利用率较高,同时选择Primitives Output Register可满足系统时序要求,则选择PrimitivesOutput Register可避免进一步增加触发器的利用率。
结论:
-在使用Block RAM时,为便于时序收敛,最好使用Embedded Registers
SLICEM中的查找表可用作分布式RAM。如果把FPGA比作大海,LUT就像一个个小的岛屿分布在这片大海上,或许这就是分布式RAM的名称由来。以UltraScale Plus芯片为例,一个6输入查找表可实现深度为64宽度为1的单端口RAM。同一个SLICEM中的8个LUT可级联构成512深度的RAM。LUT只提供一个时钟端口,一个写使能端口,因此,向RAM中写数据是同步的,但读数据则是异步的,为此,只需添加额外的寄存器即可实现同步读操作。这虽然增加了Latency,但改善了时序,提升了系统性能。
不管是单端口(SP)、简单双端口(SDP)还是真正双端口(TDP)RAM,都有三种工作模式,即读优先(read_first)、写优先(write_first)和保持(no_change)。这三种模式体现了针对同一地址同时进行读写操作时的不同处理方式。读优先时,读出的是该地址上的原有数据;写优先时,读出的是将要写入该地址的新数据;保持模式时,输出端口数据保持不变。事实上,这三种模式可通过其对应的RTL描述方式一探究竟。
从逻辑级数的角度看,一个深度为128,宽度为1的单端口RAM,需要消耗2个LUT,1个F7MUX,如下图所示,其逻辑级数为1。当深度变为256时,则需要消耗4个LUT,2个F7MUX和1个F8MUX,其逻辑级数为2。
分布式RAM的优势在于轻便灵活。对于一些需要存储较少数据(通常不超过10Kb)的场合,例如,存储FIR滤波器的系数或者缓存中间处理过程中的数据,可获得比Block RAM更好的性能。以深度为128,宽度为4的RAM为例,在Virtex-7,速度等级为-2的芯片上,分别采用Block RAM和分布式RAM,其结果如下图所示(图片来源ug949, v2015.3, table 5-10)。
结论:
-在某些场合采用查找表作为轻量级存储单元会有更好的效果
-在用作分布式存储单元(RAM/ROM)时,要注意逻辑级数对时序的影响
无论是7系列FPGA、UltraScale还是UltraScale Plus系列FPGA,都包含Block RAM(BRAM),但只有UltraScale Plus芯片有UltraRAM也就是我们所说的URAM。BRAM和URAM都是重要的片上存储资源,但两者还是有些显著的区别。
容量
BRAM的容量为36Kb,且可当作两个独立的18KbBRAM使用。对于一个36Kb的BRAM,其最能达到的最大位宽为72bit。URAM容量为288Kb,一个URAM深度为4K(4×1024),宽度为72b。
时钟
BRAM有两个时钟,在RAMB36E2的Primitive声明中就可以看到这两个时钟CLKARDCLK和CLKBWRCLK。而URAM只有一个时钟,在URAM288的Primitive声明中可以看到该时钟CLK。尽管在使用XPM_MEMORY实例化URAM时可以看到clka和clkb,但这两个端口最终都连接到URAM的物理端口CLK上。
初始值
BRAM的初始值是可以设定的,无论BRAM是单端口、简单双端口还是真双端口都可以通过COE文件设定其初始值。而URAM的初始值只能为0,且无法更改,换言之,其初始值是不可设定的。这也就意味着BRAM可以配置成ROM而URAM不可以。
工作模式
BRAM可配置为单端口、简单双端口和真双端口,但对于URAM,不能简单地将这三种模式映射过来,其工作行为如下图所示。可以看到,A/B端口不是独立的,例如,A端口读而B端口写同一地址,读出的是该地址原有数据;A端口写而B端口读同一地址,读出的是新写入的数据。
图片来源:Table 2-6, ug573
实例化方式
对于BRAM,可采用原语、XPM_MEMORY、RTL代码或者IP Core的方式进行实例化,但URAM目前只支持原语、XPM_MEMORY和RTL代码的方式。相比而言,XPM_MEMORY的方式更为快捷,也是Xilinx建议的方式。
级联方式
BRAM和URAM都可级联,只是级联方式不同。在使用BRAM时,我们只需要设定宽度和深度,并根据时钟频率合理选择Latency,也就是选择是否需要使用BRAM自带的输出寄存器或Slice中的寄存器。而URAM提供了专门的级联寄存器,同样需要根据时钟频率合理选择Latency,此时会影响到级联寄存器的使用。如下图所示,是4个URAM的级联情形。
SSI是Stacked Silicon Interconnect的缩写。SSI芯片其实就是我们通常所说的多die芯片。其基本结构如下图所示。可以看到SSI芯片的基本单元是SLR(Super Logic Region),也就是我们所说的die。SLR之间通过Interposer“粘合”在一起。每个SLR可看做一片小规模FPGA。
在芯片选型手册上,有如下图所示说明,根据图中红色方框标记可判断该芯片是否是SSI芯片。
总的来说,UltraScale+大部分都是多die芯片,如下图所示。图中还可以看到每个芯片所包含的SLR的个数以及每个SLR的大小。SLR的大小以时钟区域(Clock Region)衡量,例如,VU5P有两个SLR,每个SLR的宽度为6,高度为5,所以共有6x5也就是30个Clock Region。同时,还可以看到每个SLR的大小是一致的。
图片来源:Table 19,ds890
只要获知芯片的具体型号,在Vivado Tcl Console中执行如下图所示命令即可获得该芯片所包含的SLR的个数。例如,对于XCVU5P,属性SLRS的返回值为2,说明该芯片有两个SLR,故其是多die芯片;而对于XCVU3P,返回值为1,说明该芯片只有一个SLR,故其是单die芯片。
多die芯片的每个SLR其结构基本是一致的,都包含CLB、Block RAM、DSP和GT等。但这些SLR的地位是不一样的。这其中只有一个SLR是Master SLR。通过如下图所示的命令可获取Master SLR(需要在打开的工程中或DCP中执行该命令)。通常SLR0为Master SLR。用于配置FPGA的电路、DNA_PORT和EFUSE_USER只存在于Master SLR中。
这是多die芯片设计中的一个重要问题。SLR之间通过专用布线资源SLL(Super Long Line)互连。SLL的个数是有限的。以XCVU5P为例,可通过如下命令获取SLL的个数。这在设计初期是非常重要的。需要根据此数值评估跨die网线个数是否合理。跨die网线过多很可能造成布线拥塞,进而影响时序收敛。
对于SSI器件,Interposer上分布了专用的全局时钟走线,因此,对于跨die时钟并不需要特殊处理,同时该时钟也不会占用SLL。
以DSP48为例,其有专门的级联端口,例如PCOUT/PCIN。因此,相邻的两个DSP48级联时,会使用专用的级联布线资源。但是,这种布线资源仅限于die内。类似地,Block RAM、Carry Chain等在die内可使用固有的级联布线资源。
器件选型阶段需要根据设计规模选择合适的芯片。这个阶段,需要根据整个设计的资源利用率确定芯片规模。一旦选定SSI器件,就要及早考虑模块划分,也就是如何将设计分配到每个die内,使每个die的资源利用率尽可能平衡,此时就要考虑每个die的资源利用率,避免出现某个die某一资源利用率过高以至于出现拥塞,而另一个die该资源利用率偏低的情形。这一工作要在设计初期完成,本质上就是要设计好合理的数据流,从而达到两个目的:每个die的资源利用率比较均衡;跨die网线个数合理。
Tcl(Tool CommandLanguage)是IC业界标准程序语言。Xilinx将其集成于Vivado中,使得Vivado如虎添翼。通常,凡是借助图形界面可实现的操作都有其对应的Tcl脚本;相反,借助Tcl脚本实现更复杂、更深入的分析或操作是图形界面方式无法实现的。
就Vivado而言,采用Tcl脚本时(本质上,XDC是Tcl的一个子集)需要明确操作对象。这里介绍几个最基本的操作对象:cell, pin, net和port。如下图所示。
从图中不难看出,cell就是基本的模块,可以是Verilog中的module或VHDL中的entity,或者综合后的更细粒度的逻辑单元,比如触发器(Flip Flop)、查找表(LUT)、进位链(Carry chain)。每个cell都有自己的pin,pin是有方向的。cell之间通过net相连。顶层设计中,需要给输入/输出端口(port)分配管脚(package pin),这里就体现了pin与port的区别。package pin必然位于IO bank之中。
那么是不是只有顶层设计才有port呢?其实port和pin是相对的,例如:描述约束时,指定该约束文件的作用域(SCOPED_TO_CELLS或SCOPED_TO_REF)是某个cell,此时就可以把这个cell当作顶层来看待,这时就要把cell的输入/输出端口当作port来处理。
时序收敛是FPGA设计都要面临的问题。要解决时序收敛就要找到导致时序违例的根本原因。时序违例以同一时钟域内的触发器到触发器的建立时间违例最为普遍,优先解决这类时序违例将加速时序收敛。
典型的同步逻辑单元时序模型如下图所示。发送时钟与接收时钟为同一时钟。图中Tclka为时钟到发送触发器的延迟,Tclkb为时钟到接收触发器的延迟,Tco为发送触发器时钟到输出延迟,Tdata为数据延迟,它包括逻辑延迟Tlogic和布线延迟Tnet,Tsu是接收触发器所需的建立时间。
在此模型的基础上,可确定Setup Slack,如下图所示。Setup Slack等于数据需求时间(Data Required Time)减去数据到达时间(Data Arrival Time)。
对于前述时序模型,Setup Path Requirement为一个时钟周期,以Tperiod表示,可得如下图所示公式。该公式清晰地显示了造成时序违例的“贡献者”:Tdata、Setup Uncertainty 和Clock Skew,因为其他二者Tsu、Tco均为固定常数。进一步,Tdata可分解为Tlogic和Tnet。其中Tlogic和逻辑级数相关;而导致Tnet过大的原因则较多:可能是布线策略不合适、路径中存在扇出较大的网线、布线拥塞、控制集较多导致驱动和负载相隔较远等。Tlogic通常需要在综合或者opt_design阶段解决,而Tnet则要在布局布线阶段解决。
结论
-优先解决内部触发器到触发器的时序路径出现的时序违例
-对于这类路径,Setup Slack等于数据需求时间(Data Required Time)减去数据到达时间(Data ArrivalTime)
-导致这类时序路径出现时序违例的可能“贡献者”为Tdata、Setup Uncertainty 和Clock Skew
对于设计分析,Xilinx建议越早越好,也就是在设计初期介入并发现潜在问题往往对设计收敛起到关键的作用,这也是UltraFast设计方法学的宗旨。不同于ISE,对于Vivado工程,最好在综合后就开始对设计进行分析,尤其是对于大规模的高速设计,这一点尤为重要。那么如何着手呢?
Vivado提供了一个很好的命令report_qor_assessment,打开综合后的设计,先执行该命令。该命令会检查逻辑级数、资源利用率和时钟拓扑结构,基于此,形成一个报告,并给出一个分数。这个报告可以帮助工程师理解时序收敛问题的严重程度。Xilinx建议综合后就执行此命令,同时,只要设计有重大更新,都要重新执行此命令。首先看一下这个报告给出的分数所代表的含义,如下图所示(图片来源:Table 4,ug949)。分值越高,表明设计时序越容易收敛。若分值为1或2,则设计不可能收敛;若分值为3,则设计收敛的可能性不大。显然,我们希望综合后此分值越高越好。
分值在报告中的体现形式如下图所示,可以看到此设计的分值为3,意味着时序收敛的可能性非常小。
报告的第二部分列出了主要细节,包括资源利用率、时钟拓扑结构、拥塞和时序,如下图所示。尤其要注意Status为REVIEW的条目。图中的Threshold并不是绝对限制,只是表明实际值超过此阈值时,时序收敛将变得困难。
报告的第三部分如下图所示,实际上是report_methodology命令结果的一部分,更为详细的信息可通过该命令获取。
下一步就可以执行report_methodology命令,解决其中暴露的问题。
Vivado提供了命令report_design_analysis,该命令功能强大,不仅可以分析时序,还可用于分析逻辑级数、设计复杂度和拥塞。
时序分析
在用于时序分析时,需要添加选项-setup、-hold或-timing,具体用法如下代码所示。第一条命令可生成最差的50条路径建立时间的时序报告,报告形式如下图所示。下面三张图是报告前三行不同列的内容。
在这个报告中可以看到:
逻辑延迟的百分比
时序路径的逻辑级数
路径上是否有BRAM或DSP
路径的Requirement是否过于严苛
布线延迟百分比
路径上的最高扇出是多少?
路径上的单元是否被放置的相距较远?
对于SSI器件,是否是跨die路径?
BRAM和DSP是否适用了自带的寄存器
时钟Skew(对于Setup是否<-0.5ns,对于Hold是否大于0.5)
是否是跨时钟域路径?
是同步时钟还是异步时钟?
是否穿过IO列?
逻辑级数分析
逻辑级数分析时,建议在综合之后就执行,需要添加两个选项,如下代码所示,会生成如下图所示界面。图中右侧第一行为逻辑级数,第二行对应数字wei相应的时序路径的个数。选中这个数字,点击鼠标右键,可生成相应的时序报告。
设计复杂度分析
report_design_analysis可用于分析设计的复杂度,此时需要添加选项-complexity,如下代码所示。该代码会生成类似于下图所示的报告。在这个报告中,有三个参数需要格外关注,分别是Rent、Average Fanout和Total Instances。其中Rent指数反映了模块的互连度,该指数越高,互连越重。较重的互连意味着设计会消耗大量的全局布线资源,从而导致布线拥塞。Rent值超过0.65或者Average Fanout大于4同时Total Instances 15K时要引起足够关注,很可能会造成布线拥塞。
拥塞分析
report_design_analysis还可用于分析设计的拥塞程度,此时需要添加选项-congestion,如下代码所示。该代码会生成类似于下图所示的拥塞报告。
综上所述,report_design_analysis有三种工作模式:时序分析、复杂度分析和拥塞分析。
Schematic视图可以使我们更直观地看到电路结构、cell和FPGA内部单元的映射关系等,除此之外,我们还能看到其他内容。
注:打开任意阶段的DCP文件,都会显示Schematic视图。此外,打开Elaborated Design,也会显示Schematic视图。
扇出
在Schematic视图下,选择右上角的齿轮标记,就会显示Schematic视图的配置面板,如下图所示。只要勾选其中的红色部分,就会在电路图中显示Scalar或bus pin的扇出。
生成与目标单元相关的时序报告
这里的目标单元可以是cell、pin或net,只要可以在Schematic视图中呈现即可。例如,在Schematic视图中选中目标cell,然后点击右键,可以看到Report Timing选项,点击该选项可弹出三个子选项,如下图红色方框所示。选择其中任一选项,可生成相应的时序报告。
对于net和pin,也可以生成相应的时序报告。这里我们列出了cell、pin和net对应的子选项。Schematic视图下的Report Timing给我们提供了一个很好的生成时序报告的方式,这可以使我们把关注点更加集中在Slack为负的路径。
SetupSlack
在时序分析时,通常我们会更关注Setup Slack。点击Schematic视图右上角的齿轮按钮,会弹出Schematic视图配置面板,其中有个选项Setup Slack For Scalar Pin。勾选该选项会显示相应的Setup Slack。
这里需要注意的是,需要先生成时序报告(Report Timing Summary),才可以在Schematic视图中显示。
MMCM的一个重要功能就是过滤抖动,更准确地说是改善抖动。使用MMCM时,建议直接调用IP Core Clocking Wizard,而不要使用原语。
如果MMCM仅仅用作改善抖动,那么要求输出时钟频率和输入时钟频率一致。此时要格外关注三个选项,如下图中的红色方框标记所示。这三个选项都是用作抖动优化的。
三个选项的不同最终反映在MMCM设置中的BANDWIDTH不同。BANDWIDTH有三个值:OPTIMIZED、HIGH和LOW。
为便于说明,我们以输入时钟频率为400MHz时钟为例,输出时钟400MHz,输入时钟抖动分别为800ps和100ps,在三个不同选项下观察输出时钟抖动的变化,如下图所示。可以看到对于800ps抖动,BANDWIDTH为LOW也就是选择Maxmize InputJitter Filtering时,输出抖动最小。而当抖动较小时也就是100ps时,BANDWIDTH为HIGH也就是选择MinimizeOutput Jitter时,输出抖动最小。
对于时钟频率较低的情形又是如何呢?这里我们将输入时钟调整为100MHz,仍然观察输入抖动分别为800ps和100ps的情形,最终结果如下图所示。可以看到结论与上图保持一致。
综上所述,我们不难得出这样的结论:当用作改善抖动时,无论时钟频率高低,如果输入抖动较大,那么选择Maxmize Input Jitter Filtering(对应BANDWIDTH为LOW)时,输出抖动最小;如果输入抖动较小,那么选择Minimize OutputJitter(对应BANDWIDTH为HIGH)时,输出抖动最小。
在ISE中,可以很方便地生成RTL模块的实例化模板,Vivado其实也有这个功能,只是要通过Tcl命令实现,而且这个命令隐藏的比较深。以Vivado 2020.2为例,在菜单下选择Tools,点击其中的XHub Stores,确保Design Utilities被安装,如下图所示,因为这个命令就位于其中。
该命令要在Elaborated Design阶段执行,因此,要先打开ElaboratedDesign。该命令提供了多个选项,包括:
-verilog:生成Verilog模板
-vhdl:生成VHDL模板
-stub:生成实体部分
-template:生成端口映射部分
-cell:指定针对哪个单元生成实例化模板
我们看一下具体使用方法,以Vivado自带的例子工程wavegen为例。打开Elaborated Design,执行如下图所示的4条脚本。
代码第2行会生成如下图所示内容(-stub -verilog):
代码第3行会生成如下图所示内容(-template -verilog):
代码第4行会生成如下图所示内容(-template -vhdl)
代码第5行会生成如下图所示内容(-stub -vhdl)
高扇出net带来的后果是占用了大量的布线资源,尤其是全局复位或全局使能信号,最终很有可能导致布线拥塞,恶化时序,甚至出现布线冲突(Routing Conflict)而无法完成布线。UFDM设计方法学要求在设计早起发现存在的问题并尽快解决,因为在设计初期发现的问题解决起来相对容易一些。
对于扇出,首先要在综合之后就开始分析,打开综合后的设计用Tcl命令report_high_fanout_nets生成扇出报告,如下图所示。该命令有多个选项,使用选项-load_types可以看到负载类型,如图中右侧第4列(Set/Reset Slice)和第5列(Data & Other Slice)。在这个报告中要格外关注Fanout列和Driver Type列。Driver Type表明了net的驱动类型,如果是LUT就要格外注意了,因为工具对这类net的优化比较困难。尽管工具也会做LUT复制,但显然会造成LUT利用率过高。
如果是复位信号扇出很大,此时并不建议采用综合属性MAX_FANOUT的方式降低扇出,因为这会导致复制太多的寄存器,出现不必要的复制,同时也会增加控制集,导致SLICE占用率过高。Xilinx不建议对全局控制信号使用MAX_FANOUT来降低扇出。可行的方法是首先分析代码,从设计需求的层面看是不是可以移除一些复位,例如只是上电复位。其次,采用手工复位树的方式降低扇出,如下图所示。这里需要注意的是对于手工复制的寄存器要通过KEEP属性防止其在综合时被优化,例如,图中block_A的RST2_2和block_B的RST2_1其实的等效寄存器,即二者的输入来源是相同的,同时又被同一时钟驱动。采用复位树的方式可以将扇出隔离至模块层级,这可能就会出现某些模块的时序是收敛的,某些模块的时序仍然因为扇出较大而未能收敛,这其实已经部分解决了高扇出引起的时序违例问题。
在此基础上,可继续在模块内使用复位树的方式降低扇出,也可以采用phys_opt_design的选项-force_replication_on_nets降低扇出,该选项非常使用,尤其是当nets隶属于IP时,而对于IP我们无法从代码层面进行优化。此外,phys_opt_design还提供了-directive AggressiveFanoutOpt也可优化扇出。
对于局部控制信号或数据信号,且扇出不是很高的情况下,如果仍需降低扇出,则可以采用MAX_FANOUT的方式。此外,Vivado在opt_design和place_design阶段都会对扇出进行优化,例如,opt_design阶段提供了选项-hier_fanout_limit。Xilinx给出了扇出与时钟频率直接的关系,如下图所示。
DSP48最早出现在XilinxVirtex-4 FPGA中,但就乘法器而言,Virtex-II和Virtex-II Pro中就已经有了专用的18x18的乘法器,不过DSP48可不只是乘法器,其功能更加多样化。DSP48基本结构如下图所示(图片来源:ug073, Figure 2-1)。DSP48中的核心单元是18x18的乘法器。从图中不难看出,DSP48可实现基本数学函数P=Z±(X+Y+CIN)。这里X、Y和Z是图中3个MUX的输出。根据图中MUX的输入,上述数学函数可以变为P=AB+C或P=AB+PCIN,后者需用级联DSP48。因为PCIN和PCOUT是专用走线相连。同时,与Virtex-II不同,Virtex-II中,相邻的DSP48和Block RAM共享互连资源,而在Virtex-4中,DSP48和Block RAM有独立的布线资源。
此外,从资源角度看,Virtex-4SX55包含的DSP48最多,一共8列512个DSP48,在全流水模式下,可运行到的最高频率为500MHz。
在Virtex-5中,引入了增强型DSP48,称之为DSP48E,其基本结构如下图所示(图片来源ug193, Figure 1-1)。这种增强体现在以下几点:乘法器变为25x18;A端口变为30位,其中低25位可用于乘法器的输入,A和B可拼接为48位,从而可实现{A,B}+C(两个48位数据相加);乘法器之后不再是简单的累加器,而是功能更为多样的ALU(算术逻辑单元),可实现算术运算和逻辑运算。这里特别介绍一下ALU,ALU支持SIMD功能(Single InstructionMultiple Data),使得ALU可配置为2个24位的加法器或者4个12位的加法器。从资源角度看,Virtex-5 SX240T包含的DSP48E最多,共1056个,在全流水模式下,可运行到的最高频率为550MHz。
在Virtex-6和7系列FPGA中,DSP48E功能进一步增强,称之为DSP48E1,其基本结构如下图所示(图片来源ug369, Figure 1-1)。最显著的变化是在DSP48E中添加了预加器(可实现25位的加法运算),这对于系数对称的滤波器而言非常有利,可将乘法器资源减半。
在UltraScale和UltraScale Plus系列FPGA中,引入了DSP48E2,其基本结构如下图所示(图片来源ug579,Figure 2-1)。相比于DSP48E1,其中的乘法器变为27x18,端口D的位宽也由25位变为27位,这样预加器可支持27位的加法运算。预加器的输出可同时送给乘法器的两个输入端口,从而很容易实现平方运算。同时,增加了一个MUX,对应图中的W。ALU可实现Z+W+X+Y。
对比DSP48、DSP48E、DSP48E1和DSP48E2,如下表所示。
接下来,我们考虑4个32-bit有符号数相加该如何实现,其中目标时钟频率仍为400MHz。以UltraScale Plus系列芯片为目标芯片。
第一种方案:四个数直接相加
此方案对应的电路图如下图所示。这里不难看出关键路径是三个加法器所在路径,这将是时序收敛的瓶颈。
此电路对应的SystemVerilog代码如下图所示。
从综合后的结果来看,逻辑级数最高为7。
第二种方案:加法树
加法树的结构如下图所示,两两相加。与第一种方案相比,可以有效降低逻辑级数。
此电路对应的SystemVerilog代码如下图所示。
从综合后的结果来看,逻辑级数最高为6。
第三种方案:加法链
之所以选用加法链的结构是因为DSP48本身就是这种链式结构。对应的电路如下图所示。其中a0和a1端口有一级寄存器,a2端口有两级寄存器,a3端口有三级寄存器。
此电路对应的SystemVerilog代码如下图所示。
接下来,我们对这三种方案进行比较,如下图所示。不难看出,第一种方案逻辑级数最高,消耗的LUT也最多,时序结果也是最差的(尽管达到了收敛的目的)。后两种方案不相上下。
如果将这三种方案通过综合属性USE_DSP使其映射到DSP48上,结果如何呢?如下图所示。不难看出,第一种方案只消耗了两个DSP,资源利用率最低,但时序也是最糟糕的。后两种方案都用了三个DSP,但第三种方案由于可以很好地匹配硬件结构,故时序最好。
对比下来不难得出这样的结论:写RTL代码时,尽可能地做到代码风格与硬件结构相匹配,可达到更好的性能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。