Electronic – Debounce circuit design in Verilog

digital-logicfpgartlsystem-verilogverilog

I'm trying to design a de-bouncer circuit , which is widely used in digital design . The module that I'm trying to implement is as shown below :-

enter image description here

I've written the following Verilog Module :-

`timescale 1ns / 1ps

module debounce_ckt(
input button,
input clk,
output reg result
    );

/************************************* Internal Variables **********************************/    
reg Q1;
reg Q2;
wire EN1 = 1'b1;
wire EN2 = 1'b1;
wire xor_out;
/****************************** Debounce ckt Implementation code ****************************************/

DFF FF1 (button,clk,EN1,Q1);
DFF FF2 (Q1,clk,EN2,Q2);
     
xor g1 (xor_out,Q1,Q2);

counter C1 (clk,xor_out,~Cout,Cout);     
DFF FF3 (Q2,clk,Cout,result);

endmodule


/***************************************** N-bit counter *************************************/
 module counter (clk, SCLR,EN,Cout);
     input clk;
     input SCLR;   // Clear of counter //
     input EN ; // Active 'HIGH' Enable //
     output reg [N-1:0] Cout; // Counter Output //
 
         
         // Time period of debounce ckt = T = (2^N + 2)/ f //
        // 'f' is the input clock frequency //
        // 'N' is the mod value of counter //

parameter N = 16;

            always@(posedge clk)
                    if(SCLR)    Cout <= 0;
                        else if (EN)    
                            begin
                                    if (Cout == N-1) 
                                            Cout <= 0;
                                    else
                                            Cout <= Cout + 1;
                            end
                    
endmodule       
 

/************************************* D Flip Flop Module (with Enable)**************************/

module DFF(input D,input clk,input EN ,output reg Q);

            always @(posedge clk or EN) 
                    begin
                        if(EN)
                                begin
                                   Q <= D; 
                                end 
                    end     
endmodule 

Testbench is as follows :-

`timescale 1ns / 1ps

module tb;

    // Inputs
    reg button;
    reg clk;

    // Outputs
    wire result;

    // Instantiate the Unit Under Test (UUT)
    debounce_ckt uut (
        .button(button), 
        .clk(clk), 
        .result(result)
    );

   initial begin
      clk = 1'b0; 
    end

always #5 clk = ~ clk;  

initial begin

        #12 button = 0;#10 button = 0 ; #10 button = 1 ; #10 button = 0 ;
        #12 button = 1;#10 button = 1 ; #10 button = 0 ; #10 button = 1 ;
        #12 button = 1;#10 button = 0 ; #10 button = 0 ; #10 button = 1 ;
        #12 button = 0;#10 button = 1 ; #10 button = 1 ; #10 button = 0 ;
        #10 $finish;
    end
  
endmodule


The output 'result' is going to 'X' don't care state, when I'm trying to simulate the files.

enter image description here

Can anyone point out where the issue lies and what corrections are to be made. I'm not expecting a whole working code in the answer. I'd just want to know the error in my code which is causing this.

Best Answer

Debouncer

This debouncer assumes that its input is synchronised to the clock.

The output will only change state when the input has been in the opposite state for N clock cycles, i.e. a form of hysteresis to produce a kind of low pass filter.

The counter only counts when the input and output differ, thus reducing switching losses when the input equals the output.

module Debounce
#(
    parameter MAX_COUNT = 16
)
(
    input wire clock,
    input wire in,    // Synchronous and noisy input.
    output reg out,   // Debounced and filtered output.
    output reg edj,   // Goes high for 1 clock cycle on either edge of output. Note: used "edj" because "edge" is a keyword.
    output reg rise,  // Goes high for 1 clock cycle on the rising edge of output.
    output reg fall   // Goes high for 1 clock cycle on the falling edge of output.
);

    localparam COUNTER_BITS = $clog2(MAX_COUNT);

    reg [COUNTER_BITS - 1 : 0] counter;
    wire w_edj;
    wire w_rise;
    wire w_fall;

    initial
    begin
        counter = 0;
        out = 0;
    end

    always @(posedge clock)
    begin
        counter <= 0;  // Freeze counter by default to reduce switching losses when input and output are equal.
        edj <= 0;
        rise <= 0;
        fall <= 0;
        if (counter == MAX_COUNT - 1)  // If successfully debounced, notify what happened.
        begin
            out <= in;
            edj <= w_edj;    // Goes high for 1 clock cycle on either edge.
            rise <= w_rise;  // Goes high for 1 clock cycle on the rising edge.
            fall <= w_fall;  // Goes high for 1 clock cycle on the falling edge.
        end
        else if (in != out)  // Hysteresis.
        begin
            counter <= counter + 1;  // Only increment when input and output differ.
        end
    end

    // Edge detect.
    assign w_edj = in ^ out;
    assign w_rise = in & ~out;
    assign w_fall = ~in & out;

endmodule

Synchroniser

All asynchronous inputs, such as buttons, need to be synchronised to the clock something like this:

module Sync
#(
    parameter SYNC_BITS = 3  // Number of bits in the synchronisation buffer (2 minimum).
)
(
    input wire clock,
    input wire in,     // Asynchronous input.
    output wire out    // Synchronous output.
);

    localparam SYNC_MSB = SYNC_BITS - 1;

    reg [SYNC_MSB : 0] sync_buffer;

    assign out = sync_buffer[SYNC_MSB];

    always @(posedge clock)
    begin
        sync_buffer[SYNC_MSB : 0] <= {sync_buffer[SYNC_MSB - 1 : 0], in};
    end

endmodule

The output of the synchroniser should be connected to the input of the debouncer.

Test Bench

I increased the button timings and reduced MAX_COUNT to see the debouncing effect.

`timescale 1ns/1ps

module SyncDebounce_TB;

    reg clock;
    reg button;
    wire button_sync;
    wire button_sync_db;
    wire edj;
    wire rise;
    wire fall;

    Sync Sync_Inst
    (
        .clock(clock),
        .in(button),
        .out(button_sync)
    );

    Debounce
    #(
        .MAX_COUNT(4)
    )
    Debounce_Inst
    (
        .clock(clock),
        .in(button_sync),
        .out(button_sync_db),
        .edj(edj),
        .rise(rise),
        .fall(fall)
    );

    initial
    begin
        clock = 0;
    end

    always #5 clock = ~clock;

    always
    begin
        #2 button = 0; #20 button = 1; #20 button = 0;
        #22 button = 1; #20 button = 1; #20 button = 0; #20 button = 1;
        #22 button = 1; #20 button = 0; #20 button = 0; #20 button = 1;
        #22 button = 0; #20 button = 1; #20 button = 1; #20 button = 0;
        #80 $stop;
    end

endmodule

Simulation

This shows the debounced button signal with edge detect (both, rise and fall).

It takes 3 clock cycles to synchronise the input and 4 clock cycles to debounce it.

Simulation of synchroniser and debouncer