前情提要:前面 16 篇文章覆盖了 SDC 全部命令、UPF 入门、CTS、MMMC、SPEF、库文件等。 但有一个问题一直没回答:所有的约束写完之后,怎么确认它们是对的?参数从哪来?报告怎么看? 本篇回答这三个问题。带上你的虚拟项目 digital_core 一起走一遍。


一、什么是约束驱动的 Sign-off

传统 sign-off 流程的致命问题是:约束问题留到最后才发现。你布完线跑 STA,发现 slack 差 200ps。改什么?改约束还是改 floorplan?不知道,因为你从来没验证过约束本身。

约束驱动的 Sign-off 把约束验证提前到第一步:

约束写完后 → 约束完整性检查(check_timing + 人工判读)→ 综合 → CTS → SPEF → STA → 约束回归 → sign-off
                 ↑ 本篇重点


二、report_clock:时钟报告怎么看

2.1 正常输出长什么样

pt_shell> report_clock

  Clock         Period   Waveform            Sources
  ──────────────────────────────────────────────────────────
  clk_2M5       400.0    {0 200}             clk_in
  spi_clk       1000.0   {0 500}             spi_sclk
  clk_1M25      800.0    {0 400}             clk_divider/Q
  scan_clk      100.0    {0 50}              test_clk

逐行判读:

  • clk_2M5 period=400ns ← 2.5MHz ✅

  • spi_clk period=1000ns ← 1MHz ✅

  • clk_1M25 period=800ns ← 2.5MHz÷2=1.25MHz ✅

  • scan_clk period=100ns ← 10MHz ✅

错误信号(一眼能看出问题):

# ❌ 错误示例
  Clock         Period   Waveform            Sources
  ──────────────────────────────────────────────────────────
  clk_2M5       400.0    {0 200}             clk_in
  clk_1M25      400.0    {0 200}             clk_divider/Q   ← period=400 和 master 一样,没分频!

clk_1M25 的 period 应该是 800(÷2),但它显示 400 和 master 一样。原因可能是 -divide_by 2 漏写了,或者 -master_clock 指向了错误的 source。

你的项目的正确值怎么来?

时钟 频率 来源 如何确定
clk_2M5 2.5MHz 外部 pin clk_in 芯片 spec:系统时钟 2.5MHz
spi_clk 1MHz 外部 pin spi_sclk SPI 从模式,外部 master 决定,设 1MHz 为最差情形
clk_1M25 1.25MHz clk_divider/Q(÷2) 数字逻辑内部生成,从 rtl 看分频比
scan_clk 10MHz 外部 test_clk DFT spec:scan 频率 10MHz

关键: 这些数字不是你拍脑袋定的。它们来自项目 spec(system clock)、接口协议(SPI 速率)、RTL 代码(分频比)、DFT plan(scan 频率)。

2.2 常见异常及根因

report_clock 输出 问题 根因
period=0 clock 未正确传播 -period 没写或 syntax error
某时钟没出现 clock 定义无效 create_clock 的 port/pin 名字写错
gen_clk 和 master 的 period 一样 分频没生效 -divide_by 漏了或 -master_clock 指向错误
多出意料之外的 clock clock 被意外 propagated 没设 set_clock_sense -stop_propagation

三、check_timing:约束完整性报告解读

3.1 正常输出

pt_shell> check_timing

  Information: (PT-001) The following design/analysis information:
    No constraining statements exist for the following:
      (none)                                                    ← 没有未约束内容
    No timing generator statements exist for the following:
      (none)                                                    ← 所有 clock 都定义了
    Warning: : some pins have no clock. (PT-016)                ← 这个警告要仔细看

那个 PT-016 是什么?

pt_shell> report_clock -pins

  Pin                    Clock
  ──────────────────────────────
  clk_in                 clk_2M5
  clk_divider/Q          clk_1M25
  scan_mux/Z             (no clock)                             ← 这里有 clock 没传播到

判读: scan_mux/Z 没有 clock。在功能模式下,scan_mux/Z 应该是 clk_2M5 的一个分支。如果这里没 clock,所有经过 scan_mux 的路径都不会被分析。但你的设计中 scan_mux 在功能模式下应当传播 clk_2M5

修复:

