Vivado 实现倍数含有小数的分频器设计

1 功能需求

        输入时钟 A 的频率是 25MHz ,产生一个400KHz的低频时钟 B 。

2  分频思路

        频率是时钟周期的倒数。

        时钟周期的单位是,秒/周期,即完成一个时钟需要多少秒。频率的单位是,周期/秒,即一秒内产生多少个时钟。

        频率越大的时钟,相同时间内产生的时钟越多。        

        输入时钟 A 的频率是 25MHz ,低频时钟 B 的频率是 400KHz 。说明相同时间内,A 的周期数是 B 的 25M/400K = 62.5 倍。也可以理解为 ,A 产生一个时钟,B 产生 1/62.5 个时钟。

        因此得到,A 产生 62.5 个时钟,B产生 1 个时钟。

3 分频方法

        由上得知,62.5 个时钟 A ,产生 1 个低频时钟 B ( 62.5 分频,即 1 个输入时钟 A ,产生 1/62.5 个低频时钟 B)。

        但电路无法产生 0.5 个时钟,因此将其扩大为:625 个时钟 A ,产生 10 个低频时钟 B 。

        使用 “”双模前置小数分频” ,取 62 (分频的整数部分)与 63 (分频的整数部分加 1 )作为两个分频周期,公式如下:

        解得 M = 5 ,N = 5 。

        说明需要 5 个 62 分频,5 个 63 分频,共 625 个输入时钟 A ,产生 10 个 低频时钟 B 。

        取公约数 5 进行化简:需要 1 个 62 分频,1 个 63 分频,共 125 个输入时钟 A ,产生 2 个 低频时钟 B 。

        在 62 分频和 63 分频内,各有 1 个 低频时钟 B 。

        在 62 分频中,前 31 分频对应低频时钟 B 的高电平,后 31 分频对应低频时钟 B 的低电平。在 63 分频中,前 31 分频对应低频时钟 B 的高电平,后 32 分频对应低频时钟 B 的低电平。如下图:

4 Verilog 代码

`timescale 1ns/1ps

module Divider(
    input   clk,                // 输入时钟 A ,频率是 25MHz
    input   rst_n,

    output  reg clk_400KHz          // 输出低频时钟 B ,频率是 400KHz
);

// 在 165 个输入时钟内输出 2 个低频时钟。
// 125 个输入时钟分成两个时钟周期,分别有 62 个时钟,63 个时钟。
// 62 个时钟的周期内,前 31 个时钟对应的低频时钟为高电平,后 31 个时钟对应的低频时钟为低电平。
// 63 个时钟的周期内,前 31 个时钟对应的低频时钟为高电平,后 32 个时钟对应的低频时钟为低电平。
// 以 165 个时钟作为大周期进行循环。

localparam div_62 = 8'd62;      // 前 62 个输入时钟的周期
localparam div_63 = 8'd63;      // 后 63 个输入时钟的周期
localparam div_165 = 8'd125;    // 总体 165 个输入时钟的大周期

reg [7:0] cnt_62;               // 前 62 个输入时钟的周期的计数器
reg [7:0] cnt_63;               // 后 63 个输入时钟的周期的计数器
reg [7:0] cnt;                  // 总体 165 个输入时钟的大周期的计数器




// 对三个计数器初始化及赋值。需要注意,所有计数器都是从 0 开始。
// 在前 62 个输入时钟的周期时,cnt_62 大于等于 0 ,小于等于 61 。cnt 大于等于 0 ,小于等于 61 。
// 在后 63 个输入时钟的周期时,cnt_63 大于等于 0 ,小于等于 62 。cnt 大于等于 62 ,小于等于 164 。

// 对前 62 个时钟周期的计数器 cnt_62 的初始化及赋值。 cnt_62 实际从 0 到 61 。
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_62 <= 8'b0;
    else begin 
        if (cnt_62 < div_62 - 1)               
            cnt_62 <= cnt_62 + 1'b1;
        else
            cnt_62 <= 8'd0;
    end
end

// 对后 63 个时钟周期的计数器 cnt_63 的初始化及赋值。 cnt_63 实际从 0 到 62 。
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_63 <= 8'b0;
    else begin
        if (cnt_63 < div_63 - 1)
            cnt_63 <= cnt_63 + 1'b1;
        else
            cnt_63 <= 8'd0;
    end
end

// 对 165 个时钟周期的计数器 cnt 初始化及赋值。 cnt 实际从 0 到 164 。
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt <= 8'd0;
    end
    else begin
        if(cnt < div_165 - 1)                      // 在前 62 个时钟周期,cnt 大于等于 0 ,小于等于 61 。
            cnt <= cnt + 1'b1;
        else
            cnt <= 8'd0;
    end
end



// 在 125 个输入时钟内,低频时钟变化为:高、低、高、低。产生两个低频时钟周期。
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        clk_400KHz <= 1'b0;
    else begin
        if(cnt < 8'd62) begin
            if(cnt_62 < 8'd31)
                clk_400KHz <= 1'b1;
            else
                clk_400KHz <= 1'b0;
        end
        else begin
            if(cnt_63 < 8'd31)
                clk_400KHz <= 1'b1;
            else
                clk_400KHz <= 1'b0;
        end
    end
end

endmodule

5 testbench 代码

// 声明仿真的单位和精度
`timescale 1ns / 1ps



// 定义测试模块
module Divider_tb;



// 宏定义
parameter  CLK_PERIOD = 40 ;            // 25MHz 的时钟周期为 40ns ( 1秒/25MHz=40纳秒 )



// 声明 tb 中的信号
// tb 中声明的信号中,reg 表示激励信号,wire 表示输出信号
reg clk;
reg rst_n;
wire clk_400KHz;



// 模块实例化
// 将 tb 中的激励信号和输出信号与待测的顶层模块连接
Divider Divider_0( .clk(clk), .rst_n(rst_n), .clk_400KHz(clk_400KHz) );



// 输入时钟信号和复位信号的初始化
initial begin
    // 输入时钟信号
    clk = 0;

    // 复位信号
    rst_n = 1;                         // 复位信号初始为高电平    
    #2;                                 // 开始后 2ns ,复位信号变低电平,复位一次
    rst_n = 0;                         
    #(CLK_PERIOD*3);                    // 复位后,经过 3 个 CLK_PERIOD 周期,复位信号恢复高电平,开始分频
    rst_n = 1;
end



// 时钟信号的产生
always #(CLK_PERIOD/2) clk = ~clk;         // 输入时钟每 20ns 跳变一次



// 测试激励产生
initial begin
    @(posedge clk);                     // 时钟上升沿激励有效
    @(posedge rst_n);                   // 等待复位完成

    repeat(1000) begin                    // 执行循环体中的语句 10 次               
        @(posedge clk);
    end

    #10_000;                            // 总共执行 10_000 ns 后结束仿真    
    $stop;
    
end

endmodule

6 仿真波形

        低频时钟周期为 2520000 ps ,即 396.83 KHz ,有误差。

        注意到低频时钟波形产生了毛刺。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值