Verilog implements even and odd frequency division of the clock

even frequency division

You can adopt the idea of ​​synchronous integer frequency division, and you can use Moore's state machine or counter (sequence machine) idea. Here is an implementation idea of ​​synchronous 7-frequency Moore's state machine:

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 

The testbench looks like this:

`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

The simulation waveform is:
Insert image description here

Odd frequency division

method one

The main idea is as follows:
First, two counters are constructed based on the rising edge of the clock and the falling edge of the clock. Both counters are set to 0 when the maximum value of the frequency division is set. Then two counters are given respectively and two variables are declared, clk_div1 and clk_div2. . If the two clock edges are less than half of the maximum count value, the signal will be pulled high. Otherwise, the signal will be lowered. Finally, the final divided clock can be obtained by ORing these two variable signals.

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或运算

Simulation screenshot:
Insert image description here

Method Two

The second method is from "The Art of Hardware Architecture". Based on its ideas, the implementation code is as follows. There are detailed comments in the code, so I won't go into details.

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

The simulation is as follows:
Insert image description here

All code

//时钟分频器
//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

Guess you like

Origin blog.csdn.net/weixin_45614076/article/details/126650516