【牛客网verilog刷题】详细讲解了常见分频方法,以牛客网时序逻辑VL40、VL41、VL42题为例

1.序言

本文详细讲解了基于verilog的常见分频方法,并以牛客网时序逻辑部分VL40、VL41、VL42题为例。**注意:**使用自己编写的分频器时序性能并不好,对时序要求较高的话,建议使用ip核。

2.常见分频系数及实现

(1)偶数分频——以6分频电路为例

(A)分析
偶数(N)分频,直接使用一个N/2进制计数器(对应计数器状态为0~(N/2-1)),计数器记到最后一个状态,输出时钟状态反转。
(B)代码
以6分频电路为例。
代码:

module clk_divi_6(
    input       clk_in  ,
    input       rst_n   ,
    output  reg clk_out_6
    );
    //6/2=3进制计数器
    reg [1:0] cnt_3;
    always@(posedge clk_in or negedge rst_n)
        if(!rst_n)
            cnt_3 <= 2'd0;
        else if(cnt_3 == 2'd2)
            cnt_3 <= 2'd0;
        else
            cnt_3 <= cnt_3 + 1'b1;

    always@(posedge clk_in or negedge rst_n)
        if(!rst_n)
            clk_out_6 <= 1'b0;
        else if(cnt_3 == 2'd2)
            clk_out_6 <= !clk_out_6;
        else    
            clk_out_6 <= clk_out_6;
endmodule

testbench

`timescale 1ns / 1ps
module clk_divi_6_tb();

    reg     clk_in      ;
    reg     rst_n       ;
    wire    clk_out_6   ;
    //输入时钟周期20ns,即50MHz
    parameter T = 20;

    initial begin
        clk_in = 1'b0;
        rst_n = 1'b0;
        #(T);
        rst_n = 1'b1;
    end
    always#(T/2) clk_in = !(clk_in);
    clk_divi_6 u_clk_divi_6(
        .clk_in   (clk_in    ),
        .rst_n    (rst_n     ),
        .clk_out_6(clk_out_6 )
    );
endmodule

(C)仿真结果
在这里插入图片描述

(2)奇数分频——牛客网VL42(没有占空比要求)

(A)题目分析
以5分频为例,输出没有占空比要求,只要满足输出时钟周期为输入时钟周期的5倍即可。可以是3个时钟周期为高,两个时钟周期为低;也可以是4个时钟周期为高,1个时钟周期为低;
(B)例子牛客网VL42
注意:下图中波形示意图和题目要求不符合,波形示意图为占空比50%。虽然题目中说了占空比没有要求,但是正确答案是两个时钟周期为高,三个时钟周期为低的组合。
在这里插入图片描述
(C)代码

`timescale 1ns/1ns
module odd_div (    
    input     wire rst ,
    input     wire clk_in,
    output    wire clk_out5
);
    reg [2:0]  cnt_5;
    always@(posedge clk_in or negedge rst)begin
        if(!rst)
            cnt_5 <= 'd0;
        else if(cnt_5 == 3'd4)
            cnt_5 <= 'd0;
        else
            cnt_5 <= cnt_5 + 1'b1;
    end
    assign clk_out5 = ((3'd0<cnt_5)&& (cnt_5<3'd3))?1'b1:1'b0;
endmodule

(3)奇数分频——牛客网VL40(占空比50%)

A)分频分析
以7分频为例,占空比50%,输出3.5个时钟周期为高电平,3.5个时钟周期为低电平。常用的方法是输入时钟上升沿和下降沿联合检测。利用时钟上升沿检测(posedge clk)控制的变量clk_reg_pose保持三个时钟周期高电平,时钟下降沿检测(negedge clk)控制的变量clk_reg_nege保持三个时钟周期高电平,利用高电平的区域存在0.5个时钟周期的错位,相或输出,就得到了3.5个时钟高电平,3.5个时钟低电平的分频输出
在这里插入图片描述
B)题目描述
在这里插入图片描述

C)代码

