Verilog HDL:分频器设计

分频器的设计(Verilog HDL)

在fpga应用中,有时需要对系统时钟进行分频。通常情况下有两种情况,一种是偶分频,一种是奇分频。

偶分频

偶分频指的是分频系数是偶数的分频器。比如,假设输入时钟是100MHz,分频系数是2,则输出的方波频率为50MHz。
偶分频的实现比较简单。分频系数实际上代表的是输出信号的周期同输入信号的周期的倍数。因此,设分频系数为N,则每隔N/2个输入信号周期,输出信号电平发生一次反转,即可。
8分频的参考代码如下:

`timescale 1ns/10ps
module clk_div(
    input   wire            i_clk,      // 输入时钟
    input   wire            rst_n,      // 复位信号
    input   wire            en,         // 使能信号,低电平有效
    output  wire            o_clk       // 输出时钟
);

/* ================= 内部引线定义 ================= */
reg     [ 2:0]      cnt_q;
reg                 clk_q;
reg                 en_reg;
reg                 en_reg0;

assign o_clk = clk_q;

/* ================= 实例生成 ================= */
always @(posedge i_clk) begin
    en_reg0 <= en;
    en_reg <= en_reg0;
end
    
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
    if(~rst_n | ~en_reg) begin
        cnt_q <= 0;
    end
    else if(cnt_q == (8 - 1))
        cnt_q <= 0;
    else
        cnt_q <= cnt_q + 1;
end

always @(posedge i_clk or negedge rst_n or negedge en_reg) begin
    if(~rst_n | ~en_reg) begin
        clk_q <= 0;
    end
    else if((cnt_q == 0) || (cnt_q == (8 >> 1))) begin   // 计数到分频因子或分频因子的一半时,信号反转
        clk_q <= ~clk_q;
    end
    else begin  // 信号保持
        clk_q <= clk_q;
    end
end

endmodule

奇分频

奇分频的实现相对而言比较复杂,原因在于,如果只在输入信号的上升沿或下降沿处,触发电平反转,会导致输出信号的占空比不为50%。因此,需要采用两个计数器进行计数:

  • 一个在输入信号上升沿时计数,在 1 ∼ N − 1 2 1\sim\frac{N-1}{2} 12N1个时钟周期内输出一个电平,在 N + 1 2 ∼ N \frac{N+1}{2}\sim N 2N+1N个时钟周期内,输出反转后的电平。
  • 一个在输入信号下降沿时计数,在 1 ∼ N − 1 2 1\sim\frac{N-1}{2} 12N1个时钟周期内输出一个电平,在 N + 1 2 ∼ N \frac{N+1}{2}\sim N 2N+1N个时钟周期内,输出反转后的电平。

这计数器输出的信号的相位差,刚好差半个输出信号周期,将两个信号取或逻辑操作,即可得到占空比为50%的奇分频信号了。7细分的参考代码如下:

`timescale 1ns/10ps
module clk_div(
    input   wire            i_clk,      // 输入时钟
    input   wire            rst_n,      // 复位信号
    input   wire            en,         // 使能信号,低电平有效
    output  wire            o_clk       // 输出时钟
);

/* ================= 内部引线定义 ================= */
reg     [ 2:0]      cnt_q;
reg                 clk_neg;
reg                 clk_pos;
reg                 en_reg;
reg                 en_reg0;

assign o_clk = clk_neg | clk_pos;

/* ================= 实例生成 ================= */
always @(posedge i_clk) begin
    en_reg0 <= en;
    en_reg <= en_reg0;
end

always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
    if(~rst_n | ~en_reg) begin
        cnt_q <= 0;
    end
    else if(cnt_q == (7 - 1)) begin
        cnt_q <= 0;
    end
    else begin
        cnt_q <= cnt_q + 1;
    end
end

// 奇分频的思路是,相位相差90°,然后取或组合得到占空比为50%的方波
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin:posedge_trigger
    if(~rst_n | ~en_reg) begin
        clk_pos <= DEF_LEVEL;
    end
    else if((cnt_q == (7 - 1)) || (cnt_q == ((7 - 1) >> 1))) begin
        clk_pos <= ~clk_pos;
    end
    else begin
        clk_pos <= clk_pos;
    end
