在FPGA设计中,每个模块常常要用不同频率的时钟信号作为时钟输入端,但往往使用计数器设计的时钟精度不够,所产生的的误差在一次次的累加下会变得越来越大。使用锁相环IP核当然可以实现,但所需要的分频时钟多起来了,这时候的锁相环就不那么容易使用;并且针对某些时钟频率,锁相环也达不到那个要求。所以,分享一种原理方便,操作简单,并且精度还可以达到很高的一种分频方法。
原理
DDS原理我们在此不过多赘述,只需要了解DDS的一个核心公式即可。
——(1)
是所需要的时钟频率;
是开发板时钟或者叫系统时钟;
是频率控制字;
是所定义的计数器位宽。
知道了原理,那这个公式究竟是怎么用呢?
第一步:稍稍对公式做个变形:
——(2)
第二步:确定、
、
等参数;
第三步:依据公式计算频率控制字。
现在我们来演示一下:例如我们的板载晶振为50MHz,所需要的分频时钟为8.35MHz,计数器位宽我们选择48位(计数器位宽越大,精度越好;综合考虑来说,当计数器位宽为48位时,精度完全满足要求并且也不会占用特别大的资源)。
代入(2)式计算可得: = 47006321110680("小数部分大于5需要进位,由FPGA的特性决定")
此时将的值再代入(1)式中,可得
=8.3500000000000795807864051312208
可以看到精度逼近于小数点后13位,由此可见,理论上完全满足要求。
看到这里,有人会说,你这只是验证了时钟周期的精度,我最终的目的就是为了输出时钟频率那怎么办呢?简单的分两种情况,由特殊到一般性。
一、特殊情况,即50%的占空比:
①简单写法:取计数器的最高位直接输出;
② 通用写法:当计数器小于时,时钟输出端为0,否则为1。
二、一般情况,即任意占空比:
计数器大小,可以理解为周期;那么任意占空比就是给
乘以你要的占空比,小于时时钟输出为0,大于时时钟输出为1。
//**************************************** Message ***********************************//
//技术交流:folkore0118@163.com
//关注CSDN博主:“彼稷”
//Author: 彼稷
//All rights reserved
//----------------------------------------------------------------------------------------
// Target Devices: DELL-G15
// Tool Versions: vivado2021.2
// File name:
// Last modified Date: 2023年12月12日
// Last Version:
// Descriptions: 基于DDS原理的时钟分频
//----------------------------------------------------------------------------------------
//****************************************************************************************//
`timescale 1ns / 1ps
module CLK(
i_sys_clk ,
i_rst_n ,
o_clk
);
input i_sys_clk ;
input i_rst_n ;
output o_clk ;
localparam FRE_WROD = 48'd47006321110680;
reg [47:0] cnt_clk;
always@(posedge i_sys_clk or negedge i_rst_n)begin
if(!i_rst_n)
cnt_clk <= 48'd0;
else
cnt_clk <= cnt_clk + FRE_WROD;
end
assign o_clk = cnt_clk[47];
endmodule
由于计数器只在时钟上升沿有效,故会出现些许偏差,系统时钟频率越高,分频效果越好。
注意!!!
频率控制字一定要声明位宽,vivado中数据位宽默认是32位,若频率控制字位宽大于32位,则会出错。