`timescale 1ns/1ns
module odo_div_or
   (
    input    wire  rst ,
    input    wire  clk_in,
    output   wire  clk_out7
    );
    //7进制计数器
    reg [2:0] cnt_7;
    always@(posedge clk_in or negedge rst)begin
        if(!rst)
            cnt_7 <= 'd0;
        else if(cnt_7 == 3'd6)
            cnt_7 <= 'd0;
        else
            cnt_7 <= cnt_7 + 1'b1;
    end
    //上升沿触发状态指示信号
    reg clk_reg_pose;
    always@(posedge clk_in or negedge rst)begin
        if(!rst)
            clk_reg_pose <= 1'b0;
        else if(cnt_7 == 3'd3)
            clk_reg_pose <= 1'b1;
        else if(cnt_7 == 3'd6)
            clk_reg_pose <= 1'b0;
        else
            clk_reg_pose <= clk_reg_pose;
    end
    //下降沿触发状态指示信号
    reg clk_reg_nege;
    always@(negedge clk_in or negedge rst)begin
        if(!rst)
            clk_reg_nege <= 1'b0;
        else if(cnt_7 == 3'd3)
            clk_reg_nege <= 1'b1;
        else if(cnt_7 == 3'd6)
            clk_reg_nege <= 1'b0;
        else
            clk_reg_nege <= clk_reg_nege;
    end
    assign clk_out7 = clk_reg_pose|clk_reg_nege;
endmodule

(4)小数分频——牛客网VL41

(A)##############分频说明######################(很重要)
小数分频,比如8.7倍分频,无法实现输出的每一个时钟就是输入时钟的8.7分频。实际的实现方法是最小整数倍法,即8.7个输入时钟对应1个输出时钟,那么8.710=87个输入时钟对应110=10个输出时钟。所以只要保证输出时钟为10个,并不要求输出时钟周期完全一样。
因为是8.7分频,所以可以由8.7两边的整数倍分频构成:
a)8分频和9分频
设输出的10个时钟周期为X个8分频和Y个九分频,得到二元一次方程:
没个周期对应输入时钟周期数:
8X+9Y=87;
总的输出时钟周期数:
X+Y=10;
解得:X=3,Y=7
这种搭配并不是随意的,有要求:
1.在要求分频数8.7的两边;
2.上述两个二元一次方程有整数解;
3.也可以由3/4或者更多中分频方式构成。
(B)题目描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(C)题目解析
在这里插入图片描述
a)用一个87进制计数器进行时序控制;
87个状态,状态0~86;
b)前三个8分频输出:
先输出3个8分频输出,即对应计数器前24个状态;
输出相较于计数器延迟一拍,对应计数器1~24
在这里插入图片描述
c)后7个9分频输出:
后输出7个9分频输出,即对应计数器后63个状态;
(注意题目中的波形可以看出,9分频不是占空比50%,而是分成4和5;
输出相较于计数器延迟一拍,对应计数器25~86,0
在这里插入图片描述
(d)整合输出
将8分频和9分频输出相或,得到输出8.7分频时钟。
(D)代码

`timescale 1ns/1ns
module div_M_N(
 input  wire clk_in,
 input  wire rst,
 output wire clk_out
);
parameter M_N = 8'd87; 
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期

    //87进制计数器
    reg [6:0] cnt_87;
    always@(posedge clk_in or negedge rst)begin
        if(!rst)
            cnt_87 <= 'd0;
        else if(cnt_87 == 7'd86)
            cnt_87 <= 'd0;
        else
            cnt_87 <= cnt_87 + 1'b1;
    end
    //87进制计数器分为两段,1——24,25——86,0

    //前一段为3个8进制计数器
    reg [2:0] cnt_8;
    always@(posedge clk_in or negedge rst)begin
        if(!rst)
            cnt_8 <= 3'd0;
        else if(cnt_87 < 7'd24)
            if(cnt_8 == 3'd7)
                cnt_8 <= 3'd0;
            else
                cnt_8 <= cnt_8 +1'b1;
        else
            cnt_8 <= 'd0;
    end
    wire clk_8;
    assign clk_8 = ((3'd0<cnt_8)&&(cnt_8<3'd5))?1'b1:1'b0;
    //后一段为7个9进制计数器,占空比不是50%,分成4和5
    reg [3:0] cnt_9;
    always@(posedge clk_in or negedge rst)begin
        if(!rst)
            cnt_9 <= 'd0;
        else if(7'd24 <= cnt_87)
            if(cnt_9 == 4'd8)
                cnt_9 <= 'd0;
            else
                cnt_9 <= cnt_9 + 1'b1; 
        else
            cnt_9 <=4'b0;
    end
    //9分频占空比为50%
    // reg clk_9_pose;
    // always@(posedge clk_in or negedge rst)begin
    //     if(!rst)
    //         clk_9_pose <= 1'b0;
    //     else if(7'd24 <= cnt_87)
    //         if(cnt_9 == 'd0)
    //             clk_9_pose <= 1'b1;
    //         else if(cnt_9 == 4'd4)
    //             clk_9_pose <= 1'b0;
    //         else
    //             clk_9_pose <= clk_9_pose;
    //     else
    //         clk_9_pose <= 1'b0; 
    // end
    // reg clk_9_nege;
    // always@(negedge clk_in or negedge rst)begin
    //     if(!rst)
    //         clk_9_nege <= 1'b0;
    //     else if(7'd24 <= cnt_87)
    //         if(cnt_9 == 'd1)
    //             clk_9_nege <= 1'b1;
    //         else if(cnt_9 == 4'd5)
    //             clk_9_nege <= 1'b0;
    //         else
    //             clk_9_nege <= clk_9_nege;
    //     else
    //         clk_9_nege <= 1'b0; 
    // end
    wire clk_9;
    assign clk_9 = ((3'd0<cnt_9)&&(cnt_9<3'd5))?1'b1:1'b0;
    assign clk_out = clk_8|clk_9;

endmodule

(5)使用IP核实现分频

如果对时钟要求较高,建议使用IP核实现分频,后续会出时钟IP核的文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值