ZYNQ从入门到秃头06 Vivado下的IP核MMC/PLL实验

Posted “逛丢一只鞋”

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZYNQ从入门到秃头06 Vivado下的IP核MMC/PLL实验相关的知识,希望对你有一定的参考价值。

很多初学者看到板上只有一个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 上 , 下面为程序设计的详细步骤。

  1. 新建一个 pll_test 的工程,点击 Project Manager 界面下的 IP Catalog 。

  2. 再在 IP Catalog 界面里选择 FPGA Features and Design Clocking 下面的 Clocking Wizard ,双击打开配置界面。

  3. 默认这个 Clocking Wizard 的名字为 clk_wiz_0, 这里我们不做修改。在第一个界面 Clocking Options 里, 我们选择 PLL 资源,输入的时钟频率为 50Mhz 。

  4. 在 Output Clocks 界面里选择 clk_out1~clk_out4 四个时钟的输出,频率分别为 200Mhz,100Mhz, 50Mhz, 25Mhz 。这里还可以设置时钟输出的相位,我们不做设置,保留默认相位,点击 OK 完成

  5. 在弹出的对话框中点击 Generate 按钮生成 PLL IP 的设计文件。

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

    选择 IP Sources 这页,然后双击打开 clk_wiz_0.veo 文件,这个文件里提供了这个 IP 的 实例化模板 。我们只需要把框框的中内容拷贝到我们 verilog 程序中,对 IP 进行实例化。

  7. 我们再来编写一个顶层设计文件来实例化这个 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

以上是关于ZYNQ从入门到秃头06 Vivado下的IP核MMC/PLL实验的主要内容,如果未能解决你的问题,请参考以下文章

ZYNQ从入门到秃头07 FPGA 片内 RAM && ROM 读写测试实验

ZYNQ从入门到秃头07 FPGA 片内 RAM && ROM 读写测试实验

ZYNQ从入门到秃头10 DAC FIFO实验(AXI-stream FIFO IP核配置)

ZYNQ从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)

ZYNQ从入门到秃头03 Vivado软件的硬件调试

ZYNQ从入门到秃头[番外] Vivado VSCode现代化编辑工具配置