《基于PLL分频计数的LED灯闪烁实例》实验记录

问题概述:

输入FPGA引脚上的25MHz时钟,配置PLL使其输出4路分别为12.5MHz25MHz50MHz100MHz的时钟信号,这4路时钟信号又分别驱动4个不同位宽的计数器不停的计数工作,这些计数器的最高位最终输出用于控制4个不同的LED亮灭由于这4个时钟频率都有一定的倍数关系,所以我们也很容易通过调整合理的计数器位宽,达到4LED闪烁一致的控制。


问题就这么多,我觉得有必要对此做一个简单的解释:

先介绍一下PLL:

PLL(Phase Locked Loop):为锁相回路或锁相环,用来统一整合时脉讯号,使内存能正确的存取资料。PLL用于振荡器中的反馈技术。许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步,利用锁相环路就可以实现这个目的

PLL一个最主要的功能:能够对输入的基准时钟信号进行一定范围内的分频或者倍频,从而产生多个输出时钟信号供芯片内部的各个功能模块使用。


然后解释下上面的要求:

使用Spartan-6内部的PLL产生四个输出时钟,分别为12.5MHz25MHz50MHz100MHz,然后由这4路时钟分别驱动各自的计数器计数,也就是上升沿计数。

由于4路输出时钟的频率成2倍关系,故各自驱动的计数器的计数频率也成2倍关系,也就是频率高的时钟计数的速度块。例如,用12.5MHz的时钟驱动一个23位的计数器,计数达到最大;

然后用25MHz的时钟驱动一个23位的计数器,计数达到最大;

那么二者谁先计数达到最大呢?

显然,25MHz的时钟驱动的计数器只用一半于12.5MHz的时钟驱动的计数器,就能计数达到最大值。

那么如何才能才能使,二者都计数达到最大值,且用的时间一致呢?

很显然,让25MHz时钟驱动的计数器的位数加宽1位即可,也就是24位的计数器,多1位意味着计数值多了1倍,这样用的时间就比原来多了1倍,可以达到要求的目的。

根据上面的思路,让50MHz的时钟驱动的计数器位宽达到25位,100MHz的时钟驱动的计数器的位宽达到26位。

那么这四个计数器计数达到最大值,使用的时间一样,我们让各自对应的led灯从各自的计数器从0开始计数时,处于点亮状态,然后计数达到最大值后熄灭,之后再从0计数,意思就是LED灯再次点亮,如此循环。

如果4个LED灯同时亮灭,那么就说明实验成功。


关于如何使用定制一个PLL IP核,见博文:

Xilinx IP核专题之PLL IP核介绍(Spartan-6)

例化模板如下:

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

  pll_clk instance_name
   (// Clock in ports
    .CLK_IN1(CLK_IN1),      // IN
    // Clock out ports
    .CLK_OUT1(CLK_OUT1),     // OUT
    .CLK_OUT2(CLK_OUT2),     // OUT
    .CLK_OUT3(CLK_OUT3),     // OUT
    .CLK_OUT4(CLK_OUT4),     // OUT
    // Status and control signals
    .RESET(RESET),// IN
    .LOCKED(LOCKED));      // OUT
// INST_TAG_END ------ End INSTANTIATION Template ---------

然后如何使用Verilog HDL设计这样的一个满足要求的电路呢?

这里需要采用模块化设计:

模块化设计概述

模块功能相对独立,模块内部联系尽量紧密,而模块间的连接尽量简单

模块化设计的实现步骤包含:

●初始预算,本阶段是实现步骤的第一步,对整个模块化设计起着指导性的作用

●子模块的激活模式实现,在该阶段,每个项目成员并行完成各自子模块的实现

●模块的最后合并,在该阶段项目管理者将顶层的实现结果和所有子模块的激活模式实现结果有机地组织起来,完成整个设计的实现步骤。

模块化设计实践

在这个设计中,顶层模块是sp6_pll.v,我们将使用模块化的设计,将各个不同的独立的功能逻辑代码分别写在不同的源文件中,然后通过“例化”的方式,将它们之间的接口互联起来

因此,这个例子中,sp6_pll.v文件里面其实几乎是没有具体的逻辑功能的,它只是做一些基本的例化和互联,将它下面的5个功能模块相关的接口信号都连接起来。


如果还不是太明白,那就看看代码吧。

主模块:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    21:49:05 08/16/2018 
// Design Name: 
// Module Name:    sp6_pll 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module sp6_pll(
			input ext_clk_25m,	//外部输入25MHz时钟信号
			input ext_rst_n,	//外部输入复位信号,低电平有效
			output[7:0] led		//8个LED指示灯接口	
		);													

wire clk_12m5;	//PLL输出12.5MHz时钟
wire clk_25m;	//PLL输出25MHz时钟
wire clk_50m;	//PLL输出50MHz时钟
wire clk_100m;	//PLL输出100MHz时钟
wire sys_rst_n;	//PLL输出的locked信号,作为FPGA内部的复位信号,低电平复位,高电平正常工作
		
//-------------------------------------
//PLL例化
  pll_clk u1
   (// Clock in ports
    .CLK_IN1(ext_clk_25m),      // IN
    // Clock out ports
    .CLK_OUT1(clk_12m5),     // OUT
    .CLK_OUT2(clk_25m),     // OUT
    .CLK_OUT3(clk_50m),     // OUT
    .CLK_OUT4(clk_100m),     // OUT
    // Status and control signals
    .RESET(!ext_rst_n),// IN
    .LOCKED(sys_rst_n));      // OUT		
		
		
//-------------------------------------
//12.5MHz时钟进行分频闪烁,计数器为23位															
led_control	#(23)		uut_led_controller_clk12m5(
								.clk(clk_12m5),		//时钟信号
								.rst_n(sys_rst_n),	//复位信号,低电平有效
								.sled(led[0])		//LED指示灯接口	
							);

//-------------------------------------
//25MHz时钟进行分频闪烁,计数器为24位																
led_control	#(24)		uut_led_controller_clk25m(
								.clk(clk_25m),		//时钟信号
								.rst_n(sys_rst_n),	//复位信号,低电平有效
								.sled(led[1])		//LED指示灯接口	
							);
		
//-------------------------------------
//25MHz时钟进行分频闪烁,计数器为25位																
led_control	#(25)		uut_led_controller_clk50m(
								.clk(clk_50m),		//时钟信号
								.rst_n(sys_rst_n),	//复位信号,低电平有效
								.sled(led[2])		//LED指示灯接口	
							);
		
//-------------------------------------
//25MHz时钟进行分频闪烁,计数器为26位																
led_control	#(26)		uut_led_controller_clk100m(
								.clk(clk_100m),		//时钟信号
								.rst_n(sys_rst_n),	//复位信号,低电平有效
								.sled(led[3])		//LED指示灯接口	
							);		

//-------------------------------------							
//高4位LED指示灯关闭							
assign led[7:4] = 4'b1111;			
		
endmodule

关于这个代码我必须说一句话,是针对那个例化模块的LOCKED:

 pll_clk u1
   (// Clock in ports
    .CLK_IN1(ext_clk_25m),      // IN
    // Clock out ports
    .CLK_OUT1(clk_12m5),     // OUT
    .CLK_OUT2(clk_25m),     // OUT
    .CLK_OUT3(clk_50m),     // OUT
    .CLK_OUT4(clk_100m),     // OUT
    // Status and control signals
    .RESET(!ext_rst_n),// IN
    .LOCKED(sys_rst_n));      // OUT        

为什么用LOCKED这个管脚上输出的信号作为以下计数器的复位信号呢?

如果有这个疑问,那是你不了解这个LOCKED,这个信号在PLL输出信号稳定后,就变成了高电平,时钟不稳定时,就是低电平。

在输出时钟不稳定的时候,当然不能驱动计数器计数了,这时候利用这个低电平信号作为复位有效信号,使得计数器处于复位状态,然后等输出时钟稳定的时候,LOCKED信号变成了高电平了,这时候计数器也就不处于复位状态了,这个时候就可以正常计数了。

这样解释很好吧。


控制LED灯的子模块:

`timescale 1ns / 1ps

//单个LED闪烁
module led_control(
			input clk,		//时钟信号
			input rst_n,	//复位信号,低电平有效
			output sled		//LED指示灯接口	
		);													
	
parameter CNT_HIGH = 24;	//计数器最高位
//-------------------------------------
reg[(CNT_HIGH-1):0] cnt;		//24位计数器															

	//cnt计数器进行循环计数
always @ (posedge clk or negedge rst_n)									
	if(!rst_n) cnt <= 0;											
	else cnt <= cnt+1'b1;																		

assign sled = cnt[CNT_HIGH-1];	//cnt从0开始计数时,led灯一直是亮的,然后计数达到最大值,led灯就灭了	

endmodule

约束一下管脚,然后就可以进行综合,实现,然后下载到FPGA板子上观察实验现象。

实验很完美。

猜你喜欢

转载自blog.csdn.net/Reborn_Lee/article/details/81750031