# 让工具知道 clock 可以通过 scan_mux
set_clock_sense -stop_propagation [get_pins scan_mux/SEL]  # 只切断 SEL pin 的传播
# 这样 clock 仍然可以从 A/B 输入传送到 Z 输出

3.2 错误输出的判读

# ❌ 错误示例
  No constraining statements exist for the following:
    Ports: int_out                                                  ← IO 约束漏了
    Pins:  u_charge_ctrl/pwm_out_reg/D                              ← 这个 FF 没被任何 clock 覆盖

  Warning: (PT-016) Some pins have no clock.
    255 pins have no clock.                                         ← 255 个 pin 没时钟,问题严重

分析:

报告项 可能原因 影响 修复
Ports: int_out 忘写 set_output_delay 该端口相关路径不分析 补上 output_delay
Pins: u_charge_ctrl/pwm_out_reg/D 该 FF 所在时钟域没定义时钟 该路径不分析 检查 pwm 模块的时钟定义
255 pins have no clock set_clock_sense 设错或 gen_clk 没定义 大量路径遗漏,sign-off 无效 report_clock -pins 找出哪些 pin 没时钟

3.3 coverage 报告怎么看

pt_shell> report_analysis_coverage

  Type of Check        Total  Met  Violated  Untested  Coverage%
  ──────────────────────────────────────────────────────────────
  setup                3353   3353   0        0         100.0
  hold                 3353   3353   0        0         100.0
  recovery             100    100    0        0         100.0
  removal              100    100    0        0         100.0
  ──────────────────────────────────────────────────────────────
  Overall              6906   6906   0        0         100.0

逐行判读:

  • setup 3353 = 3353 条 setup path ✅

  • setup Coverage 100% = 所有寄存器都被覆盖 ✅

  • Untested 0 = 没有未测试路径 ✅

❌ 错误示例:

  Type of Check        Total  Met  Violated  Untested  Coverage%
  ──────────────────────────────────────────────────────────────
  setup                2900   2890  10        0         99.7
  hold                 2800   2800  0         553       83.5      ← 553 条 hold path 没测!

hold Untested=553 意味着: 553 条路径没有做 hold check。通常是因为 clock 关系没定义好——工具不知道这些路径的 launch 和 capture 是什么关系。

检查方法:

# 找出哪些路径是 untested
report_timing -untested -max_paths 10

# 输出示例:
  Path 1: launch clock = clk_2M5, capture clock = <none>
  → 这个 FF 的 capture clock 没定义

四、report_timing:路径延迟报告深度解析

4.1 正常输出逐段判读

pt_shell> report_timing -from [get_ports spi_mosi] -to [get_pins spi_reg/D]

  Startpoint: spi_mosi (input port)
  Endpoint:   spi_reg/D (rising edge triggered flip-flop)
  Path Group: SPI_input
  Path Type:  max (setup)

    Point                                                   Incr     Path
    ────────────────────────────────────────────────────────────────────────────
    input external delay                                    0.300    0.300 r
    spi_mosi (in)                                            0.100    0.400 r
    U1/BUF (BUF_X2)                                          0.080    0.480 r
    U2/NAND (NAND_X1)                                        0.120    0.600 f
    U3/INV (INV_X1)                                          0.050    0.650 r
    spi_reg/D (DFF_X1)                                       0.000    0.650 r
                                                                      ────────
    Library setup time                                       -0.050
    Data required time (clock edge + uncertainty)                      0.800
    ────────────────────────────────────────────────────────────────────────────
    Slack (MET)                                                      0.150

每列含义:

  • Point — 路径经过的 cell/net + pin 名 ← 每个 cell 的延迟都列在这里

  • Incr — 本段增加的延迟 ← 找瓶颈看这里

  • Path — 累计延迟 ← 看总延迟看最后一行的 Path

  • r/f — rise/fall transition

判读步骤:

① 总 path delay = 0.650ns
② data required time = 0.800ns(一个 clock period 400ns - uncertainty 0.5ns + clock path delay... 
   等等,400ns 是因为 `set_clock_uncertainty -setup 0.5` 把 capture edge 从 400ns 提前到了 399.5ns?
   不是,这是 IO 路径,用的是 spi_clk period 1000ns)
   
实际上这里:
   clock period = 1000ns (spi_clk)
   uncertainty = 1.5ns (set_clock_uncertainty -setup 1.5 [get_clocks spi_clk])
   clock path delay ≈ 0.3ns
   
所以 data required time ≠ 1000-1.5+0.3 ≈ 998.8... 
但在 report 中 data required time = 0.800 是相对于 data arrival time 的...

实际上 report_timing 中的 Time 值是经过简化的。你需要理解原理而不是死记数字。

setup 公式(简化):

slack = T_capture + T_capture_clock_delay - T_launch - T_launch_clock_delay - T_data - T_setup - uncertainty

重点看 Incr 列:

  • 如果某个 cell 的 Incr 突然变大(比如从 0.05 跳到 0.30)→ 瓶颈在这里

  • 如果是 IO 路径:0.3 的 input_delay 占了路径的一半 → 可以考虑收紧 input_delay

4.2 ❌ slack 失败时的分析

  Slack (VIOLATED)                                               -0.250

分析步骤:

1. 看哪个 cell 的 Incr 最大 → 瓶颈 cell
2. 看这个 cell 的 input_slew 和 output_load 是不是合理
3. 如果 input_slew 过大 → 前面的 cell 驱动不够,换大 cell
4. 如果 output_load 过大 → 扇出太多或走线太长,插入 buffer
5. 如果 cell 本身 delay 大 → 换 LVT(但你的库没有 LVT)或优化 floorplan

4.3 parameter 从哪来

参数 来源 例子
clock period 项目 spec / 协议 SPI=1MHz → period=1000ns
input_delay 上游芯片 datasheet SPI master 输出 delay max=200ns
output_delay 下游芯片 datasheet ADC 采样 setup time=300ns
wire_load foundry 的 WLM 表 smic018_wl10
operating_conditions foundry 的 .lib wc_0_18v_125c

实操方法:

用 SPI 接口举例:

上游芯片 datasheet 说:
  t_CO_clk (clock to data out) = 150ns max
  t_hold_data = 50ns
  t_setup_data = 80ns

你的 input_delay = t_CO_clk = 150ns  max
                  t_CO_clk = 50ns   min

五、check_power_domain:UPF 约束报告解读

5.1 正常输出

pt_shell> check_power_domain

  Information: Checked 2 power domains
    PD_TOP — always-on domain. 125 cells.
    PD_SW  — switchable domain. 250 cells.
  Information: Isolation strategy: clamp output low (ISO_CLAMP_LOW)
  Information: Level shifters found: 5 (3 from PD_TOP→PD_SW, 2 from PD_SW→PD_TOP)
  Information: Retention registers: 50 (in PD_SW domain)
  Information: (no errors)

5.2 ❌ 错误示例

  Error: PD_SW has 25 cells without retention behavior.
  Warning: No isolation cell found on signal adc_data[7:0] crossing from PD_SW to PD_TOP.
  Error: Level shifter required but not found on path from PD_TOP (1.8V) to PD_SW (1.8V).

判读:

  • 25 cells without retention → 这些寄存器在 PD_SW 关断时会丢数据 → 要么加 retention,要么改 UPF 定义

  • No isolation cell → 该信号跨域时没 clamp → PD_SW 关断时 PD_TOP 会收到不定态

  • Level shifter required but same voltage → 两侧都是 1.8V,不需要 LS。可能是 UPF 设错了 supply net


六、跨工艺节点的典型参数对比:180nm / 130nm / 12nm

同一个约束参数,在不同工艺节点下有完全不同的量级和分配逻辑。以下以你的虚拟项目为基础,给出三个节点的典型值。

6.1 工艺节点 × 频率组合

标准 中端 高端
工艺 Smic 180nm Smic 130nm 先进 12nm
典型时钟 1MHz 2MHz 400MHz
周期 1000ns 500ns 2.5ns
应用 PMIC、充电管理 MCU、BLE 应用处理器、AI

6.2 set_clock_uncertainty 的分配对比

# ===== 180nm @ 1MHz (周期 1000ns) =====
# 0.5ns 的 uncertainty 对 1000ns 周期影响 = 0.05%
# → 几乎无影响
set_clock_uncertainty -setup 0.5 [get_clocks clk_1M]
set_clock_uncertainty -hold 0.2 [get_clocks clk_1M]
# 分配:
#   Clock tree skew     0.30ns   ← CTS 后 real skew ≈ 0.25~0.35ns
#   PLL jitter          0.10ns   ← 180nm PLL period jitter ≈ 100~200ps
#   Margin              0.10ns
# ===== 130nm @ 2MHz (周期 500ns) =====
# 0.8ns 的 uncertainty 对 500ns 周期影响 = 0.16%
# → 影响仍然很小
set_clock_uncertainty -setup 0.8 [get_clocks clk_2M]
set_clock_uncertainty -hold 0.3 [get_clocks clk_2M]
# 分配:
#   Clock tree skew     0.50ns   ← 130nm 工艺波动更大,skew 更大
#   PLL jitter          0.15ns
#   Margin              0.15ns
# ===== 12nm @ 400MHz (周期 2.5ns) =====
# 0.15ns 的 uncertainty 对 2.5ns 周期影响 = 6%
# → uncertainty 占据了周期间的显著比例,必须精确控制
set_clock_uncertainty -setup 0.15 [get_clocks clk_400M]
set_clock_uncertainty -hold 0.05 [get_clocks clk_400M]
# 分配:
#   Clock tree skew     0.08ns   ← 先进的 CTS 算法 + H-tree,skew 控制好
#   PLL jitter          0.03ns   ← 先进 PLL jitter 更小
#   Margin              0.04ns   ← 余量很紧
#
#   注意:12nm 下 uncertainty 占总周期的 6%
#   如果设成 0.3ns(像 180nm 那种比例),你的 setup 不可能过
参数 180nm @ 1MHz 130nm @ 2MHz 12nm @ 400MHz
setup_uncertainty 0.5ns 0.8ns 0.15ns
占周期比例 0.05% 0.16% 6%
skew 主导因素 线长差异 线长+工艺波动 OCV + 局部工艺波动
margin 宽松度 余量宽裕 适中 必须精确控制

6.3 IO delay 的对比

# ===== 180nm @ 1MHz SPI =====
# 上游 SPI master datasheet: t_co = 5~200ns
# 200ns 对 1000ns 周期 = 20%
set_input_delay -clock spi_clk -max 200 [get_ports {spi_mosi}]
set_input_delay -clock spi_clk -min   5 [get_ports {spi_mosi}]
# ===== 130nm @ 2MHz SPI =====
# 上游 SPI master: t_co = 3~100ns
# 100ns 对 500ns 周期 = 20%
set_input_delay -clock spi_clk -max 100 [get_ports {spi_mosi}]
set_input_delay -clock spi_clk -min   3 [get_ports {spi_mosi}]
# ===== 12nm @ 400MHz DDR =====
# DDR PHY: t_co = 0.1~0.5ns
# 0.5ns 对 2.5ns 周期 = 20%
# 比例和 180nm 一样——IO delay 通常占周期的 15~25%
set_input_delay -clock ddr_clk -max 0.5 [get_ports {dq[*]}]
set_input_delay -clock ddr_clk -min 0.1 [get_ports {dq[*]}]
参数 180nm SPI 130nm SPI 12nm DDR
input_delay max 200ns 100ns 0.5ns
占周期比例 20% 20% 20%
上游器件 通用 SPI master 通用 SPI master DDR PHY
是否瓶颈 ❌ 不是(周期大) ❌ 不是 ⚠️ 可能是

6.4 set_load 的对比

# 180nm: PCB 走线 + 下游芯片输入 = 3pF + 2pF = 5pF
set_load 5.0 [get_ports spi_miso]

# 130nm: PCB 走线 + 下游芯片输入 = 2pF + 1.5pF = 3.5pF
set_load 3.5 [get_ports spi_miso]

# 12nm: 片上负载为主,片外走线极短 = 0.1pF + 0.05pF = 0.15pF
set_load 0.15 [get_ports dq_io]

6.5 timing derate 的对比

# ===== 180nm @ 1MHz =====
# OCV 影响小,全局 derate 足够
set_timing_derate -early 0.95
set_timing_derate -late  1.10

# ===== 130nm @ 2MHz =====
# 工艺波动更大,derate 范围略宽
set_timing_derate -early 0.93
set_timing_derate -late  1.12

# ===== 12nm @ 400MHz =====
# OCV 严重,必须用 AOCV table 而非全局 derate
# 假设 AOCV table 中 5 级 cell: early=0.90, late=1.15
# 假设 AOCV table 中 20 级 cell: early=0.95, late=1.08
# → 直接用 report_timing_derate 验证当前路径的 derate 值

6.6 WLPM 精度的对比

工艺 pre→post slack 差异 原因
180nm 10~20% 线延迟占比较小,WLPM 误差影响不大
130nm 20~30% 线延迟占比增大,WLPM 误差放大
12nm 30~60% 线延迟占 path delay 的 50%+,耦合效应显著

12nm 为什么差异大:

180nm: cell_delay : wire_delay ≈ 80% : 20%
        WLPM 误差 40% → 整体误差 = 20% × 40% = 8%

12nm:  cell_delay : wire_delay ≈ 40% : 60%
        WLPM 误差 40% → 整体误差 = 60% × 40% = 24%

再加上耦合电容占 wire_cap 的 50%+
综合时不知道耦合 → wire_delay 低估更多

6.7 一句话总结

工艺 约束设计的核心理念
180nm @ MHz 周期大,约束宽松——uncertainty 0.5ns 不影响 setup。重点在 IO 时序和 reset
130nm @ MHz 周期适中,约束要开始关注——skew 和 derate 开始占周期比例
12nm @ GHz 周期极小,每个 ps 都要精打细算——必须用 AOCV,必须用 CCS,必须精确提取

七、参数值从哪来:完整溯源(工艺无关部分)

7.1 uncertainty 的分配公式

# 你一直写的:
set_clock_uncertainty -setup 0.5 [get_clocks clk_2M5]

# 但 0.5 怎么来的?
# uncertainty = clock_skew + PLL_jitter + margin

# clock_skew:   post-CTS report 显示 max skew = 0.28ns
# PLL_jitter:   datasheet 说 period jitter = 150ps pk-pk
# margin:       安全余量 = 0.07ns

# 所以:0.28 + 0.15 + 0.07 = 0.50ns

7.2 input_delay 的来源(以 SPI 为例)

上游芯片(SPI master)datasheet:
  CPOL=0, CPHA=0 模式
  t_clk = 1000ns (1MHz)
  t_co (clock to data out) = 5ns ~ 200ns
  t_hold (data hold after clock) = 10ns

所以你的 input_delay:
  max = t_co_max = 200ns   ← 数据在 clock edge 后最晚 200ns 到达
  min = t_co_min = 5ns      ← 数据最早在 clock edge 后 5ns 到达

= set_input_delay -clock spi_clk -max 200 [get_ports {spi_mosi spi_csn}]
  set_input_delay -clock spi_clk -min   5 [get_ports {spi_mosi spi_csn}]

7.3 set_load 的来源

SPI 输出 pin 接到 PCB 走线 + 下游芯片输入 pin
负载 = PCB 走线电容 + 下游芯片输入电容
     ≈ 3pF  + 2pF = 5pF

= set_load 5.0 [get_ports spi_miso]

7.4 derating 的来源

foundry 的 AOCV table 文件示例(smic018_aocv.tbl):
  Depth  Early  Late
  1      0.85   1.20
  2      0.88   1.17
  5      0.92   1.12
  10     0.94   1.08
  20     0.96   1.05

你设的:
  set_timing_derate -early 0.95
  set_timing_derate -late  1.10

这是"全局 derating",对所有路径统一应用。
AOCV table 是根据路径深度查表,更精确。

八、完整的 Sign-off 检查清单


九、总结

三个核心原则:

  1. 不要只看命令有没有跑过,要看输出——report_clock 的 period 对不对、check_timing 有没有列 port、coverage 是不是 100%

  2. 参数值不是拍脑袋的——uncertainty 来自 CTS skew + PLL jitter,IO delay 来自上下游 datasheet,derating 来自 foundry 的 AOCV table

  3. 错误输出一眼能看出来——period=0、coverage<95%、untested>0、slack VIOLATED

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