4、时序约束基础:时钟定义与输入输出延迟
好,咱们进入时序约束的核心部分了。说实话,很多新手觉得STA难,其实一大半的坑都出在约束没写对。时钟定义错了,后面所有分析都是白搭。我见过太多人花了两周修时序,最后发现是时钟约束写错了——那种感觉,真的想砸电脑。
4.1 时钟定义:create_clock
时钟是时序分析的“心跳”。没有时钟,STA根本没法跑。create_clock这个命令,说白了就是告诉工具:嘿,这里有个时钟,周期多长,波形长啥样。
基本语法长这样:
create_clock -name clk_sys -period 10 [get_ports clk_in]
这条命令的意思是:在端口clk_in上创建一个叫clk_sys的时钟,周期10ns。嗯,10ns对应100MHz,这是最常见的写法。
但实际项目中,时钟波形往往不是简单的50%占空比。比如DDR接口,可能需要25%占空比。这时候就要加-waveform参数:
create_clock -name ddr_clk -period 5 \
-waveform {0 1.25} [get_ports ddr_ck]
-waveform后面跟两个值:上升沿时间和下降沿时间。上面这个例子,上升沿在0ns,下降沿在1.25ns,周期5ns。说白了就是高电平只占1/4周期。
关键点:时钟定义必须放在所有约束的最前面。工具读约束文件是按顺序处理的,时钟没定义,后面的输入输出延迟、生成时钟全都会报错。
4.2 时钟延迟与不确定性
时钟从源端到触发器,中间有走线延迟。这个延迟在STA里分两块:
- 网络延迟(network latency):时钟信号在芯片内部走线的延迟
- 源延迟(source latency):时钟从外部源到芯片端口的延迟
我习惯用set_clock_latency来约束:
set_clock_latency -source 2.0 [get_clocks clk_sys]
set_clock_latency 1.5 [get_clocks clk_sys]
-source表示源延迟,不加-source表示网络延迟。这里源延迟2ns,网络延迟1.5ns,加起来时钟从源头到触发器总共3.5ns。
我的经验:早期项目我经常忽略时钟延迟,觉得工具会自动算。后来发现,对于片外时钟输入,不设source latency会导致setup分析过于乐观。有一次流片回来,芯片在低温下时序失效,就是因为没考虑时钟源延迟的偏差。
时钟不确定性(clock uncertainty)是另一个重要概念。它用来建模时钟抖动、PLL相位噪声、时钟树偏差等非理想因素。说白了,就是给时序留点余量:
set_clock_uncertainty -setup 0.2 [get_clocks clk_sys]
set_clock_uncertainty -hold 0.05 [get_clocks clk_sys]
setup uncertainty通常比hold大,因为setup受时钟抖动影响更大。hold主要受时钟树偏差影响,一般小一些。
注意:时钟不确定性设太大,会过度约束设计,导致面积和功耗增加。设太小,又可能漏掉真正的时序问题。我一般参考工艺厂商的推荐值,再根据项目经验微调。比如28nm工艺,setup uncertainty我通常设0.15-0.25ns。
4.3 输入延迟约束
输入延迟描述的是:外部信号从片外到达芯片输入端口,花了多长时间。这个时间包括外部器件的时钟到输出延迟(Tco)和PCB走线延迟。
命令格式:
set_input_delay -clock clk_sys -max 3.0 [get_ports data_in]
set_input_delay -clock clk_sys -min 1.5 [get_ports data_in]
-max用于setup分析,-min用于hold分析。为什么要有两个值?因为外部器件的延迟有工艺偏差,最大最小代表最差情况。
嗯,这里有个容易搞混的地方:输入延迟是相对于时钟边沿的。比如-max 3.0,意思是数据在时钟沿之后3ns才到达。那对于setup分析,数据到达时间就是时钟沿+3ns。
避坑指南:我曾经在一个项目中,把输入延迟的-max和-min写反了。结果setup分析通过,hold分析也通过,但芯片就是工作不稳定。查了两天才发现,-min应该比-max小,我写成了-min比-max大。这导致hold约束太松,内部触发器hold time不满足。
4.4 输出延迟约束
输出延迟和输入延迟是对称的。它描述的是:芯片内部数据从输出端口到外部器件,需要多长时间才能被正确采样。
set_output_delay -clock clk_sys -max 2.5 [get_ports data_out]
set_output_delay -clock clk_sys -min 0.8 [get_ports data_out]
这里的-max和-min,含义和输入延迟正好相反。对于输出延迟:
- -max:外部器件要求的最大建立时间,用于内部setup分析
- -min:外部器件要求的最小保持时间,用于内部hold分析
说白了,输出延迟就是告诉工具:外部器件需要数据在时钟沿之前多久准备好(setup),以及之后保持多久(hold)。
我的习惯:写输出延迟时,我会先画出时序图。把内部时钟、输出数据、外部时钟、外部数据采样点都画出来。然后根据时序图算延迟值。这样不容易出错。你想想看,光靠脑子想,很容易把max和min搞反。
4.5 完整约束示例
把上面这些串起来,一个典型的约束文件大概长这样:
# 时钟定义
create_clock -name sys_clk -period 10 [get_ports clk_in]
# 时钟延迟
set_clock_latency -source 1.5 [get_clocks sys_clk]
set_clock_latency 1.0 [get_clocks sys_clk]
# 时钟不确定性
set_clock_uncertainty -setup 0.2 [get_clocks sys_clk]
set_clock_uncertainty -hold 0.05 [get_clocks sys_clk]
# 输入延迟
set_input_delay -clock sys_clk -max 2.0 [get_ports {addr[*] data_in[*]}]
set_input_delay -clock sys_clk -min 1.0 [get_ports {addr[*] data_in[*]}]
# 输出延迟
set_output_delay -clock sys_clk -max 3.0 [get_ports data_out]
set_output_delay -clock sys_clk -min 1.2 [get_ports data_out]
这个例子覆盖了最基本的约束场景。实际项目中,还会有多时钟域、生成时钟、false path等更复杂的约束,但基础就是这些。
总结一下:时钟定义是STA的基石,时钟延迟和不确定性是给时序留余量,输入输出延迟是连接片内和片外的桥梁。这四个概念搞清楚了,时序约束就算入门了。下一章咱们聊生成时钟和时钟分组,那才是真正考验功力的地方。