3、时序约束核心文件:SDC文件语法入门
说到时序约束,就绕不开SDC文件。这玩意儿,说白了就是告诉工具你的设计到底想跑多快、哪些路径要特别关照。我刚开始接触SDC时,看着那一堆命令也挺懵的。后来发现,其实核心就三个东西:创建时钟、生成时钟、虚拟时钟。搞懂了这三个,基本就掌握了SDC的精髓。
3.1 SDC文件是什么?
SDC全称是Synopsys Design Constraints,是业界标准的时序约束格式。不管是Vivado、Quartus还是其他工具,都认这个格式。我个人习惯把SDC看作是「设计的说明书」——你告诉工具你的时钟长什么样,工具才能帮你分析时序。
嗯,这里要注意:SDC文件不是Verilog代码,它是一组Tcl命令。所以语法上跟Tcl脚本很像,每条命令后面要加分号。
3.2 创建时钟:create_clock
创建时钟是最基本的操作。没有时钟,时序分析根本没法做。命令格式很简单:
create_clock -name clk_sys -period 10.000 [get_ports clk_in]
这条命令的意思是:创建一个叫clk_sys的时钟,周期10ns(也就是100MHz),端口是clk_in。
我在项目中遇到过一个问题:有人把时钟创建在了一个内部节点上,而不是端口上。结果时序分析出来的结果完全不对。记住,时钟一定要创建在顶层端口或者PLL的输出上。
关键参数说明:
-name:时钟名称,建议用有意义的命名-period:时钟周期,单位ns[get_ports]:指定时钟源端口-waveform:可指定占空比,默认50%
举个例子,如果你要创建一个占空比不是50%的时钟:
create_clock -name clk_ddr -period 5.000 -waveform {0 2.500} [get_ports ddr_clk_p]
这里-waveform后面的两个值,分别是上升沿和下降沿的时间点。0ns上升,2.5ns下降,周期5ns,占空比就是50%。
3.3 生成时钟:create_generated_clock
生成时钟,说白了就是从已有的时钟派生出来的时钟。比如你用PLL倍频、分频,或者用寄存器做了个时钟分频器,这时候就要用create_generated_clock。
为什么要区分创建时钟和生成时钟?因为工具需要知道时钟之间的相位关系。你想想看,如果两个时钟是相关的,工具就能做跨时钟域的时序分析。如果不相关,工具就会把它们当成异步时钟处理。
create_generated_clock -name clk_50m -source [get_ports clk_in] -divide_by 2 [get_pins pll_inst/clk_out]
这条命令的意思是:从clk_in这个源时钟派生出一个叫clk_50m的时钟,频率除以2,输出引脚是pll_inst/clk_out。
我的经验:生成时钟的-source一定要指定到源时钟的端口或引脚,而不是随便写个名字。我曾经因为-source写成了时钟名称而不是端口,导致工具找不到源时钟,时序分析直接报错。
常用的参数还有:
-multiply_by:倍频系数-divide_by:分频系数-edges:直接指定边沿关系-invert:反相
比如一个2倍频的时钟:
create_generated_clock -name clk_200m -source [get_ports clk_in] -multiply_by 2 [get_pins pll_inst/clk_out]
3.4 虚拟时钟:虚拟时钟
虚拟时钟这个名字听起来挺玄乎的,其实它就是一个「不存在于设计中的时钟」。什么意思呢?就是你在SDC里定义了一个时钟,但这个时钟没有连到任何端口或引脚上。
为什么要用虚拟时钟?我举个例子你就明白了。假设你的FPGA要跟外部的一个芯片通信,外部芯片有自己的时钟。这个时钟不在你的FPGA内部,但你要约束输入输出延时,就得有个参考时钟。这时候虚拟时钟就派上用场了。
create_clock -name clk_virtual -period 8.000
注意看,这条命令后面没有[get_ports]或者[get_pins]。这就是虚拟时钟和普通时钟的区别——它没有物理连接。
避坑指南:我曾经犯过一个错误,把虚拟时钟的周期设得跟实际时钟不一样。结果输入输出延时约束全偏了,时序分析出来的结果完全不能用。虚拟时钟的周期一定要跟外部器件的实际时钟一致。
虚拟时钟的典型应用场景:
- 外部器件输入数据到FPGA,需要参考外部时钟
- FPGA输出数据到外部器件,需要参考外部时钟
- 多芯片系统中的接口时序约束
3.5 三种时钟的对比
| 时钟类型 | 命令 | 物理连接 | 典型用途 |
|---|---|---|---|
| 创建时钟 | create_clock | 有(端口) | 板级时钟输入 |
| 生成时钟 | create_generated_clock | 有(内部节点) | PLL输出、分频时钟 |
| 虚拟时钟 | create_clock(无端口) | 无 | 外部接口时序参考 |
3.6 知识体系结构图
下面这张图,是我自己总结的SDC时钟约束的核心逻辑。你看一遍就能明白三种时钟之间的关系:
3.7 实战中的注意事项
说了这么多,最后分享几个我在实际项目中踩过的坑:
- 时钟命名要规范:别用clk1、clk2这种名字。我习惯用clk_频率_用途,比如clk_100m_ddr。这样看约束文件时一目了然。
- 生成时钟的-source要写对:这个前面提过,但值得再说一遍。source必须是物理节点,不是时钟名称。
- 虚拟时钟别忘了加:很多人做输入输出延时约束时,忘了定义虚拟时钟。结果工具报错说找不到参考时钟。
- 检查时钟是否传播:约束写完后,用report_clock命令检查一下时钟有没有正确传播到所有时序路径上。
小技巧:写完SDC文件后,先用check_timing命令检查一下。这个命令会告诉你哪些路径没有约束、哪些时钟定义有问题。我每次写完约束都会跑一遍这个命令,能省不少调试时间。
好了,关于SDC文件中的三种时钟,就讲到这里。说白了,创建时钟是根基,生成时钟是衍生,虚拟时钟是外挂。搞清楚了这三者的区别和用法,时序约束就算入门了。
公众号:蓝海资料掘金营,微信deep3321