3. Lattice设计约束基础
在Lattice FPGA的时序收敛流程中,SDC(Synopsys Design Constraints)是贯穿综合、映射、布局布线与静态时序分析(STA)全过程的约束载体。与Xilinx的XDC、Intel的SDC类似,Lattice的约束体系同样以Tcl命令为基础,但不同器件系列(如ECP5、iCE40、CrossLink-NX、Certus-NX、MachXO3等)在工具链上存在差异:Diamond使用LSE/Precision综合与自身的时序分析引擎,Radiant则基于Lattice的LSE或Synplify Pro,并采用统一的SDC解析器。理解SDC在Lattice工具中的支持范围、约束语义以及加载顺序,是后续时序收敛与约束治理的前提。
3.1 SDC在Lattice工具链中的支持范围
Lattice工具对SDC的支持并非全量覆盖。Diamond与Radiant均支持核心的时钟定义、输入输出延迟、时序例外、多周期路径、伪路径、最大/最小延迟等命令,但对部分高级命令(如set_clock_groups的某些变体、set_data_check、set_max_time_borrow)的支持程度因版本和器件而异。实际工程中,最常用且必须掌握的SDC命令包括:
create_clock/create_generated_clock:定义主时钟与衍生时钟;set_input_delay/set_output_delay:描述外部器件与FPGA之间的接口时序;set_false_path/set_multicycle_path:声明时序例外;set_max_delay/set_min_delay:覆盖默认的路径延迟要求;set_clock_groups:声明异步或互斥时钟域;set_disable_timing:切断特定单元的时序弧。
需要注意的是,Lattice工具在读取SDC时,会将其转换为内部时序图上的约束对象。若命令不被支持,工具通常给出警告而非报错,但该约束不会生效——这正是许多“约束写了却没收敛”问题的根源。因此,在编写SDC前,应查阅对应版本的工具手册,确认命令支持列表。
3.2 时钟约束:时序分析的起点
时钟约束是所有时序分析的基准。没有create_clock,STA引擎无法建立时间轴,后续的输入输出延迟、例外约束都失去参照。在Lattice工具中,时钟约束通常写在SDC文件的开头,并遵循以下原则:
- 每个实际进入FPGA的时钟端口都应有一条
create_clock; - 时钟周期使用纳秒(ns)为单位,例如100 MHz对应10 ns;
- 对于PLL输出的衍生时钟,优先使用
create_generated_clock,并指定源时钟与分频/倍频关系; - 若时钟由内部逻辑分频产生,应通过
create_generated_clock描述其与源时钟的相位关系,而非再建一个独立主时钟。
一个典型的Lattice ECP5时钟约束示例如下:
create_clock -name sys_clk -period 10.000 [get_ports clk_in]
create_clock -name ext_clk -period 20.000 [get_ports clk_ext]
create_generated_clock -name pll_clk \
-source [get_ports clk_in] \
-divide_by 2 -multiply_by 4 \
[get_pins pll_inst/CLKOP]
这里sys_clk为100 MHz主时钟,ext_clk为50 MHz外部时钟,PLL输出pll_clk为源时钟的4倍频再2分频,即200 MHz。工具会据此推导出各时钟域之间的相位关系,并用于建立/保持时间检查。
3.3 输入输出延迟:接口时序的量化描述
输入延迟(set_input_delay)和输出延迟(set_output_delay)用于描述FPGA外部器件与FPGA引脚之间的时序关系。它们不是FPGA内部路径的延迟,而是“外部世界”对FPGA接口提出的时间要求。以源同步接口为例,若外部器件在时钟上升沿发出数据,数据到达FPGA引脚的时间相对于时钟沿有最大和最小偏移,则约束应写成:
set_input_delay -clock sys_clk -max 3.5 [get_ports data_in*]
set_input_delay -clock sys_clk -min 1.2 [get_ports data_in*]
set_output_delay -clock sys_clk -max 4.0 [get_ports data_out*]
set_output_delay -clock sys_clk -min 1.5 [get_ports data_out*]
其中-max用于建立时间检查,-min用于保持时间检查。数值的确定依赖于外部器件的Tco、PCB走线延迟以及时钟偏斜。若接口为DDR或源同步随路时钟,还需配合-clock_fall或create_generated_clock描述随路时钟。输入输出延迟写错,往往导致接口路径被过度约束或约束不足,前者浪费布线资源,后者留下时序隐患。
3.4 时序例外:伪路径与多周期路径
时序例外用于修正STA引擎对某些路径的默认分析方式。最常见的两类是伪路径(set_false_path)和多周期路径(set_multicycle_path)。伪路径表示该路径在功能上不需要满足时序要求,例如异步时钟域之间的握手同步器、复位路径、配置寄存器路径等。多周期路径则表示该路径允许在多个时钟周期内完成,例如慢速使能控制的乘法器或分频逻辑。
set_false_path -from [get_clocks ext_clk] -to [get_clocks sys_clk]
set_multicycle_path 2 -setup -from [get_pins mult_reg*/D] -to [get_pins result_reg*/D]
set_multicycle_path 1 -hold -from [get_pins mult_reg*/D] -to [get_pins result_reg*/D]
注意多周期路径必须同时约束setup和hold,且hold的周期数通常为setup减一,否则保持时间检查会过于宽松或过于严苛。伪路径的声明应尽量精确,避免使用-from [all_clocks] -to [all_clocks]这类全局豁免,否则可能掩盖真实的时序问题。
3.5 约束文件组织与加载流程
在Lattice工程中,SDC文件通常以.sdc或.ldc(Diamond)为扩展名,通过工程属性或Tcl脚本加载。合理的组织方式是按功能拆分:时钟约束、接口约束、例外约束、区域约束分别放在不同文件或同一文件的不同段落,并用注释标明用途。加载顺序至关重要:时钟约束必须最先加载,其次是输入输出延迟,最后是时序例外。若例外约束在时钟之前加载,工具可能因找不到目标时钟而忽略该约束。
在Radiant中,可通过ldc_set或直接在策略中指定SDC文件;在Diamond中,则通过Project > Active Strategy > Timing Analysis加载。无论哪种方式,都应在综合后、布局布线前检查约束是否被正确读取。工具通常会在日志中列出已解析的约束数量及未匹配的约束,未匹配项往往意味着对象名错误或加载顺序不当。
3.6 约束加载顺序与常见陷阱
约束加载顺序不是形式问题,而是直接影响约束是否生效。正确的顺序应为:先create_clock建立时间基准,再set_input_delay/set_output_delay描述接口,最后set_false_path/set_multicycle_path修正例外。若顺序颠倒,工具在解析例外约束时可能找不到对应的时钟对象,从而静默忽略。另一个常见陷阱是对象名不匹配:get_ports、get_pins、get_clocks的返回值必须与网表中的实际名称一致,大小写敏感,且端口名可能因综合优化而被重命名。建议在综合后先运行一次report_clocks和report_timing_requirements,确认约束已正确绑定。
report_timing -constraint查看每条约束对应的时序路径;在Diamond中,使用report_timing -verbose可输出约束匹配详情。若日志中出现“Constraint ignored”或“No objects matched”,应优先检查对象名和加载顺序。
约束文件组织建议采用“主文件+子文件”的方式:主SDC文件按顺序source各子文件,子文件分别负责时钟、接口、例外。这样既保证加载顺序可控,又便于版本管理和复用。对于多时钟域设计,还应单独维护一份时钟域交叉(CDC)约束清单,明确哪些路径是伪路径、哪些是多周期路径,避免遗漏或过度约束。
最后需要强调:SDC约束不是“写完就完”的静态文档,而是需要随设计迭代持续维护的活文档。每次修改RTL或更换器件后,都应重新检查约束的匹配情况和时序报告,确保约束始终与设计意图一致。只有约束准确、加载正确,后续的时序收敛才有可靠的基础。