芯片SDC约束
1.芯片开发流程
数字开发过程中主要可以分为数字前端和数字后端,每个项目首先都是从客户那里拿到需求,架构人员根据需求指定整个芯片的设计方案,在进入到数字前端进行设计和验证,对fix的代码我们需要综合成门级网表,在对网表做PR,那么综合和PR都属于数字后端范围内了,和芯片的具体实现功能是相关比较小的。
在数字开发中,每个阶段常用的工具
2.综合是在做什么
综合处于设计前端(Front End)和后端(Back End)的交界处。简单概述就是完成了RTL代码到网表的转换,从逻辑级转换到了可实际生产的门级/晶体管。综合实际上就是将写代码中的always时序逻辑和assign这种组合逻辑,使用标准单元库中器件转化到电路的过程。
一般地,生成的网表文件分为:
.ddc,实际的网表文件
.v,供后仿真使用的网表文件
那么我们再使用DC综合工具的时候,就是需要来告诉综合工具在实现相同功能的许多可能实现方式中,应该选择哪个以同时满足对面积、功率和性能的要求,这个要求也就是约束。
约束是来告诉综合工具在实现相同功能的许多可能实现方式中,应该选择哪个以同时满足对面积、功率和性能的要求。
建立时间:数据在时钟上升沿之前稳定的时间
保持时间:数据需要在时钟上升沿之后稳定的时间
3.综合需要做什么
SDC约束总共有几类:
- 时序约束
- 面积和功率约束
- 设计规则约束
- 接口约束
- 特定模式和配置约束
- 设计要求的异常
- 其他命令
4.各约束详解
4.1 create_clock
create_clock -period period_value //时钟周期
[source_objects] //时钟源,端口、引脚或网络
[ -name clock_name] //时钟名
[ -waveform edge_list] //指定占空比
[ -add ] //同源多时钟
[ -comment comment_string] //注释
create_clock -period 10 -name CLK -waveform {3 5 8 9} [get_ports C3]
create_clock -name C1 -period 10 [get_ports CLK]
create_clock -name C2-period 15 [get_ports CLK] -add
4.2 create_generated_clock
create_generated_clock [source_objects] //创建派生时钟的端口、网络
-source clock_source_pin //源时钟的端口、网络
[ -master_clock master_clock_name] //源时钟名
[ -name generated_clock_name] //派生时钟名
[ -edges edge_list] //指定派生时钟根据那个源时钟边沿产生
[ -divide_by factor] //派生时钟是源时钟的分频比
[ -multiply_by factor] //派生时钟是源时钟的倍频比
[ -invert ] //派生时钟的反向
[ -edge_shift shift_list] //相对原时钟的边沿位移
[ -duty_cycle percent] //派生时钟的占空比
[ -combinational ] //穿过组合逻辑
[ -add ] //同源多时钟
[ -comment comment_string] //注释
create_generated_clock [get_pins FF1/Q] -name GCLK1 -source [get_ports CLK] -divide_by 2
create_generated_clock [get_pins FF1/Q] -name GCLK1 -source [get_ports CLK] -edges { 1 3 5}
create_generated_clock [get_pins FF1/QBAR] -name GCLK2 -source [get_ports CLK] -divide_by 2 -invert
create_generated_clock [get_pins AN1/Z] -name PULSE -source [get_ports clk] -edges { 1 1 3} -edge_shift {0 2 0}
create_clock -name C1 -period 10 [get_ports CLK]
create_clock -name C2-period 15 [get_ports CLK] -add
create_generated_clock [get_pins FF2/Q] -name GC1 -divide_by 3 -source [get_port CLK] -master_clock C1
create_generated_clock [get_pins FF2/Q] -name GC2 -divide_by 3 -source [get_port CLK] -master_clock C2 -add
4.3 set_clock_groups
设定时钟组能够切断不同组中不相关时钟之间的时序, Timing Analyzer排除每个独立组的时钟之间的时序路径。
set_clock_groups [- name group_name] //时钟组名
[- group clock_list] //组内时钟列表
[- logically_exclusive ] //时钟组逻辑独立
[- physically_exclusive ] //时钟组物理独立
[- asynchronous ] //时钟组异步
[- allow_paths ] //异步时钟时进行串扰分析,而不禁用时钟之间的时序路径
[- comments comment_string] //注释
set_clock_groups -group A -group B
会分析A的时钟,B的时钟,但A和B时钟的交互式异步关系不进行分析,C、D没有指定,则不相干,进行时序分析
set_clock_groups -group {A B}
A、B在一个时钟组内,会分析A、B的交互时序分析,C、D没有指定,则不相干,进行时序分析
GC1时钟和GC2时钟为逻辑独立关系,C1和C2经过选择后作为派生时钟,不会在设计中并存。
create_clock -period 10 -name C1 -waveform {0 5} [get_ports C1]
create_clock -period 20 -name C2 -waveform {0 12} [get_ports C2]
create_generated_clock -name GC1 -source [get_ ports C1] [get_ pins mux1/A] –combinational
create_generated_clock -name GC2 -source [get_ ports C2] [get_ pins mux1/B] -combinational
set_clock_groups -logically_exclusive -group GC1 -group GC2
GC1时钟和GC2时钟为物理独立关系,C1和C2的派生时钟GC1和GC2在一个网络中不会同时存在,但是C1和C2在其他网络中是有交互关系的。
create_clock -period 10 -name C1 -waveform {0 5} [get_ports C1]
create_clock -period 20 -name C2 -waveform {0 12} [get_ports C2]
create_generated_clock -name GC1 -divide_by 1 -source [get_pins mux1/A] [get_pins mux1/Z] -combinational
create_generated_clock -name GC2 -divide_by 1 -source [get_pins mux1/B] [get_pins mux1/Z] -combinational -add
set_clock_groups -physically_exclusive -group GC1 -group GC2
4.4 set_clock transition
信号从一个状态跳变到另一个状态的过渡时间,既压摆率slew。
set_clock transition [- rise ] //设定上升沿的过渡时间
[- fall ] //设定下降沿的过渡时间
[- max ] //设定过渡时间的最大值
[- min ] //设定过渡时间的最小值
clock_list //设定时钟源
transition_time //设定时间,单位为设定的unit
set clock transition - rise 0 . 2 [get clocks C1]
set clock transition - fall - min 0 . 2 [get clocks C2]
set clock transition - fall - max 0 . 4 [get clocks C2]
4.5 set_clock_uncertainty
源时钟到达所有触发器的时间因为时钟树网络路径延迟、耦合电容或者PVT的关系。是不会很一致的对其的。时钟到达不同点的差异纪委时钟偏斜skew。PLL产生时钟由于串扰或者其他原因时钟边沿具有的不确定性即为抖动jitter。由于skew和jitter的存在,无法准确的计算出一个边沿何时到达触发器,就是我们这节说的不确定度uncertainty。
set_clock_uncertainty
[- from | - rise_from | - fall_from from_clock]
[- to |- rise_to | - fall_to to_clock]
[- setup ]
[- hold ]
[- rise ]
[- fall ]
[object_list]
Uncertainty_value
# Clock uncertainty from C1 to C2 for setup and hold
set_clock_uncertainty -from C1 -to C2 -setup 0.5
set_clock_uncertainty -from C1 -to C2 -hold 0.5
# Clock uncertainty from rising edge (C1) to falling edge (C2)
set_clock_uncertainty -rise_from C1 -fall_to C2 0.5
4.6 set_input_delay
假设信号到达I1的最早有效时间为3ns,通过组合逻辑C1的最小延迟是4ns,那么信号到达F1的最早有效时间为7ns,这样,只要F1的保持时间要求小于7ns,那么新的数据就不会干扰之前的数据。
如果信号到达I1的最迟时间是5ns,通过组合逻辑C1的最大延迟是6ns,那么信号到达F1的最迟有效时间为11ns,如果我们假设F1的建立时间要求是0.5ns,那么时钟信号到达F1的时间是11.5ns或者更晚一些,触发器F1都能正常的捕获数据。
这样我们能够知道输入延迟的最小时间用于分析hold,最大延迟用于分析setup。
设定的延迟为外部器件到达IC或FPGA的最大最小延迟。
最大延迟为从振荡器的时钟到达外部器件的最大延迟+外部器件内的最大时钟延迟+到IC路径的最大延迟-振荡器时钟到达IC的最小延迟,最大延迟用于IC器件分析setup是否满足。
最小延迟为从振荡器的时钟到达外部器件的最小延迟+外部器件内的最小时钟延迟+到IC路径的最小延迟-振荡器时钟到达IC的最大延迟,最小延迟用于IC器件分析holp是否满足。
set_input_delay [ -clock clock_name] //指定参考时钟,默认上升沿
[ -clock_ fall ] //指定参考时钟clock_name的下降沿
[ -level_sensitive ] //启动元器件是锁存器时使用,慎用
[ -rise ] //限定输入延迟与输入端口信号的上升沿对应
[ -fall ] //限定输入延迟与输入端口信号的下降沿对应
[ -max ] //指定延迟为到达输入端口的最晚时间,setup
[ -min ] //指定延迟为到达输入端口的最早时间,hold
[ -add_delay ] //不覆盖之前的约束,进行的额外约束
[ -network_latency_included ] //外部器件端口到输出触发器的延迟
[ -source_latency_included ] //时钟源到外部器件端口的延迟
delay_value port_pin_list //延迟值及端口
如果设定的延迟值包含了L1则需开启-source_latency_included;如果设定的延迟值包含了L2则需要开启-network_latency_included 。
set_input_delay -clock CLK1 -clock_fall 2.0 [get_ports I1]
set_input_delay -clock CLK1 7.0 [get_ports I1]
4.7 set_output_delay
设定的延迟为IC或FPGA到达外部器件的最大最小延迟。
最大延迟为从振荡器的时钟到达外部器件的最大延迟+外部器件内的最大时钟延迟+到IC路径的最大延迟-振荡器时钟到达IC的最小延迟,最大延迟用于IC器件分析setup是否满足。
最小延迟为从振荡器的时钟到达外部器件的最小延迟+外部器件内的最小时钟延迟+到IC路径的最小延迟-振荡器时钟到达IC的最大延迟,最小延迟用于IC器件分析holp是否满足。
set_output_delay [ -clock clock_name] //指定参考时钟,默认上升沿
[ -clock_ fall ] //指定参考时钟clock_name的下降沿
[ -level_sensitive ] //启动元器件是锁存器时使用,慎用
[ -rise ] //限定输出延迟与输出端口信号的上升沿对应
[ -fall ] //限定输出延迟与输出端口信号的下降沿对应
[ -max ] //指定延迟为到达输出端口的最晚时间, hold
[ -min ] //指定延迟为到达输出端口的最早时间, setup
[ -add_delay ] //不覆盖之前的约束,进行的额外约束
[ -network_latency_included ] //外部器件端口到输出触发器的延迟
[ -source_latency_included ] //时钟源到外部器件端口的延迟
delay_value port_pin_list //延迟值及端口
对于apb的pwdata pradata port的input delay和output delay约束为:
set_input_delay -max [expr 0.7 * apb_clk] -clock apb_clk [get_ports apb_pwdata]
set_output_delay -max [expr 0.7 * apb_clk] -clock apb_clk [get_ports apb_prdata]
为什么要设置70%?因为input delay是约束芯片外部的delay 情况,也就是外部约束70%,内部剩余30%的余量,因为外部的情况并不太清楚,所以估计的悲观一些,output delay原因同理。如果估计的过于乐观,那么如果都是这么设置的有可能导致两个block接上之后timing设置都不能满足timing check要求。
4.8 set_driver
set_driver实际约束的电阻值得大小,但是实际电路中对应驱动能力是电阻值的倒数,电阻值越大,驱动能力约弱,电阻值约小驱动能力约强。min最小电阻意味着更强的驱动能力,信号的过渡时间更快,因此用于hold分析;max最大电阻意味着驱动能力弱,过渡时间长,因此用于setup分析。
对于芯片级的设计(即顶层设计)而言,它的输入端口的驱动能力很难用工艺库中的某个具体单元去量化,因此我们一般在顶层通过set_drive命令定义输人电阻的大小,由于在顶层,要驱动的是负载较大的IO单元,故需要的驱动能力较强,典型的值是0.05,单位取工艺库中电阻的单位,一般为kΩ。由于通常情况下,设计的时钟和复位端都由驱动能力很大的单元或树形缓冲来驱动,所以可以用set_drive命令将这两个端口的驱动电阻设置为0,也就是驱动强度为无穷大。
set_drive [ -rise ] [ -fall ] //指定约束信号的上升沿或下降沿驱动,不指定为都约束
[ -min ] [ -max ] //指定驱动的最小电阻或最大电阻,最小电阻用于hold分析,最大电阻用于setup分析
resistance_value port_list//驱动值及端口列表
给设计的输入端口A、B和C指定2.0 kΩ(单位一般是kΩ,跟库中定义有关)的电阻,可以用该命令:
set_drive 2.0 {A B C}
为设计中的所有输入端口指定12.3 kΩ的电阻,可以用该命令:
set_drive 12.3 [all_inputs]
将ddfs的时钟和复位端的驱动设为无穷大,即驱动阻抗设为零:
set_drive 0 {clk, reset}
4.9 set_driving_cell
set_driving_cell [ -lib_cell lib_cell_name]//选择cell的名字去驱动到指定的端口
[ -rise ] [ -fall ] //指定上升沿或下降沿的驱动单元
[ -min ] [ -max ] //指定上升沿或下降沿的驱动单元
[ -library lib_name] //指定驱动单元的库
[ -pin pin_name] //指定驱动单元的哪个管脚用于驱动
[ -from_pin from_pin_name] //指定驱动的时序弧
[ -multiply_by factor] //指定被驱动端口占驱动端口能力的占比
[ -dont_scale ] //不使用降额特性
[ -no_design_rule ] // 阻止驱动引脚的规则属性转移
[ -clock clock_name] //指定关联时钟
[ -clock_fall ] //指定关联时钟的下降沿
[ -input_transition_rise rise_time] //指定驱动单元时序弧通路上升沿过渡时间
[ -input_transition_fall fall_time] //指定驱动单元时序弧通路下降沿过渡时间
port_list //指定端口列表
对于单元B1的管脚I1选择它的驱动单元为MUX21,会有两个时钟关联,从clk1时钟的F1触发器的A时序弧经过MUX21驱动I1,另一条是从clk2时钟的F2触发器的B时序弧经过MUX21驱动I1。
set_driving_cell -lib_cell MUX21 -from_pin A -clock CLK1 [get_ports I1]
set_driving_cell -lib_cell MUX21 -from_pin B -clock CLK2 [get_ports I1]
4.10 set_input_transition
set_input_transition命令为输入端口指定一个固定的transition时间,工具会用该transition时间来计算它驱动的逻辑电路的延迟。
set_input_transition [ -rise ] [ -fall ] //指定上升沿或下降沿的transition 时间
[ -min ] [ -max ] //指定最小或最大的transition 时间
[ -clock clock_name] //指定选择的时钟源
[ -clock_fall ] //指定选择时钟源的下降沿
transition port_list //指定transition值及端口列表
set_input_transition 0.12 [get_ports A]
4.11 set_driving_cell或set_drive或set_input_transition命令都可以指定输入端口的驱动,那么它们有什么区别呢?
set_driving_cell命令通常是首选方法,因为它是最真实的模型。当我们的设计是模块级(Block level)的design时,我们的设计前面一般还有其他的设计。那么前面模块的输出便是我们模块的输入。
当我们的设计是芯片级(Chip level)的顶层设计时,芯片外围电路要驱动的是负载较大的IO单元,故需要的驱动能力较强,一般无法用库里面已有的单元来指定驱动强度,因此一般用set_drive命令。
对于顶层设计(Chip level的设计)而言,除了用set_drive命令之外,我们也可以使用set_input_transition命令来为其设置固定的transition时间,因为芯片外边一般存在驱动能力比较强的器件和大电容。在这种情况下,transition时间相对独立于当前设计中的电容。
对于内部的一个模块sub_design2,由于是Block level的design,因此我们根据已知的前级设计的输出单元用set_driving_cell来为本模块指定输入端口的驱动强度。
对于Chip level的顶层设计top_level_design而言,芯片外部的电路需要驱动负载较大的PAD,因此需要使用set_drive命令来通过设置电阻值来指定输入端口的驱动强度。
4.12 set_load
set_load 是指output端口的负载,设置的值会影响输出到端口的net上的电容,从而影响相应的路径延时。
set_load [ -min ] [ -max ] //指定负载电容为最大值或最小值
[ -subtract_pin_load ] //标注净电容负载时,需要打开避免与后端输入负载重复
[ -pin_load ] //指定为管脚负载
[ -wire_load] //指定为线路负载
value objects //指定负载电容值和约束的端口
set_load [expr {30.0/1000}] [get_ports B]
4.13 set_ fanout _load
set_ fanout _load 是指output端口的负载,设置的值会影响输出到端口的net上的电容,从而影响相应的路径延时。
set_fanout_load value //指定扇出负载值
port_list //指定约束的端口
下面的例子设定所有的输出端口负载电容为2倍标准负载电容
set_fanout_load 2 [get_outputs]
set_load与set_fanout_load不同,set_load指定负载的实际电容负载,而set_fanout_load是根据标准负载指定负载值。电容负载(set_load)=标准负载(set_fanout_load)×单个标准负载的电容负载。
4.14 set_max_area
如果不设置面积的约束,DC将做最小限度的面积优化。设置了面积的约束后,DC将在达到面积约束目标时退出的面积优化。如果设置面积的约束为“0” , DC将为面积做优化直到再继续优化也不能有大的效果,这时,DC将中止优化。注意,对于很大(如百万门电路)的设计,如将面积的约束设置为“0” , DC可能要花很长的时间为设计做面积优化。综合时,运行的时间很长。
我们用set_max_area命令为设计作面积的约束。例如:
set_max_area 10000
当设计不是很大的时候,让DC做最大的面积优化约束,我们就可以使用下面的命令进行面积约束:
set_max_area 0
4.15 set_max_fanout
set_max_fanout约束了 由 input port 驱动的net(以及design内部所有nets)上所有相连的fanout_load值的总和,而该fanout_load值由lib库中的default_fanout_load定义,这个值一般都为“1”。由此大家可以简单理解为set_max_fanout约束了某条net上驱动的所有cell的个数的总和,而这条net可以由input port驱动,也可以使内部的任何一条net。
设定所有的输入端口和内部设计net的最大扇出为16:
set_max_fanout 16
4.16 set_case_analysis
set_case_analysis常应用于多mode的设计,给一些pin/port设置常量0或1,来实现切换mode分析时序的作用。举例来说,设计中存在SCAN mode和FUNC mode,在FUNC mode下将SCAN enbale信号用set_case_analysis设置为0,在FUNC mode下就不会分析scan clock的时序。
create_clock [get_ports func_clk] -name func_clk -period 10 -waveform {0 5}
create_clock [get_ports scan_clk] -name scan_clk -period 100 -waveform {0 50}
set_case_analysis 0 [get_ports scan_ena]
4.17 set_false_path
false path指逻辑上存在,但不需要分析的timing path,使用set_false_path去忽略timing分析和优化的过程,减少run time,把更多的资源放在优化真实的critical path上。设置为false path的path,工具不会考虑它的时序约束,但是delay还是要计算的,因为这条false path上的组合逻辑可能应用于其他的path。
如设定复位信号不做时序分析:
set_false_path from [get_port reset_n]
set_false_path from [get_port reset_n] to [all_registers]
4.18 set_disable_timing
可以想一下如果这个组合逻辑环,用了set_false_path约束会怎么样?当然设计中存在组合逻辑环本身就是错误的事情。
set_disable_timing [- from pin_name]
[- to pin_name]
design_objects
set_disable_timing -from B -to Z I1
set_false_path和set_disable_timing的却别在于set_false_path只是阻止改路径的时序分析,但是延迟计算不会停止,但是set_disable_timing是打断改时序弧,将路径本身从时序分析中移除,并且所有经过这段时序弧的 path 都会被打断。
4.19 set_multicycle_path
默认情况下综合工具会把每条路径定义为单周期路径,即源触发器在时钟的任一边沿启动(launch)的数据都应该由目的触发器在时钟的下一上升沿捕获(capture)。有的设计可能存在时序例外(timing exceptions),如多周期路径、虚假路径等。数据从起点到终点的传输时间需要一个时钟周期以上才能稳定的路径,这类路径被称为多周期路径。
set_multicycle_path [ -setup ]
[ -hold ]表明多周期路径设置是对setup(max_delay)或者是hold(min_delay),setup时默认移动capture_clk,hold时默认移动launch_clk;
[ -rise ] [ -fall ]
[ -start ]表示强制移动的为start clock即launch clock;
[ -end ]表示强制移动的为end clock即capture clock。
[ -from from_list]
[ -to to_list] [ -through through_list]
时钟上升沿对齐,且默认的STA检查情况为单周期,默认情况下:setup检查是从launch_clk的一个上升沿到capture_clk的下一个上升沿,hold检查是从launch_clk的一个上升沿到capture_clk的捕获沿的前一个沿。
set_multicycle_path 1 -setup -from CLK1 -to CLK2
set_multicycle_path 0 -hold -from CLK1 -to CLK2
set_multicycle_path 2 -setup -from CLK1 -to CLK2
时钟上升沿对齐,且默认的STA检查情况为单周期,默认情况下:setup检查是从launch_clk的一个上升沿到capture_clk的下一个上升沿,hold检查是从launch_clk的一个上升沿到capture_clk的捕获沿的前一个沿。
set_multicycle_path 1 -setup -from CLK1 -to CLK2
set_multicycle_path 0 -hold -from CLK1 -to CLK2
set_multicycle_path 2 -setup -from CLK1 -to CLK2
set_multicycle_path -hold 1 -from CLK1 -to CLK2
由于launch_clk和capture_clk的频率不一样,所以需要用-start/end来指定移动的clk,这样PT检查才不会出错。 由于-hold默认向右移动launch_clk,现在加上-end后即为指定向左移动capture_clk。
set_multicycle_path 1 -setup -from CLK1 -to CLK2
set_multicycle_path 0 -hold -from CLK1 -to CLK2
set_multicycle_path 3 -setup -end -from CLK1 -to CLK2
set_multicycle_path 2 -hold -end -from CLK1 -to CLK2
set_multicycle_path 1 -setup -from CLK1 -to CLK2
set_multicycle_path 0 -hold -from CLK1 -to CLK2
set_multicycle_path 3 -setup -start -from CLK1 -to CLK2
set_multicycle_path 2 -hold -start -from CLK1 -to CLK2
4.20 set_units
设定约束中变量使用的基本单位
set_units
-capacitance cap_unit //设定电容,f\ff\pf\nf\uf\mf\10ff\100ff
-resistance res_unit //设定电阻,ohm\Kohm\mohm\Mohm\10ohm\100ohm
-time time_unit //设定时间,s\fs\ps\ns\us\ms\10ps\100ps
-voltage voltage_unit //设定电压,V\fV\pV\nV\uV\mV\10mV\100mV
-current current_unit //设定电流,A\fA\pA\nA\uA\mA\10uA\100uA\10mA\100mA
-power power_unit //设定功率, W\fW\pW\nW\uW\mW\10uW\100uW\10mW\100mW\10pW\100pW\10nW\100nW
set_units –tims ns –resistance Kohm –capacitance pf –power uW –voltage V –current A