Un contador de doble filo es un contador que cuenta tanto con los flancos ascendentes como descendentes del reloj. Por lo tanto, se necesitan dos conteos, es decir, el contador de flanco ascendente y el contador de flanco descendente son auxiliares, como se muestra en la siguiente figura:
Se puede observar que se puede obtener un contador de doble flanco sumando los contadores de flanco ascendente y descendente . Un caso más complicado es el estado establecido de los dos contadores. Ahora supongamos que hacemos un contador de doble filo con un valor máximo de 17, como se muestra en la siguiente figura:
Podemos ver que cuando los dos aceleradores se suman al valor máximo, un contador debe establecerse en 0 y el otro. El contador se establece en 1. Si está en el flanco ascendente. Cuando se detecta que la suma es el valor máximo, el contador del flanco ascendente se establece en 0. De lo contrario, el contador de flanco descendente se establece en 0. Por lo tanto, es necesario declarar dos señales de configuración 1 para estos dos contadores. Si la señal de configuración 1 es alta, el contador correspondiente se establece en 1 en el siguiente ciclo de reloj. Además, es necesario explicar que los respectivos recuentos máximos de los dos contadores son sólo la mitad de los recuentos máximos de los dos bordes. El código específico es el siguiente, con comentarios detallados:
//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
El banco de pruebas es el siguiente:
`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
Diagrama de forma de onda de simulación: