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 

テストベンチは次のようになります。

`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

シミュレーション波形は次のとおりです。
ここに画像の説明を挿入します

奇数分周

方法 1

主な考え方は次のとおりです:
まず、クロックの立ち上がりエッジとクロックの立ち下がりエッジに基づいて 2 つのカウンターが構築され、分周の最大値が設定されると両方のカウンターが 0 に設定されます。それぞれ与えられ、2 つの変数 clk_div1 と clk_div2 が宣言されます。2 つのクロック エッジが最大カウント値の半分未満の場合、信号は High になります。そうしないと、信号が低下します。最後に、これら 2 つの変数信号を OR 演算することで、最終的な分周クロックを取得できます。

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

シミュレーションのスクリーンショット:
ここに画像の説明を挿入します

方法 2

2 番目の方法は、『The Art of Hardware Architecture』から引用したもので、その考え方をもとに実装したコードは次のとおりです。コード内に詳細なコメントがあるので、詳細は省略します。

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