4. 生成时钟约束:create_generated_clock、分频与倍频时钟、时钟相位关系
各位同学,今天我们来聊聊生成时钟。说实话,这个知识点在时序约束里属于「看着简单,用起来容易翻车」的类型。我自己刚入行那会儿,就因为一个分频时钟的相位关系没搞对,导致后仿出现了一堆hold违例,查了整整两天才找到根因。
生成时钟,说白了就是由主时钟通过分频、倍频或者相位调整派生出来的时钟。在数字芯片里,PLL、DLL、分频器、门控时钟,这些都是生成时钟的常见来源。SDC里用create_generated_clock来定义它们。
4.1 基本语法与核心参数
先看一个最基础的例子。假设你有一个100MHz的主时钟,经过一个二分频寄存器,得到50MHz的时钟:
create_clock -name clk_sys -period 10.0 [get_ports clk_in]
create_generated_clock -name clk_div2 \
-source [get_ports clk_in] \
-divide_by 2 \
[get_pins u_divider/Q]
这里有几个关键参数,我挨个说一下:
- -source:指定源时钟的引脚或端口。注意,不是时钟名,是物理节点。
- -divide_by / -multiply_by:分频或倍频系数。整数倍,不支持小数。
- -edges:显式指定生成时钟的边沿位置。这个参数很强大,但容易用错。
- -edge_shift:在-edges基础上做偏移,单位是ns。
- -invert:直接取反,相当于180度相移。
4.2 分频时钟的相位关系
分频时钟的相位,取决于你从哪个边沿开始计数。举个例子,一个二分频时钟,如果源时钟的上升沿触发分频器,那么生成时钟的上升沿与源时钟的上升沿对齐。但如果是下降沿触发呢?相位就差了半个周期。
嗯,这里要注意:工具默认认为生成时钟与源时钟的边沿对齐。如果你的设计不是这样,必须显式指定。
我曾经遇到过一个案例:设计里用了一个二分频器,但分频器的输出又经过了一个反相器。结果STA工具默认认为生成时钟的上升沿在0ns,实际上它在5ns(对于10ns周期的源时钟)。这导致setup分析时,工具少算了半个周期的裕量。后来我在约束里加上了-invert才搞定。
4.3 倍频时钟与PLL
倍频时钟通常由PLL产生。PLL的输出与输入之间,不仅有频率关系,还有相位关系。很多PLL支持相位可调,比如0度、45度、90度等。
create_generated_clock -name clk_200m \
-source [get_pins pll/ref_clk] \
-multiply_by 2 \
[get_pins pll/out_clk]
但PLL的输出与输入之间,往往有一个固定的相位偏移。这个偏移由PLL的锁定特性决定,可能是0,也可能是某个固定值。我建议你在约束里加上-edge_shift来补偿这个偏移,除非你确认PLL的输出与输入是严格对齐的。
4.4 多级生成时钟
有时候,生成时钟还可以作为另一个生成时钟的源。比如,先二分频,再四分频:
create_generated_clock -name clk_div2 \
-source [get_ports clk_in] -divide_by 2 [get_pins div2/Q]
create_generated_clock -name clk_div4 \
-source [get_pins div2/Q] -divide_by 2 [get_pins div4/Q]
这里有个细节:第二个生成时钟的-source是div2/Q,而不是clk_in。如果你写成了clk_in,工具会认为clk_div4直接由clk_in派生,分频系数变成4。结果虽然一样,但相位关系可能不对。你想想看,如果div2/Q的相位相对于clk_in有延迟,那么clk_div4的相位就应该基于div2/Q来计算,而不是clk_in。
4.5 时钟相位关系的可视化
为了让大家更直观地理解,我画了一张图。这张图展示了主时钟、二分频时钟(上升沿触发)、以及四分频时钟之间的相位关系。
从图上可以清楚看到,二分频时钟的周期是20ns,四分频是40ns。它们的上升沿都与主时钟的上升沿对齐——这是最理想的情况。实际设计中,由于分频器本身的延迟,这个对齐会有偏差,需要通过set_clock_latency来补偿。
4.6 实战中的常见问题
我总结几个实际项目中容易踩的坑:
- 源时钟指定错误:-source应该指向时钟树的根节点,而不是时钟的终点。如果你指向了寄存器时钟引脚,工具会报warning,但结果可能不对。
- 分频系数与边沿不匹配:比如你写-divide_by 2,但实际电路是下降沿触发分频。这时候生成时钟的相位会差半个周期,必须用-edges或-invert来修正。
- 多级生成时钟的-source链:每一级生成时钟的-source都应该指向上一级生成时钟的物理节点,而不是最顶层的主时钟。否则相位关系会乱。
- PLL输出时钟的相位偏移:很多PLL在锁定后,输出与输入之间有一个固定的相位偏移。这个偏移在约束里必须体现,否则跨时钟域分析会出错。
4.7 检查与验证
写完约束后,我建议你用以下命令检查:
report_clock -generated
report_clock_timing -type summary
check_timing -verbose
report_clock -generated会列出所有生成时钟及其源时钟、分频/倍频系数、边沿信息。仔细核对每个生成时钟的边沿时间,确保与你的设计意图一致。
check_timing -verbose会报告所有未约束的路径、悬空的时钟、以及可能的约束冲突。我每次跑完约束都会先跑这个命令,确保没有遗漏。
好了,关于生成时钟的约束,核心就是这些。记住:频率可以算,相位必须量。下一节我们聊聊如何约束异步时钟域,那又是一个容易翻车的地方。
可可精品资料库