第二章:约束文件入门——SDC文件结构、时钟约束与IO约束

大家好,我是你们的时序约束讲师。今天咱们聊聊约束文件的基础——SDC。说实话,我见过不少工程师,写RTL代码一把好手,但一碰到SDC就头大。其实这东西没那么玄乎,说白了就是告诉工具:你的芯片想跑多快、数据什么时候来、什么时候走。

我个人习惯把SDC文件当成芯片的「交通规则」。没有它,综合工具和STA工具就像没交警的路口——乱成一锅粥。好,咱们直接进入正题。

2.1 SDC文件结构:别把它当黑盒子

SDC(Synopsys Design Constraints)是行业标准格式。你想想看,不管是Synopsys的PrimeTime,还是Cadence的Tempus,甚至开源工具,都认这个格式。为什么?因为它简单、清晰。

一个典型的SDC文件,结构大致是这样的:

# 时钟定义
create_clock -name clk_sys -period 10 [get_ports clk]
create_generated_clock -name clk_div2 -source [get_ports clk] -divide_by 2 [get_pins u_div/clk_out]

# IO约束
set_input_delay -clock clk_sys -max 5 [get_ports data_in]
set_output_delay -clock clk_sys -min 2 [get_ports data_out]

# 时序例外(后面章节会细讲)
set_false_path -from [get_clocks clk_sys] -to [get_clocks clk_div2]

嗯,这里要注意:SDC是顺序执行的。后面的约束可能会覆盖前面的。我在项目中遇到过有人把set_input_delay写在create_clock前面,结果工具报了一堆warning——因为时钟还没定义呢。

小技巧:我建议你在SDC文件开头加个注释块,写明这个文件适用于哪个模块、哪个工艺角、哪个版本。别笑,我吃过亏——有一次拿错SDC版本,流片回来时序全崩了。

2.2 create_clock:一切时序的起点

时钟是时序分析的「心跳」。没有时钟,STA根本没法干活。create_clock就是告诉工具:嘿,这里有个时钟,周期多少,从哪个端口进来。

基本语法:

create_clock -name <时钟名> -period <周期(ns)> [get_ports <端口名>]

举个例子:

create_clock -name clk_100m -period 10 [get_ports clk_in]

这表示:定义一个叫clk_100m的时钟,周期10ns(也就是100MHz),从端口clk_in进入芯片。

你可能会问:为什么还要给时钟起名字?直接用端口名不行吗?说实话,在简单设计中确实可以。但一旦涉及多时钟域、时钟复用,名字就很重要了。我习惯用「时钟名_频率」的命名方式,比如clk_100m、clk_50m,一目了然。

注意:create_clock只能定义在端口(port)上,不能定义在内部节点(pin)上。如果你想把内部节点当时钟源,需要用create_generated_clock。我曾经看到有人直接在寄存器输出端create_clock,结果工具报错——这是新手常犯的错误。

2.3 create_generated_clock:派生时钟的「身份证」

实际芯片中,很少有只有一个时钟的情况。PLL分频、门控时钟、倍频……这些派生时钟怎么定义?用create_generated_clock。

基本语法:

create_generated_clock -name <时钟名> -source <源时钟> -divide_by <分频系数> [get_pins <输出引脚>]

举个例子:

create_generated_clock -name clk_50m -source [get_ports clk_in] -divide_by 2 [get_pins u_div/clk_out]

这表示:从clk_in输入100MHz时钟,经过分频器u_div,在clk_out引脚上输出50MHz时钟,命名为clk_50m。

这里有个关键点:-source指定的是源时钟的端口或引脚,而不是源时钟的名字。我刚开始做的时候也搞混过,后来养成了习惯——先看原理图,找到时钟路径,再写约束。

除了-divide_by,还有-multiply_by(倍频)、-edges(边沿指定)等选项。比如:

# 2倍频
create_generated_clock -name clk_200m -source [get_ports clk_in] -multiply_by 2 [get_pins u_pll/clk_out]

