3. Libero SoC时序约束体系
在 PolarFire FPGA 的时序收敛流程中,Libero SoC 的约束体系以 Synopsys Design Constraints(SDC)为核心载体。综合、布局布线、时序验证三个阶段都读取同一套 SDC,但各阶段对约束的解析能力和依赖程度不同:综合阶段主要用时钟与输入输出延迟来驱动逻辑优化,布局布线阶段依赖时钟、例外和路径分组来指导物理实现,时序验证阶段则用完整约束生成报告。理解 SDC 的结构与优先级,是判断“约束是否真正生效”的前提。
SDC 文件的组织与加载顺序
Libero SoC 工程中通常存在多个 SDC 来源:由 SmartDesign 或 IP 自动生成的派生约束、用户手写的顶层约束、以及从其他工具迁移的约束文件。它们在工程中的加载顺序决定了同名约束的覆盖关系。一般建议把工程拆成三个文件管理:base_clocks.sdc 定义主时钟与生成时钟,io_timing.sdc 定义输入输出延迟,exceptions.sdc 定义时序例外与路径分组。这样在调试时可以通过注释掉某个文件快速定位问题来源。
SDC 命令按顺序执行,后执行的命令会覆盖先执行的同类命令。例如先写 create_clock 再写 create_generated_clock,后者会在指定源时钟基础上派生新时钟;而如果对同一对象重复 set_input_delay,只有最后一条生效。因此文件内的命令顺序本身就是一种优先级表达。
时钟定义:从主时钟到生成时钟
时钟是 SDC 的根约束。PolarFire 器件中,外部晶振或时钟源进入器件后,可能经过 CCC(Clock Conditioning Circuit)分频、倍频或相移,再驱动全局时钟网络。约束时需要先定义输入端口上的主时钟,再定义 CCC 输出或寄存器分频产生的生成时钟。
create_clock -name sys_clk -period 10.000 -waveform {0 5} [get_ports {REFCLK}]
create_generated_clock -name clk_100m -source [get_pins {CCC_0/CLKIN}] \
-divide_by 1 -multiply_by 2 [get_pins {CCC_0/GL0}]
create_clock -name virt_clk -period 6.667
set_clock_groups -asynchronous -group {sys_clk} -group {virt_clk}
示例中 sys_clk 周期 10 ns,对应 100 MHz;clk_100m 由 CCC 倍频得到,周期 5 ns。若两个时钟域之间没有确定的相位关系,应使用 set_clock_groups -asynchronous 声明异步,否则工具会按最坏情况建立保持关系分析,导致大量虚假违例。需要注意的是,create_generated_clock 的 -source 必须指向真实存在的源引脚或端口,否则约束会被忽略。
输入输出延迟:把外部世界纳入分析
FPGA 内部时序分析只覆盖器件边界以内的路径,边界以外的 PCB 走线、外部器件建立保持时间必须通过 set_input_delay 和 set_output_delay 建模。这两个命令描述的是“数据到达 FPGA 端口相对于时钟沿的时间”,而不是走线本身的延迟。
set_input_delay -clock sys_clk -max 3.0 [get_ports {data_in[*]}]
set_input_delay -clock sys_clk -min 1.0 [get_ports {data_in[*]}]
set_output_delay -clock sys_clk -max 2.5 [get_ports {data_out[*]}]
set_output_delay -clock sys_clk -min 0.5 [get_ports {data_out[*]}]
以输入为例,-max 3.0 表示外部器件在最坏情况下,数据在时钟沿之后 3 ns 才稳定到达 FPGA 引脚,工具据此计算输入建立余量;-min 1.0 表示最快情况下数据在时钟沿之后 1 ns 到达,用于保持检查。输出延迟同理,-max 对应外部器件的建立要求,-min 对应保持要求。若只写 -max 不写 -min,保持分析会缺少边界条件,报告可能偏乐观。
set_input_delay -clock_fall 或虚拟时钟使用。时序例外与路径分组
时序例外用于告诉工具“哪些路径不需要按默认规则检查”。常用的有 set_false_path、set_max_delay、set_min_delay 和 set_multicycle_path。误用例外是时序收敛中最隐蔽的风险:它不会报错,但会让真实违例从报告中消失。
set_false_path -from [get_clocks {sys_clk}] -to [get_clocks {virt_clk}]
set_multicycle_path 2 -setup -from [get_pins {reg_a[*]/Q}] -to [get_pins {reg_b[*]/D}]
set_max_delay 4.0 -from [get_ports {async_in}] -to [get_pins {sync_ff/D}]
路径分组则通过 set_path_groups 或 group_path 把相关路径归入同一组,便于报告分类和优先级控制。例如把跨时钟域路径单独分组,可以在报告中快速区分“真实需要修复的路径”和“已声明异步的路径”。
约束优先级与生效判断
Libero SoC 中约束的优先级大致遵循:时序例外优先于普通时序要求,生成时钟优先于主时钟对同一对象的定义,后加载文件覆盖先加载文件中的同名约束。但“优先级高”不等于“一定生效”,还需要检查对象是否匹配、时钟是否已定义、命令是否被工具支持。
实际调试中,可以用 report_sdc 或 Libero 的约束检查功能查看每条约束是否被识别、作用在哪些对象上。若某条 set_false_path 没有出现在报告中,通常是因为 -from 或 -to 的对象名写错,或者时钟尚未定义。另一个常见问题是跨时钟域路径被误设为 false path 后,工具不再检查,但实际数据仍可能因亚稳态导致功能错误,因此 false path 只应用于确认无需时序检查的路径,而不是用来“消除违例”。
约束检查清单
| 检查项 | 判断依据 | 常见问题 |
|---|---|---|
| 主时钟周期与端口 | 与板级时钟频率一致 | 周期写错、端口名不匹配 |
| 生成时钟源 | 源引脚存在于网表中 | 源对象被优化或改名 |
| 输入输出延迟 | max/min 成对出现 | 只写 max,保持分析缺失 |
| 异步时钟组 | 跨域路径确实无相位关系 | 漏声明导致虚假违例 |
| 时序例外 | 有明确设计原因 | 滥用 false path 掩盖违例 |
把上述约束按文件分层管理、按优先级顺序加载,并在每次修改后通过报告确认生效情况,才能让 SDC 真正成为时序收敛的可信依据,而不是一份“写了但没起作用”的文档。