很多初学者看到板上只有一个50Mhz 时钟输入的时候都产生疑惑,时钟怎么才 50Mhz ?如果要工作在 100Mhz 、 150Mhz 怎么办?
其实在很多 FPGA 芯片内部都集成了 PLL ,其他厂商可能不叫 PLL ,但是也有类似的功能模块,通过 PLL 可以倍频分频,产生其他很多时钟。本实验通过调用 PLL IP core 来学习 PLL 的使用、 vivado 的 IP core 使用方法。
实验原理
PLL(phase locked loop) loop),即锁相环。是 FPGA 中的重要资源。由于一个复杂的 FPGA 系统往往需要多个不同频率,相位的时钟信号。所以,一个 FPGA 芯片中 PLL 的数量是衡量 FPGA 芯片能力的重要指标。
FPGA 的设计中,时钟系统的 FPGA 高速的 设计极其重要, 一个低抖动 , 低延迟的系统时钟会增加 FPGA 设计的成功率 。
本实验将通过使用PLL, 输出一个方波到开发板上的扩展口( AX7020/AX7010 开发板 J11 的PIN3 脚),来给大家演示在 Vivado 软件里使用 PLL 的方法 。
7 系 列 的 FPGA 使用了专用的全局 ( 和区域 (Regional)IO 和时钟资源来管理设计中各种的时钟需求。 Clock Management Tiles(CMT) 提供了时钟合成 (Clock frequency synthesis),倾斜矫正( deskew),过滤抖动 (jitter filtering) 功能。
每个 CMTs 包含一个 MMCM (mixed mode clock manager) 和一个 PLL 。 如下图所示, CMT 的输入可以是 BUFR IBUFG BUFG GT BUFH ,本地布线(不推荐使用),输出需要接到 BUFG 或者BUFH 后再使用
BUFG/BUFH/CMT在一个时钟区域内的连接
- 混合模式时钟管理器 MMCMM
MCM 用于在与给定输入时钟有设定的相位和频率关系的情况下,生成不同的时钟信号。MMCM 提供了广泛而强大的时钟管理功能,
- 数字 锁相环 PLL
锁相环(PLL )主要用于频率综合。使用一个 PLL 可以从一个输入时钟信号生成多个时钟信号。
其中MMCM的功能是 PLL的超集,其具有比 PLL更强大的相移功能。 MMCM主要用于驱动器件逻辑( CLB、 DSP、 RAM等 )的时钟。 PLL主要用于为内存接口生成所需的时钟信号,但也具有与器件逻辑的连接,因此如果需要额外的功能,它们可以用作额外的时钟资源。
PLL由 以下几部分组成:前置分频计数器( D计数器 、 相位 -频率 检测器 PFD Phase-FrequencyDetector 电路 电荷 泵( Charge Pump 、环路 滤波器( Loop Filter 、 压控振荡器( VCO Voltage Controlled Oscillator)、 反馈乘法器计数器( M计数器 和 后置分频计数器( O1-O6计数器 。
创建Vivado工程
本实验中为大家演示如果调用Xilinx 提供的 PLL IP 核来产生不同频率的时钟 , 并把其中的一个时钟输出到 FPGA 外部 IO 上 , 下面为程序设计的详细步骤。
-
新建一个 pll_test 的工程,点击 Project Manager 界面下的 IP Catalog 。
-
再在 IP Catalog 界面里选择 FPGA Features and Design Clocking 下面的 Clocking Wizard ,双击打开配置界面。
-
默认这个 Clocking Wizard 的名字为 clk_wiz_0, 这里我们不做修改。在第一个界面 Clocking Options 里, 我们选择 PLL 资源,输入的时钟频率为 50Mhz 。
-
在 Output Clocks 界面里选择 clk_out1~clk_out4 四个时钟的输出,频率分别为 200Mhz,100Mhz, 50Mhz, 25Mhz 。这里还可以设置时钟输出的相位,我们不做设置,保留默认相位,点击 OK 完成
-
在弹出的对话框中点击 Generate 按钮生成 PLL IP 的设计文件。
-
这时一个 clk_wiz_0.xci 的 IP 会自动添加到我们的 pll_test 项目中 , 用户可以双击它来修改这个 IP 的配置。
选择 IP Sources 这页,然后双击打开 clk_wiz_0.veo 文件,这个文件里提供了这个 IP 的 实例化模板 。我们只需要把框框的中内容拷贝到我们 verilog 程序中,对 IP 进行实例化。
-
我们再来编写一个顶层设计文件来实例化这个 PLL IP, 编写 pll_test.v 代码如下。 注意 PLL 的复位是高电平有效,也就是高电平时一直在复位状态, PLL 不会工作 ,这一点 很多新手会忽略掉 。这里我们将 rst_n 绑定到一个按键上,而按键是低电平复位,因此需要反向连接到 PLL 的复位。
module pll_test(
input sys_clk,
input rst_n,
output clk_out
);
wire locked;
/PLL IP call
clk_wiz_0 clk_wiz_0_inst(
// Clock in ports
.clk_in1(sys_clk), // in 50Mhz
// Clock out ports
.clk_out1(), // out 200Mhz
.clk_out2(), // out 100Mhz
.clk_out3(), // out 50Mhz
.clk_out4(clk_out), // out 25Mhz
// Status and control signals
.reset(~rst_n), // pll reset, high_actice
.locked(locked) // out
);
endmodule
程序中先用实例化clk_wiz_0 , 把单端 50Mhz 时钟信号输入到 clk_wiz_0 的 clk in 1 ,把clk_out4 的输出赋 给 clk _out 。
注意:例化的目的是在上一级模块中调用例化的模块完成代码功能,在Verilog 里例化信号的格式如下:模块名必须和要例化的模块名一致 ,比如程序中的 clk_wiz_0 ,包括 模块 信号名也必须一致 ,比如 clk_in1 clk_out1 clk_out 2… 。连接信号为 TOP 程序跟模块之间传递的
信号, 模块与模块之间的连接信号不能相互冲突,否则会产生编译错误。
8) 保存工程后, pll_test 自动成为了 top 文件, clk_wiz_0 成为 Pll_test 文件的子模块。
9) 再为工程添加 xdc 管脚约束文件 pll.xdc 添加方法参考 PL 的 ”Hello World”LED 实验 ””,也可以直接复制以下内容。 并编译生成 bitstream 。
############## clock and reset define##################
create_clock -period 20 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports {sys_clk}]
set_property PACKAGE_PIN U18 [get_ports {sys_clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {rst_n}]
set_property PACKAGE_PIN N15 [get_ports {rst_n}]
############## pll output define J11 PIN3##################
set_property IOSTANDARD LVCMOS33 [get_ports clk_out]
set_property PACKAGE_PIN F17 [get_ports clk_out]
仿真
添加一个 tb_pll_test 仿真文件
`timescale 1ns / 1ps
module tb_pll_test;
// pll_test Parameters
parameter PERIOD = 10;
// pll_test Inputs
reg sys_clk = 0 ;
reg rst_n = 0 ;
// pll_test Outputs
wire clk_out ;
initial
begin
forever #(PERIOD/2) sys_clk = ~sys_clk;
end
initial
begin
#(PERIOD*2) rst_n = 1;
end
pll_test u_pll_test (
.sys_clk ( sys_clk ),
.rst_n ( rst_n ),
.clk_out ( clk_out )
);
initial
begin
sys_clk = 0;
rst_n = 0;
# 1000
rst_n = 1;
$finish;
end
endmodule
运行后 PLL 的 lock 信号会变高,说明 PLL IP 锁相环已经初始化完成。 clk_out 有时钟信号输出,输出的频率为输入时钟频率的 1/2, 为 25Mhz 。
板上验证
编译工程并生成pll_test.bit 文件,再把 bit 文件下载到 FPGA 中,接下去我们就可以用示波器来测量输出时钟波形了。
用示波器探头的地线连接到开发板上的地(AX70 20/AX7010 开发板 J1 1 的 PIN 1 脚 )),信号端连接 AX70 20 开发板 J1 1 的 PIN3 脚(测量的时候需要注意,避免示波器表头碰到其它管脚而导致电源和地短路 。
这时我们可以在示波器里看到25Mhz 的时钟波形,波形的幅度为 3.3V, 占空比为 1:1, 波形显示如下图所示:
如果您想输出其它频率的波形,可以修改时钟的输出为 clk_wiz_0 的 clk_out2 或 clk_out3或 clk_out4 。也可以修改 clk_wiz_0 的 clk_out4 为您想要的频率,这 里也需要注意一下,因为时钟的输出是通过PLL 对输入时钟信号的倍频和分频系数来得到的,所以并不是所有的时钟频率都可以用 PLL 能够精确产生的,不过 PLL 也会自动为您计算实际输出接近的时钟频率。
另外需要注意的是,有些用户的示波器的带宽和采样率太低,会导致测量高频时钟信号的时候,高频部分衰减太大,测量波形的幅度会变低。
生成其他PLL信号
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/12/20 21:26:23
// Design Name:
// Module Name: pll_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module pll_test(
input sys_clk,
input sys_rst_n,
output clk_out_100m,
output clk_out_180deg,
output clk_out_50m,
output clk_out_25m
);
wire locked;
/PLL IP call
clk_wiz_0 instance_name (
// Clock out ports
.clk_out1(clk_out_100m), // output clk_out1
.clk_out2(clk_out_180deg), // output clk_out2
.clk_out3(clk_out_50m), // output clk_out3
.clk_out4(clk_out_25m), // output clk_out4
// Status and control signals
.reset( ~sys_rst_n ), // input reset
.locked(locked), // output locked
// Clock in ports
.clk_in1(sys_clk)
); // input clk_in1
ila_0 ila_0_inst (
.clk(sys_clk), // input wire clk
.probe0(sys_rst_n), // input wire [0:0] probe0
.probe1(clk_out), // input wire [0:0] probe1
.probe2(locked) // input wire [0:0] probe2
);
endmodule