# 指定上升沿和下降沿(用于DDR等)
create_generated_clock -name clk_ddr -source [get_ports clk_in] -edges {1 3 5} [get_pins u_ddr/clk_out]
核心要点:create_generated_clock的本质是告诉工具:这个时钟和源时钟是什么关系。工具会根据源时钟的波形,自动推导出派生时钟的波形。所以源时钟必须已经定义好了。

2.4 set_input_delay / set_output_delay:芯片与外界的「握手协议」

芯片不是孤岛。它要和外部器件通信——比如FPGA、DDR颗粒、ADC/DAC。外部器件的数据什么时候来?芯片的数据什么时候该出去?这就是set_input_delay和set_output_delay要干的活。

2.4.1 set_input_delay:数据到达芯片的时间

基本语法:

set_input_delay -clock <时钟名> -max <最大延迟(ns)> -min <最小延迟(ns)> [get_ports <输入端口>]

举个例子:

set_input_delay -clock clk_100m -max 5 -min 2 [get_ports data_in]

这表示:数据data_in相对于时钟clk_100m的上升沿,最大延迟5ns,最小延迟2ns。换句话说,数据在时钟沿之后2ns到5ns之间到达芯片引脚。

你可能会问:这个值怎么来的?答案是——看外部器件的datasheet。比如外部器件是FPGA,它输出数据相对于时钟的延迟是Tco(Clock-to-Output),再加上PCB走线延迟,就是input delay。

避坑指南:我曾经在项目中把input delay设得太乐观(比如设了1ns,实际是3ns),结果后仿真发现setup violation。后来学乖了——留20%的裕量。比如datasheet写最大2.5ns,我就设3ns。

2.4.2 set_output_delay:芯片数据被外部采样的时间

基本语法:

set_output_delay -clock <时钟名> -max <最大延迟(ns)> -min <最小延迟(ns)> [get_ports <输出端口>]

举个例子:

set_output_delay -clock clk_100m -max 4 -min 1 [get_ports data_out]

这表示:外部器件在时钟沿之后1ns到4ns之间需要采样到数据。也就是说,芯片内部的数据必须在时钟沿之前(setup)和之后(hold)满足这个窗口。

注意:output delay是「外部器件的需求」,不是「芯片内部的实际延迟」。工具会根据这个需求,反推出芯片内部需要满足的时序。

2.5 知识体系总览

说了这么多,咱们用一张图来总结本章的核心逻辑:

SDC约束文件知识体系 SDC约束文件 时钟约束 IO约束 时序例外(后续) create_clock create_generated_clock set_input_delay set_output_delay 关键参数 -period(周期) -name(时钟名) -divide_by / -multiply_by -source(源时钟) -max / -min(最大/最小延迟) -clock(参考时钟) -rise / -fall(边沿) -add_delay(叠加) 核心思想:约束是「声明」不是「命令」——告诉工具外部环境是什么,而不是让工具去猜

2.6 本章小结

好,咱们捋一捋今天的内容:

  • SDC文件结构:顺序执行,时钟定义在前,IO约束在后。注释写清楚版本和用途。
  • create_clock:定义主时钟,只能用在端口上。周期、名字、端口三要素。
  • create_generated_clock:定义派生时钟,指定源时钟和分频/倍频关系。
  • set_input_delay:告诉工具数据从外部到达芯片的时间窗口。
  • set_output_delay:告诉工具外部器件需要什么时间窗口来采样数据。

说实话,这些基础约束看起来简单,但实际项目中80%的时序问题都出在这里。我见过有人把input delay和output delay搞反了,结果综合出来的电路根本不能用。所以我的建议是:每写一条约束,都问自己一句——「这个值合理吗?有没有datasheet支撑?」

下一章咱们会深入时序例外的世界——set_false_path、set_multicycle_path这些「大杀器」。不过在那之前,先把今天的基础打牢。记住:基础不牢,地动山摇。

课后小作业:打开你手头的一个SDC文件,数一数里面有多少条create_clock和create_generated_clock。然后对照原理图,看看每个派生时钟的源时钟对不对得上。我保证,你至少能发现一个不一致的地方——这是我在多个项目中的经验。
可可精品资料库