2. Quartus工程创建与时钟约束基础
在Quartus中做多时钟系统设计,第一步不是写代码,而是把工程骨架和时序约束的“地基”打好。工程创建决定了源文件、器件、引脚如何被组织;SDC约束文件则决定了时序分析引擎如何看待你的时钟。两者缺一不可,且顺序不能颠倒——先有工程,再谈约束。
一、新建工程:从器件选型到顶层实体
启动Quartus后,通过 File → New Project Wizard 进入新建工程向导。向导会依次询问工程目录、工程名、顶层实体名、源文件、器件型号和EDA工具设置。这里有几个容易被忽视但影响后续约束的关键点:
- 工程名与顶层实体名:建议保持一致,例如工程名
multi_clk_demo,顶层实体也命名为multi_clk_demo。Quartus默认以顶层实体名作为编译入口,名称不一致时需要在Settings中手动指定Top-Level Entity。 - 器件选型:多时钟设计对PLL资源、全局时钟网络数量敏感。以Cyclone IV EP4CE10F17C8为例,它提供2个PLL和多个全局时钟引脚,适合入门级多时钟实验。若选型过小,后续PLL级联或时钟切换可能无法布局。
- 源文件添加:可以在向导中直接添加已有的Verilog/VHDL文件,也可以先建空工程,后续再添加。建议先建空工程,确认器件和目录无误后再添加源文件,避免路径混乱。
工程创建完成后,Quartus会在工程目录下生成 .qpf(工程文件)和 .qsf(设置文件)。.qsf是纯文本文件,记录了器件、引脚、时序约束等所有工程设置,后续引脚分配和SDC引用都会写入这里。
二、引脚分配:把逻辑端口映射到物理引脚
引脚分配的本质是建立“设计端口”与“FPGA物理引脚”之间的映射关系。在Quartus中可以通过 Assignments → Pin Planner 图形化完成,也可以直接在 .qsf 中写入。对于多时钟系统,时钟输入引脚必须分配到支持全局时钟网络的专用引脚(如 CLK0、CLK1),否则时钟信号会走普通布线资源,引入较大偏斜和抖动。
以下是一个示例性的引脚分配片段,假设外部晶振50MHz接入 PIN_23,复位按键接入 PIN_25,LED输出接入 PIN_101:
set_location_assignment PIN_23 -to clk_50m
set_location_assignment PIN_25 -to rst_n
set_location_assignment PIN_101 -to led_out[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk_50m
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to rst_n
注意:引脚编号必须与所用开发板原理图一致,不同板卡的 PIN_xx 编号不同。分配完成后,建议在Pin Planner中检查是否有未分配端口或冲突分配,未分配端口在编译时会报错或自动分配到随机引脚,导致上板行为不可预期。
三、SDC约束文件:时钟定义与基本约束
SDC(Synopsys Design Constraints)是Quartus时序分析的标准约束格式。在Quartus中通过 Assignments → Settings → Timing Analysis → SDC files 添加 .sdc 文件,或直接在 .qsf 中引用。SDC文件的核心任务是告诉时序引擎:有哪些时钟、频率多少、从哪里来到哪里去、输入输出外部延迟是多少。
最基本的时钟约束使用 create_clock 命令。以下示例定义了一个50MHz输入时钟和一个由PLL生成的100MHz内部时钟:
create_clock -name clk_50m -period 20.000 [get_ports {clk_50m}]
create_clock -name clk_100m -period 10.000 [get_ports {clk_100m}]
derive_pll_clocks
derive_clock_uncertainty
其中 -period 20.000 表示周期20ns,对应50MHz;derive_pll_clocks 让Quartus自动从PLL配置推导输出时钟,避免手动逐个定义;derive_clock_uncertainty 自动添加时钟不确定性(抖动、偏斜余量),是Quartus推荐的基础约束。
除了时钟定义,输入输出延迟约束同样重要。例如外部器件在时钟上升沿后2ns输出数据到FPGA,FPGA在时钟上升沿前1ns需要数据稳定:
set_input_delay -clock clk_50m -max 2.0 [get_ports {data_in[*]}]
set_input_delay -clock clk_50m -min 1.0 [get_ports {data_in[*]}]
set_output_delay -clock clk_50m -max 3.0 [get_ports {data_out[*]}]
set_output_delay -clock clk_50m -min 1.5 [get_ports {data_out[*]}]
这些数值需要根据外部器件的时序手册填写,不能随意假设。若外部器件手册给出 tCO=2ns、tSU=1ns,则上述约束是合理的示例值。
四、约束与工程的关联关系
工程、引脚分配和SDC约束三者不是孤立的。工程提供器件和源文件,引脚分配把端口固定到物理位置,SDC则基于这些端口和内部时钟网络建立时序模型。如果引脚分配错误,SDC中的 get_ports 可能找不到对象;如果SDC中时钟名称与设计中的端口名不一致,时序分析会忽略该时钟,导致约束失效。
上图展示了工程、引脚分配、SDC约束与TimeQuest时序分析引擎之间的数据流关系。工程提供器件和源文件基础,引脚分配确定物理位置,SDC提供时序模型,三者共同输入时序分析引擎,最终输出Slack报告用于判断设计是否满足时序要求。
Read SDC File 和 Update Timing Netlist,快速检查约束是否被正确解析。若 get_ports 或 get_clocks 返回空集合,说明名称不匹配,需要回到引脚分配或顶层端口定义中核对。
create_clock 未生效,时序分析报告出现大量“Unconstrained”路径。此时应使用 get_ports * 或 get_clocks * 列出所有对象,逐一核对名称。