A dual-edge counter is a counter that counts on both the rising and falling edges of the clock. Therefore, two counts are needed, namely a rising edge counter and a falling edge counter as auxiliary, as shown in the figure below:
It can be observed that a dual-edge counter can be obtained by adding the rising edge and falling edge counters. A more complicated situation is the setting condition of the two counters. Now suppose we make a double-edge counter with a maximum value of 17, as shown in the figure below:
We can see that when the two accelerators are added to the maximum value, one counter needs to be set to 0 and the other counter to be set to 1. If it is on the rising edge When it is detected that the sum is the maximum value, the rising edge counter is set to 0. Otherwise, the falling edge counter is set to 0. Therefore, it is necessary to declare two set 1 signals for these two counters. If the set 1 signal is high, the corresponding counter will be set to 1 in the next clock cycle. In addition, it needs to be explained that the maximum counting value of each of these two counters is half of the maximum counting value of both edges. The specific code is as follows, with detailed comments:
//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
The testbench is as follows:
`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
Simulation waveform diagram: