双边沿计数器verilog设计(详细说明)

双边沿计数器要在时钟的上升沿和下降沿都要计数的计数器。因此需要两个计数,即上升沿计数器和下降沿计数器作为辅助,如下图:
双边沿计数器verilog设计(详细说明)_第1张图片
可以观察到将上升沿和下降沿计数器相加就可以得到一个双边沿计数器。比较复杂的情况是两个计数器的置位条件。现在假设我们制作一个最大值为17的双边沿计数器,如下下图所示:双边沿计数器verilog设计(详细说明)_第2张图片
我们可以看出当两个加速器加到最大值时候,需要一个计数器置0,另一个计数器置1.若是在上升沿检测到加和为最大值,则将上升沿计数器置0。否则则将下降沿计数器置0。因此就需要为这两个计数器声明两个置1信号,如果置1信号为高时,则在下一个时钟周期将对应的计数器置为1。此外还需要说明的时,这两个计数器的各自的计数最大值,就是要双边沿计数最大值的一半即可。具体代码如下,有详细的注释:

//function:实现双边沿的计数器
//date:2022/08/31

module double_edge_cnt #(
    parameter   MAX         =   100    //计数的最大值   
)

(
    input   wire                clk     ,
    input   wire                rst_n   ,

    output  wire    [31:0]      d_cnt 
);

wire                even_flag       ;       //偶数标志
wire    [31:0]      HALF_MAX        ;

reg     [31:0]      cnt_posedge     ;       //上升沿计数器
reg     [31:0]      cnt_negedge     ;       //下降沿计数器
reg                 set_one_flag_pos;       //上升沿计数器置1信号
reg                 set_one_flas_neg;       //下降沿计数器置1信号

assign even_flag = (MAX[0] == 1'b0) ? 1'b1 : 1'b0;          //判断MAX是不是偶数
assign HALF_MAX = even_flag ? MAX >> 1 : (MAX >> 1) + 1'b1; //MAX一半

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_posedge <= 32'd0;
        set_one_flag_pos <= 1'b0;
    end
    else if((cnt_negedge + cnt_posedge == MAX)) begin       //上升沿检测到加和最大值,
        cnt_posedge <= 32'd0;
    end
    else if(cnt_negedge + cnt_posedge == MAX - 1'b1) begin  //上升沿检测到加和最大值减1,说明会在下降沿检测到最大值
        set_one_flag_pos <= 1'b1;                           //因此拉高置1信号
        cnt_posedge <= cnt_posedge + 1'b1;
    end
    else if(set_one_flag_pos) begin                         //置1信号为高,置位
        set_one_flag_pos <= 1'b0;
        cnt_posedge <= 32'd1;
    end
    else begin
        cnt_posedge <= cnt_posedge + 1'b1;
    end
end

always @(negedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_negedge <= 32'd0;
        set_one_flas_neg <= 1'b0;
    end
    else if(cnt_negedge + cnt_posedge == MAX) begin         //下降沿检测到最大值
        cnt_negedge <= 32'd0;
    end
    else if(cnt_negedge + cnt_posedge == MAX - 1'b1) begin  //下降沿检测到最大值减1,说明会在上升沿检测到最大值
        set_one_flas_neg <= 1'b1;                           //因此拉高下降沿计数器置1信号
        cnt_negedge <= cnt_negedge + 1'b1;
    end
    else if(set_one_flas_neg) begin                         //置1信号为高
        set_one_flas_neg <= 1'b0;
        cnt_negedge <= 32'd1;
    end
    else begin
        cnt_negedge <= cnt_negedge + 1'b1;
    end
end

//为双边沿计数器赋值
assign d_cnt = (((cnt_posedge == HALF_MAX) && (cnt_negedge == 'd0)) || ((cnt_posedge == 'd0) && (cnt_negedge == HALF_MAX))) ? 32'd0 : cnt_negedge + cnt_posedge; 

endmodule

testbench如下:

`timescale 1ns/1ns
`define CLK_CYCLE 20
module tb_double_edge_cnt;

    reg                 clk     ;
    reg                 rst_n   ;

    wire    [31:0]      d_cnt   ;

double_edge_cnt #
(
    99
)u_double_edge_cnt(
    .clk    (clk)   ,
    .rst_n  (rst_n) ,

    .d_cnt  (d_cnt)
);


initial begin
     clk = 1'b0;
     rst_n = 1'b0;
     #200
     rst_n = 1'b1;
     #40000;
     //$finish;
end

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


endmodule

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

你可能感兴趣的:(verilog实战,fpga开发)