这一篇在干嘛?

板子只给了你一个 50MHz 晶振时钟,但实际系统常常需要 25MHz、70MHz 甚至多路不同频率、不同相位的时钟。这一篇学习用开发工具的 IP 生成器配置锁相环(PLL),从一个输入时钟「变」出多路输出时钟,并烧板后用示波器逐一验证。这是第一次接触 FPGA 的专用时钟资源,也是从「写逻辑」迈向「用硬核资源」的门槛实验。

实验目的与原理

实验目的

了解 PLL 设计流程及方法。

PLL 是什么,为什么重要

PLL(phase-locked loop),即锁相环,是 FPGA 中的重要资源。由于一个复杂的 FPGA 系统往往需要多个不同频率、相位的时钟信号,所以一个 FPGA 芯片中 PLL 的数量是衡量 FPGA 芯片能力的重要指标。FPGA 的设计中,时钟系统对 FPGA 高速的设计极其重要,一个低抖动、低延时的系统时钟会增加 FPGA 设计的成功率。

为什么不能靠计数器分频得到所有时钟?因为逻辑分频有几个硬伤:

  • 只能变慢不能变快:计数器分频只能得到低于输入频率的时钟,想要 70MHz 就无能为力了;
  • 抖动与偏斜大:普通逻辑产生的时钟走的是通用布线,到达各触发器的时间差(偏斜)大,高频下时序很难收敛;
  • 占用逻辑资源:用通用逻辑当「时钟发生器」浪费资源还容易出问题。

PLL 是 FPGA 内部的专用硬件电路,它通过压控振荡器(VCO)把输入时钟「倍频」到一个很高的内部频率,再由一组分频器把 VCO 频率分给各路输出——所以既能倍频也能分频,而且走专用时钟网络,抖动低、偏斜小。

数字锁相环(PLL)

锁相环(PLL)主要用于频率综合。使用一个 PLL 可以从一个输入时钟信号生成多个时钟信号。PLL 内部的功能框图如下图所示:

从框图理解它的工作闭环:输入参考时钟与反馈回来的时钟做相位比较,误差信号控制 VCO 调整振荡频率,直到两者相位锁定——「锁相环」因此得名。锁定之后,输出时钟与输入时钟保持固定的倍频/分频比例关系,频率精准、相位可控。

混合模式时钟管理器(MMCM)

MMCM 用于在不给定输入时钟有设定的相位和频率关系的情况下,生成不同的时钟信号。MMCM 提供了广泛而强大的时钟管理功能,MMCM 内部的功能框图如下图所示:

PLL 与 MMCM 怎么选?

简单记:只是要几路固定频率、固定相位的时钟,用 PLL 就够了,结构简单、抖动低;需要动态调频、精细移相、更多输出路数这类高级玩法,就轮到 MMCM 出场。本实验用的是最典型的 PLL 场景——一个 50MHz 输入,三路不同频率输出。

实验流程

整个实验的关键不在写代码,而在用 TD 工具配置 IP。跟着下面步骤走一遍:

  1. 新建一个 pll_test 的工程,点击 tools 界面下的 IP Generator。

  1. 再 File Name 一栏输入 PLL。

  1. 在界面里,我们选择 PLL 资源,输入时钟频率为 50Mhz。

  1. 在 Output Clocks 界面里选择 clk_out1~clk_out3 三个时钟的输出,频率分别为 6.25Mhz、25Mhz、70Mhz。这里还可以设置时钟输出的相位,保留默认相位。

  1. 点击 OK 完成,在弹出的对话框中点击 OK 按钮生成 PLL IP 的设计文件。

  1. 这时一个 pll.ipc 的 IP 会自动添加到我们的 pll_test 项目中,用户可以双击它来修改这个 IP 的配置。

  2. 保存工程后,pll_test 自动成为了 top 文件,pll_wiz_0 成为 pll_test 文件的子模块。

工具自动生成的 IP 设计文件开头注释里写明了时钟配置信息(按原文保留):

