本文介绍如何使用 ZYNQ自带的时钟模块 并进行多路时钟信号的输出。
本文在 vivado2018.3版本上 演示, 其他版本请自行研究
备注 此文针对1.1及以上版本硬件,如果是v1.0的硬件请看对应的工程
用FPGA做设计,有时候我们需要不同的时钟来驱动各个模块,比方说之前hdmi部分我们同时需要同时有40和200mhz的时钟,又或者如果我们的设计中有摄像头传感器,那我们通常还需要给传感器一个24-30mhz的时钟,又或者系统里有sdram,这个时候我们要给一个100mhz甚至更高的时钟来实现快速数据交互,但是通常我们的板子上只有一个晶振,想要生成不同的时钟这个时候我们就需要用到PLL锁相环功能了
PLL的英文全称是Phase Locked Loop,即锁相环,是一种反馈控制电路。PLL对时钟网络进行系统级的时钟管理和偏移控制,具有时钟倍频、分频、相位偏移和可编程占空比的功能。Xilinx 7系列器件中的时钟资源包含了时钟管理单元CMT,每个CMT由一个MMCM和一个PLL组成。对于一个简单的设计来说,FPGA整个系统使用一个时钟或者通过编写代码的方式对时钟进行分频是可以完成的,但是对于稍微复杂一点的系统来说,系统中往往需要使用多个时钟和时钟相位的偏移,且通过编写代码输出的时钟无法实现时钟的倍频,因此学习Xilinx MMCM/PLL IP核的使用方法是我们学习FPGA的一个重要内容。本章我们将通过一个简单的例程来向大家介绍MMCM/PLL IP核的
PLL(phase-locked loop),即锁相环。可为系统提供低抖动,低延迟,不同频率,不同相位的时钟,大大增加了系统设计的灵活性和FPGA设计的成功率,可以说在FPGA系统的设计中,PLL是非常重要的一个资源,也是衡量FPGA芯片能力的一个重要指标。
本实验将通过使用PLL输出不同频率的时钟(100m和25m)去分别驱动两个相同的LED模块(工程一中的LED模块的两个例化)通过LED的显示频率的不同来观察区别,同时将PLL输出的两个频率相同(100mhz)但是相位不同(90度)的波形映射到IO口,方便用示波器观察
程序设计
1)首先正常创建一个工程(可参考前面的例子),
2)添加PLL IP核。在Vivado软件的选中“IP Catalog”
在弹出的窗口中搜索 clock 并在搜索出的结果中双击Clock Wizard。进入时钟模块设置向导
系统弹出窗口如下图所示,这里 我们选择默认的MMCM就好,输入时钟的input Frequency地方改成板子上的50(对应PL端的50M时钟芯片),其他选项保持默认
接下来我们设置输出时钟,PLL在系统中即可以倍频也可以降频,输入的时钟是50m,那我们输出3个时钟分别进行测试 clk1是100mhz(频率升高),clk2设置成100m但是相位偏移90度(可以和clk1作对比),clk3是25m频率降低,
其他的一些选项一半都不需要修改保持默认即可(理论上并不是任何频率都可以生成,当填入的频率系统不能直接生成时,VIVADO会根据你输入的频率,还有输出的几组频率去匹配出最接近的输出频率值)
之后点选OK 在弹出的窗口选generate去生成时钟模块
接下来在我们的代码里 例化这个时钟模块
这里有个小技巧,在source目录的IP—>clk_wiz_0—>Instantitation Template—>clk_wiz_0.veo文件中我们可以找到模块例化的参考代码,可以直接(改个名字)参考调用
接下来将clk_100m,和clk_100m_90deg(相位偏移90度) 分别映射到模块的output,方便用示波器观察, 并同时例化相同的两个LED模块,分别用100m和25m时钟对这两个LED模块进行驱动。
`timescale 1ns / 1ps module CLOCK_TEST( input clk, //50M in output clk_out1, //100M out output clk_out2, //25M out output led1, output led2 ); wire clk_100m; wire clk_100m_90deg; //25M out 90deg wire clk_25m; clk_wiz_0 u_clock( .clk_out1(clk_100m), .clk_out2(clk_100m_90deg), .clk_out3(clk_25m), .reset(0), .locked(), .clk_in1(clk) ); assign clk_out1=clk_100m; assign clk_out2=clk_100m_90deg; LED_MODULE U1( .clk(clk_100m), .led(led1) ); LED_MODULE U2( .clk(clk_25m), .led(led2) ); endmodule
因为时钟模块中的 RESET信号是 高电平有效,所以上面的程序直接简单给 reset 信号赋0即.reset(0)(当然也可以接外部复位信号,这里仅演示所以不接了)
LED模块直接调用之前工程一中编写的LED模块(正常情况如果输入标准50m波形则LED灯一秒钟变换一次状态)
module LED_MODULE( input clk, output led ); parameter T1MS = 26'd50_000_000 ; //50M晶振时钟 reg [25:0]time_count;//时钟计数器 reg led_r; always@(posedge clk) if(time_count>=T1MS)begin time_count<=26'd0; led_r<=~led_r; end else time_count<=time_count+1'b1; assign led=led_r; endmodule
添加 约束文件(因为50m输入是接在普通IO所以额外要加一句CLOCK_DEDICATED_ROUTE约束语句)
set_property PACKAGE_PIN R18 [get_ports clk_out1] set_property PACKAGE_PIN P20 [get_ports clk_out2] set_property PACKAGE_PIN K17 [get_ports clk] set_property PACKAGE_PIN D18 [get_ports led1] set_property PACKAGE_PIN F20 [get_ports led2] set_property IOSTANDARD LVCMOS33 [get_ports clk_out1] set_property IOSTANDARD LVCMOS33 [get_ports clk_out2] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports led1] set_property IOSTANDARD LVCMOS33 [get_ports led2]
之后进行编译综合和下载
观察LED灯的闪烁
成功后我们可以通过LED观察到 LED1每隔1秒闪烁一次(即500ms变换一次状态),而LED1每4秒闪烁一次(即2秒变换一次状态),通过理论分析 LED1的驱动时钟100m(相当于 100_000_000),所以LED模块的每50_000_000变换一次状态刚好是半秒即500ms),同理LED2的驱动时钟是25mhz(相当于25_000_000),所以LED模块的每50_000_000变换一次状态刚好是两个周期,也即2s钟)。
通过示波器观察两个100Mhz波形
将示波器表笔接到 R18和P20上 如下图所示:
通过示波器可以看到两个100mhz的波形 ,并且两者之间有一定的相位偏差
以上就是我们对PLL模块的一些简单应用测试
备注:由于FPGA输出阻抗是50欧左右,大部分示波器输入阻抗是1M,又由于GPIO直接接的示波器(没有接任何负载),导致两者阻抗不匹配致使部分示波器在测量高速方波信号的时候会出现明显的振铃信号(属于正常现象不需要过多关注)。 当测量的波形出现很大的过冲影响观察时,可以尝试在示波器和信号之间串接一个电阻来消除因阻抗不匹配导致的振铃信号(50-200欧,越大效果越好)。 波形的过冲和负载大小,还以及测量方式有很大的关系,还和示波器的输入阻抗有关 。
振铃信号类似下图所示(属于正常现象),本试验只需要观察波形频率就好
后记(网上也查到说 示波器的接地夹更换成 弹簧探头可以解决示波器抓到的波形有振铃的问题, 回头我买一个弹簧探针测试看看),等弹簧探针回来 再更新本内容
完整工程如下: