4. 综合约束入门

各位同学,今天我们来聊聊综合约束中最基础、也最容易被忽视的部分。说白了,综合约束就是告诉工具:你的芯片要跑多快、外部信号长什么样、驱动能力够不够。我见过太多项目,因为约束没写对,导致综合出来的网表时序一塌糊涂,最后不得不返工。

嗯,咱们一个一个来看。

1. 时钟定义:create_clock

时钟是时序分析的基准。没有时钟,工具根本不知道什么时候该采样数据。我个人习惯,拿到设计的第一件事就是先把所有时钟定义好。

最基本的写法是这样的:

create_clock -name clk -period 10 [get_ports clk]

这里 -period 10 表示周期是10ns,也就是频率100MHz。注意,单位是纳秒,不是皮秒,也不是微秒。我刚开始做后端时,有一次把单位搞混了,写成了 -period 10000,结果工具报了一堆违例,查了半天才发现是单位问题。

时钟定义还有一些进阶用法:

  • 波形定义:默认是50%占空比,上升沿在0ns,下降沿在5ns。如果你想自定义,可以用 -waveform 参数。
  • 虚拟时钟:用于约束与外部接口的时序,不连接到任何端口。比如:create_clock -name vclk -period 5
  • 生成时钟:由内部逻辑分频或倍频产生的时钟,用 create_generated_clock 定义。

重要提醒:时钟定义一定要准确。我曾经遇到一个项目,时钟树综合后发现时钟偏斜很大,最后定位到是生成时钟的源点定义错了。工具以为时钟是从某个寄存器出来的,实际上是从组合逻辑出来的,导致时序分析完全不准。

2. 输入输出延迟:set_input_delay / set_output_delay

时钟定义好了,接下来要告诉工具:外部信号相对于时钟是什么时候到达的,什么时候需要被外部采样。这就是输入输出延迟约束。

先看输入延迟:

set_input_delay -clock clk -max 2.5 [get_ports data_in]
set_input_delay -clock clk -min 1.0 [get_ports data_in]

这里 -max 2.5 表示数据最晚在时钟沿后2.5ns到达芯片引脚,-min 1.0 表示最早在时钟沿后1.0ns到达。工具会根据这两个值来检查内部寄存器的建立时间和保持时间。

输出延迟类似:

set_output_delay -clock clk -max 3.0 [get_ports data_out]
set_output_delay -clock clk -min 0.5 [get_ports data_out]

输出延迟表示数据从芯片引脚输出后,外部器件需要多长时间来采样。说白了,就是给外部留出足够的裕量。

我的经验:输入输出延迟的值怎么定?一般根据外部器件的datasheet来算。比如外部器件要求数据在时钟沿前2ns稳定,那输出延迟就设成2ns。如果datasheet没给,那就按经验值,通常设成周期的20%~30%。

3. 驱动与负载约束

驱动和负载约束,说白了就是告诉工具:芯片的输入引脚被什么驱动,输出引脚要驱动多大的负载。这直接影响综合工具对cell的选择和优化。

驱动约束用 set_driving_cell:

set_driving_cell -lib_cell INVX1 [get_ports data_in]

这表示输入端口 data_in 被一个 INVX1 标准单元驱动。工具会查库文件里 INVX1 的输出电阻,然后计算输入信号的上升/下降时间。

负载约束用 set_load:

set_load -pin_load 0.5 [get_ports data_out]

这表示输出端口 data_out 的负载是0.5pF。工具会根据这个负载来选择合适的驱动单元,确保信号能按时到达。

避坑指南:我曾经犯过一个错误,把所有输入端口都设成同一个驱动单元,结果有些端口实际驱动能力很强,有些很弱,导致综合出来的网表在某些路径上驱动不足,流片回来信号质量很差。后来我学乖了,每个端口都根据实际外部器件来设。

知识体系总览

下面这张图,是我自己总结的综合约束核心逻辑,你一看就明白了:

综合约束核心知识体系 时钟定义 create_clock 输入延迟 set_input_delay 输出延迟 set_output_delay 驱动约束 set_driving_cell 负载约束 set_load 时序分析 STA检查 所有约束最终服务于静态时序分析(STA) 时钟是基准,输入输出延迟定义边界,驱动负载决定cell选择

实际项目中的约束检查清单

最后,我给大家列一个我自己常用的约束检查清单。每次做综合前,我都会过一遍:

检查项 说明 常见错误
时钟定义完整性 所有时钟是否都已定义?包括虚拟时钟和生成时钟 漏定义内部生成时钟
时钟周期正确性 周期值是否与设计规格一致?单位是否正确? 单位搞错,ns写成ps
输入延迟合理性 最大值和最小值是否合理?是否考虑了外部器件的时序? min/max设反了
输出延迟合理性 是否给外部留了足够的裕量? 裕量留太少,导致外部采样失败
驱动单元选择 驱动单元是否与实际外部器件匹配? 所有端口用同一个驱动单元
负载值准确性 负载值是否根据实际走线长度和扇出估算? 负载设太小,综合出来的驱动不够

嗯,以上就是综合约束入门最核心的内容。时钟定义是基础,输入输出延迟是边界条件,驱动负载是物理实现的关键。这三样东西写好了,综合出来的网表才有意义。不然的话,工具就是在瞎猜,流片回来能不能工作全看运气。

我个人建议,每次写完约束,先用 report_timing 跑一下,看看有没有明显的问题。别等到综合完了才发现约束写错了,那时候改起来就麻烦了。

一句话总结:约束写得好,流片没烦恼;约束写不好,debug到老。

可可精品资料库