end

always@(negedge i_clk or negedge rst_n or negedge en_reg) begin:negedge_trigger
    if(~rst_n | ~en_reg) begin
        clk_neg <= DEF_LEVEL;
    end
    else if((cnt_q == (7 - 1)) || (cnt_q == ((7 - 1) >> 1))) begin
        clk_neg <= ~clk_neg;
    end
    else begin
        clk_neg <= clk_neg;
    end
end

通用分频器

基于偶分频和奇分频的原理,我们可以将二者整合在一起,得到一个通用的分频器模块,这样在调用的时候就无需考虑,是需要例化哪一种分频器了。只需要修改分频因子即可。

`timescale 1ns/10ps
module clk_div(
    input   wire            i_clk,      // 输入时钟
    input   wire            rst_n,      // 复位信号
    input   wire            en,         // 使能信号,低电平有效
    output  wire            o_clk       // 输出时钟
);

// 位宽求解函数
function integer bw_solve(input integer value);
begin
    if(value == 0)
        value = 1;
    else if(value != 0)
        for (bw_solve = 0; value > 0;bw_solve = bw_solve + 1)
            value = value >> 1;
end
endfunction

/* ================= 参数定义 ================= */
parameter integer DIV_FACT = 20;    // 细分因子
parameter integer DEF_LEVEL = 0;    // 初始电平

localparam integer BW = bw_solve(DIV_FACT); // 分频因子位宽
localparam integer ODD_OR_EVEN = DIV_FACT % 2;  // 奇偶分频判断,0为偶分频,1为奇分频

/* ================= 内部引线定义 ================= */
reg     [BW-1:0]    cnt_q;
reg                 clk_pos;
reg                 clk_neg;
reg                 clk_q;
reg                 en_reg;
reg                 en_reg0;

/* ================= 实例生成 ================= */
always @(posedge i_clk) begin
    en_reg0 <= en;
    en_reg <= en_reg0;
end

generate
    if(!ODD_OR_EVEN) begin   // 偶分频
        assign o_clk = clk_q;
    
        always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
            if(~rst_n | ~en_reg) begin
                cnt_q <= 0;
            end
            else if(cnt_q == (DIV_FACT - 1))
                cnt_q <= 0;
            else
                cnt_q <= cnt_q + 1;
        end

        always @(posedge i_clk or negedge rst_n or negedge en_reg) begin
            if(~rst_n | ~en_reg) begin
                clk_q <= DEF_LEVEL;
            end
            else if((cnt_q == 0) || (cnt_q == (DIV_FACT >> 1))) begin   // 计数到分频因子或分频因子的一半时,信号反转
                clk_q <= ~clk_q;
            end
            else begin  // 信号保持
                clk_q <= clk_q;
            end
        end
    end
    else begin  // 奇分频
        assign o_clk = clk_neg | clk_pos;
    
        always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
            if(~rst_n | ~en_reg) begin
                cnt_q <= 0;
            end
            else if(cnt_q == (DIV_FACT - 1)) begin
                cnt_q <= 0;
            end
            else begin
                cnt_q <= cnt_q + 1;
            end
        end

        // 奇分频的思路是,生成两个占空比是25%的方波,相位相差半个输入时钟周期,然后取或组合得到占空比为50%的方波
        always@(posedge i_clk or negedge rst_n or negedge en_reg) begin:posedge_trigger
            if(~rst_n | ~en_reg) begin
                clk_pos <= DEF_LEVEL;
            end
            else if((cnt_q == (DIV_FACT - 1)) || (cnt_q == ((DIV_FACT - 1) >> 1))) begin
                clk_pos <= ~clk_pos;
            end
            else begin
                clk_pos <= clk_pos;
            end
        end

        always@(negedge i_clk or negedge rst_n or negedge en_reg) begin:negedge_trigger
            if(~rst_n | ~en_reg) begin
                clk_neg <= DEF_LEVEL;
            end
            else if((cnt_q == (DIV_FACT - 1)) || (cnt_q == ((DIV_FACT - 1) >> 1))) begin
                clk_neg <= ~clk_neg;
            end
            else begin
                clk_neg <= clk_neg;
            end
        end
    end
endgenerate

endmodule

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

弄曲幽篁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值