verilog实现时钟的偶数与奇数分频

偶数分频

可以采用同步整数分频思路,使用摩尔状态机或者计数器(序列机)思想都可以,在这里提供一个同步7分频的摩尔状态机的实现思路:

module clk_divide(
    input   wire                    clk     ,
    input   wire                    rst_n   ,

    output  wire                    clk_o   
);
//==================1. 同步整数分频器====================//
//基于摩尔状态机实现7分频(一段式状态机) 同样可以实现偶数分频
localparam      S0      =       7'b0000_001     ,           //S0状态:clk_o输出0
                S1      =       7'b0000_010     ,           //S1状态:clk_o输出0
                S2      =       7'b0000_100     ,           //S2状态:clk_o输出0
                S3      =       7'b0001_000     ,           //S3状态:clk_o输出0
                S4      =       7'b0010_000     ,           //S4状态:clk_o输出1
                S5      =       7'b0100_000     ,           //S5状态:clk_o输出1
                S6      =       7'b1000_000     ;           //S1状态:clk_o输出1

reg     [6:0]       state   ;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        state <= S0;
        clk_o <= 1'b0;
    end
    else begin
        case(state)
            S0:     begin state <= S1; clk_o <= 1'b0; end
            S1:     begin state <= S2; clk_o <= 1'b0; end
            S2:     begin state <= S3; clk_o <= 1'b0; end
            S3:     begin state <= S4; clk_o <= 1'b0; end
            S4:     begin state <= S5; clk_o <= 1'b1; end
            S5:     begin state <= S6; clk_o <= 1'b1; end
            S6:     begin state <= S0; clk_o <= 1'b1; end
            default:begin state <= S0; clk_o <= 1'b1; end
        endcase
    end
end 

testbench如下所示:

`timescale 1ns/1ns

`define CLK_CYCLE 20
module tb_clk_divide;

    reg         clk     ;
    reg         rst_n   ;

    wire        clk_o   ;

    clk_divide u_clk_divide(
        .clk    (clk)   ,
        .rst_n  (rst_n) ,

        .clk_o  (clk_o)
    );

    initial begin
        clk = 1'b0;
        rst_n = 1'b0;
        #201;
        rst_n = 1'b1;
    end

    always #(`CLK_CYCLE / 2) clk = ~clk;

endmodule

仿真波形为:
在这里插入图片描述

奇数分频

方法一

主要思路如下:
首先基于时钟的上升沿和时钟的下降沿构造两个计数器,两个计数器都是技术到分频的最大值置0.之后分别给予两个计数器,声明两个变量即clk_div1和clk_div2。两者在各自的时钟沿下,若小于最大计数值的一半,则拉高信号。否则拉低信号。最后将这两个变量信号相或就可以得到最后的分频时钟啦。

module clk_divide(
    input   wire                    clk     ,
    input   wire                    rst_n   ,

    output  wire                    clk_o   
);
//==================2. 奇数分频器=======================//
//方法1
parameter       CLK_DIV     =   7   ;           //奇数分频的频数

reg         [3:0]       cnt_pos     ;           //上升沿计数器
reg         [3:0]       cnt_neg     ;           //下降沿计数器
reg                     clk_div1    ;           //分频信号1
reg                     clk_div2    ;           //分频信号2