// Input frequency: 50.000MHz
// Clock multiplication factor: 14
// Clock division factor: 1
// Clock information:
//    Clock name | Frequency | Phase shift
//    C0 | 6.481481 MHz | 0 DEG
//    C1 | 25.000000 MHz | 0 DEG
//    C2 | 70.000000 MHz | 0 DEG
 
// timescale 1 ns / 100 fs
module PLL (
    refclk,
    reset,
    extlock,
    clk0_out,
    clk1_out,
    clk2_out
);
input refclk;
input reset;
output extlock;
output clk0_out;
output clk1_out;
output clk2_out;
 
EG PHY_PLL #(
    .DPHASE_SOURCE("DISABLE"),
    .DYNCFG("DISABLE"),
    .FIN("50.000"),

读一读 IP 生成的「账本」

这段注释就是 PLL 的频率计算账本:VCO 频率 = 输入 50MHz × 倍频系数 14 ÷ 分频系数 1 = 700MHz,三路输出再各自从 VCO 分频得到。C2 = 700 ÷ 10 = 70MHz、C1 = 700 ÷ 28 = 25MHz 与配置吻合;C0 注释写的是 6.481481MHz(700 ÷ 108),与界面目标值 6.25MHz 略有出入——最终以 IP 文件注释和示波器实测为准。学会看这个注释,以后配置任何 PLL 都心里有数。

生成的 PLL 模块端口也值得记一下:refclk 是参考时钟输入,reset 复位,extlock 是「锁定完成」指示输出(上电后 PLL 需要一小段时间才能锁定,严谨的设计要等 extlock 有效再让下游逻辑开工),clk0_out~clk2_out 就是三路输出时钟。

引脚锁定

再为工程添加 xdc 管脚约束文件 pll_test.xdc

set_pin_assignment { clk25mhz } { LOCATION = G11; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { clk50mhz } { LOCATION = R7; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { clk6mhz } { LOCATION = D14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { clk70mhz } { LOCATION = G12; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { rst } { LOCATION = A2; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }

PLL 输出必须走专用时钟引脚

注意三路输出时钟约束到了 G11、D14、G12 这几个引脚——它们不是随便挑的普通 IO,而是芯片上支持时钟输出的专用引脚。PLL 输出接到不支持的普通引脚会导致布线失败或波形异常。做 PLL 类实验时,输出引脚要对照芯片数据手册的时钟专用引脚表来分配。

输入时钟 clk50mhz 仍然是熟悉的老位置 R7,与前面所有实验一致;rst 仍是键 1(A2,低有效)。

下载测试

编译工程并生成 pll_test.bit 文件,再把 bit 文件下载到 FPGA 中,接下去我们就可以用示波器来测量 J1 接口上的 PIN3 输出时钟波形了。

用示波器探头的地线接到开发板上的地(J1 的 Pin12),再用示波器探头点中 J1 的 PIN1、2、3 脚(测量的时候需要注意,避免示波器表头碰到其它管脚而导致电源和地短路),测试如下图所示:

6.5Mhz 通道输出的波形:

25Mhz 通道输出的波形:


70Mhz 通道输出的波形

验收要点:

  • 三路波形都应是稳定的方波,周期分别约为 160ns(6.25/6.5MHz)、40ns(25MHz)和 14.3ns(70MHz);
  • 频率越高波形边沿相对越慢、幅度可能略降,这是正常的物理现象;
  • 若某一路无输出或频率不对,优先检查 IP 配置里对应输出通道是否使能、频率是否填对,再检查该路引脚约束。

现象与总结

这个实验的代码量几乎为零,但学会了一套完整的「时钟工程」流程:

  1. IP Generator 配置 PLL:输入频率 → 选择输出通道和目标频率 → 生成 IP;
  2. 读懂生成文件:端口定义 + 头部注释里的倍频/分频系数与各路实际频率;
  3. 约束与实测:输出走专用时钟引脚,示波器逐一验证。

从此以后,工程里需要任何频率的时钟(比如后面乐曲演奏器要的 2kHz、1MHz),都不再需要拿逻辑分频硬凑——先想想 PLL 能不能直接给。这也是本实验出现在 DEMO 序列中段的用意:它是很多后续实验的「水电煤」。

自测