always @(posedge clk or negedge rst_n) begin    //时钟上升沿触发
    if(!rst_n) begin
        cnt_pos <= 'd0;
    end
    else if(cnt_pos == CLK_DIV - 1'b1) begin    //计数最大值置0
        cnt_pos <= 'd0;
    end
    else
        cnt_pos <= cnt_pos + 1'b1;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_div1 <= 1'b0;
    end
    else if(cnt_pos < CLK_DIV / 2) begin        //小于计数值的一半,拉高clk_div1
        clk_div1 <= 1'b1;
    end
    else begin+
        clk_div1 <= 1'b0;
    end
end

always @(negedge clk or negedge rst_n) begin    //时钟下降沿触发
    if(!rst_n) begin
        cnt_neg <= 'd0;
    end
    else if(cnt_neg == CLK_DIV - 1) begin       //计数到最大值置0
        cnt_neg <= 'd0;
    end
    else begin
        cnt_neg <= cnt_neg + 1'b1;
    end
end

always @(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_div2 <= 1'b0;
    end
    else if(cnt_neg < CLK_DIV / 2) begin        //小于计数值的一半,拉高clk_div2
        clk_div2 <= 1'b1;
    end
    else begin
        clk_div2 <= 1'b0;
    end
end

assign clk_o = clk_div1 | clk_div2;             //将clk_div1和clk_div2或运算

仿真截图:
在这里插入图片描述

方法二

第二个方法是来自《硬件架构的艺术》上的方法,基于其思路,实现代码如下,代码中有详细的注释,便不在赘述了。

module clk_divide(
    input   wire                    clk     ,
    input   wire                    rst_n   ,

    output  wire                    clk_o   
);
//方法2(来自《硬件架构的艺术》)
//步骤1:创建由时钟上升沿触发的0-(N-1)的计数器,N为分频系数
parameter   N   =   5   ;
reg     [3:0]       cnt_up      ;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_up <= 'd0;
    end
    else if(cnt_up == N-1) begin
        cnt_up <= 'd0;
    end
    else begin
        cnt_up <= cnt_up + 1'b1;
    end
end

//步骤2:使用两个触发器,按照如下方式产生使能信号
//tff1_en:在计数值为0时使能
//tff2_en:在计数值为(N+1)/2时使能
wire         tff1_en     ;
wire         tff2_en     ;

assign tff1_en = (cnt_up == 0) ? 1'b1 : 1'b0;
assign tff2_en = (cnt_up == (N+1)/2) ? 1'b1 : 1'b0;

//步骤三:产生以下信号
//div1:TFF1的输出--->由输入时钟的上升沿触发
//div2:TFF2的输出--->由输入时钟的下降沿触发
reg     div1    ;
reg     div2    ;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        div1 <= 1'b0;
    end
    else if(tff1_en) begin
        div1 <= ~div1;
    end
    else begin
        div1 <= div1;
    end
end

always @(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
        div2 <= 1'b0;
    end
    else if(tff2_en) begin
        div2 <=~div2;
    end
    else begin
        div2 <= div2;
    end
end

//步骤四:将div1和div2信号异或
assign clk_o = div1 ^ div2;

endmodule

仿真如下:
在这里插入图片描述

全部代码

//时钟分频器
//2022/09/01
//======================================================//
//==================1. 同步整数分频器====================//
//==================2. 奇数分频器=======================//

module clk_divide(
    input   wire                    clk     ,
    input   wire                    rst_n   ,

    output  wire                    clk_o   
);
/*
//==================1. 同步整数分频器====================//
//基于摩尔状态机实现7分频(一段式状态机) 同样可以实现偶数分频
localparam      S0      =       7'b0000_001     ,           //S0状态:clk_o输出0
                S1      =       7'b0000_010     ,           //S1状态:clk_o输出0
                S2      =       7'b0000_100     ,           //S2状态:clk_o输出0
                S3      =       7'b0001_000     ,           //S3状态:clk_o输出0
                S4      =       7'b0010_000     ,           //S4状态:clk_o输出1
                S5      =       7'b0100_000     ,           //S5状态:clk_o输出1
                S6      =       7'b1000_000     ;           //S1状态:clk_o输出1

reg     [6:0]       state   ;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        state <= S0;
        clk_o <= 1'b0;
    end
    else begin
        case(state)
            S0:     begin state <= S1; clk_o <= 1'b0; end
            S1:     begin state <= S2; clk_o <= 1'b0; end
            S2:     begin state <= S3; clk_o <= 1'b0; end
            S3:     begin state <= S4; clk_o <= 1'b0; end
            S4:     begin state <= S5; clk_o <= 1'b1; end
            S5:     begin state <= S6; clk_o <= 1'b1; end
            S6:     begin state <= S0; clk_o <= 1'b1; end
            default:begin state <= S0; clk_o <= 1'b1; end
        endcase
    end
end 
*/
//==================2. 奇数分频器=======================//
/*
//方法1
parameter       CLK_DIV     =   7   ;           //奇数分频的频数

reg         [3:0]       cnt_pos     ;           //上升沿计数器
reg         [3:0]       cnt_neg     ;           //下降沿计数器
reg                     clk_div1    ;           //分频信号1
reg                     clk_div2    ;           //分频信号2

always @(posedge clk or negedge rst_n) begin    //时钟上升沿触发
    if(!rst_n) begin
        cnt_pos <= 'd0;
    end
    else if(cnt_pos == CLK_DIV - 1'b1) begin    //计数最大值置0
        cnt_pos <= 'd0;
    end
    else
        cnt_pos <= cnt_pos + 1'b1;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_div1 <= 1'b0;
    end
    else if(cnt_pos < CLK_DIV / 2) begin        //小于计数值的一半,拉高clk_div1
        clk_div1 <= 1'b1;
    end
    else begin+
        clk_div1 <= 1'b0;
    end
end

always @(negedge clk or negedge rst_n) begin    //时钟下降沿触发
    if(!rst_n) begin
        cnt_neg <= 'd0;
    end
    else if(cnt_neg == CLK_DIV - 1) begin       //计数到最大值置0
        cnt_neg <= 'd0;
    end
    else begin
        cnt_neg <= cnt_neg + 1'b1;
    end
end

always @(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_div2 <= 1'b0;
    end
    else if(cnt_neg < CLK_DIV / 2) begin        //小于计数值的一半,拉高clk_div2
        clk_div2 <= 1'b1;
    end
    else begin
        clk_div2 <= 1'b0;
    end
end

assign clk_o = clk_div1 | clk_div2;             //将clk_div1和clk_div2或运算
*/

//方法2(来自《硬件架构的艺术》)
//步骤1:创建由时钟上升沿触发的0-(N-1)的计数器,N为分频系数
parameter   N   =   5   ;
reg     [3:0]       cnt_up      ;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_up <= 'd0;
    end
    else if(cnt_up == N-1) begin
        cnt_up <= 'd0;
    end
    else begin
        cnt_up <= cnt_up + 1'b1;
    end
end

//步骤2:使用两个触发器,按照如下方式产生使能信号
//tff1_en:在计数值为0时使能
//tff2_en:在计数值为(N+1)/2时使能
wire         tff1_en     ;
wire         tff2_en     ;

assign tff1_en = (cnt_up == 0) ? 1'b1 : 1'b0;
assign tff2_en = (cnt_up == (N+1)/2) ? 1'b1 : 1'b0;

//步骤三:产生以下信号
//div1:TFF1的输出--->由输入时钟的上升沿触发
//div2:TFF2的输出--->由输入时钟的下降沿触发
reg     div1    ;
reg     div2    ;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        div1 <= 1'b0;
    end
    else if(tff1_en) begin
        div1 <= ~div1;
    end
    else begin
        div1 <= div1;
    end
end

always @(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
        div2 <= 1'b0;
    end
    else if(tff2_en) begin
        div2 <=~div2;
    end
    else begin
        div2 <= div2;
    end
end

//步骤四:将div1和div2信号异或
assign clk_o = div1 ^ div2;

endmodule

猜你喜欢

转载自blog.csdn.net/weixin_45614076/article/details/126650516