0
0
VerilogHow-ToBeginner · 4 min read

Verilog Code for Debouncer: Syntax, Example, and Tips

A debouncer in Verilog filters noisy button signals by sampling the input over time and confirming stability before changing output. Use a counter and state register to detect a stable input, as shown in the debouncer module example below.
📐

Syntax

The debouncer module typically has an input signal (noisy button), a clock, and a clean output signal. It uses a counter to measure how long the input stays stable before updating the output.

  • clk: Clock input to synchronize sampling.
  • btn_in: Noisy button input signal.
  • btn_out: Debounced clean output signal.
  • counter: Counts clock cycles while input is stable.
  • threshold: Number of cycles input must be stable to confirm.
verilog
module debouncer(
    input wire clk,
    input wire btn_in,
    output reg btn_out
);

    reg [19:0] counter = 0; // 20-bit counter for delay
    reg btn_sync_0 = 0, btn_sync_1 = 0;

    // Synchronize input to clock domain
    always @(posedge clk) begin
        btn_sync_0 <= btn_in;
        btn_sync_1 <= btn_sync_0;
    end

    always @(posedge clk) begin
        if (btn_sync_1 == btn_out) begin
            counter <= 0; // reset counter if input matches output
        end else begin
            counter <= counter + 1; // increment counter if input differs
            if (counter == 20'd1000000) begin // threshold for stable input
                btn_out <= btn_sync_1; // update output after stable period
                counter <= 0;
            end
        end
    end

endmodule
💻

Example

This example shows a debouncer module that cleans a noisy button input by waiting for the input to be stable for about 1 million clock cycles before changing the output. It uses a 20-bit counter and input synchronization to avoid glitches.

verilog
module testbench;
    reg clk = 0;
    reg btn_in = 0;
    wire btn_out;

    debouncer uut(
        .clk(clk),
        .btn_in(btn_in),
        .btn_out(btn_out)
    );

    // Clock generation: 10ns period
    always #5 clk = ~clk;

    initial begin
        // Simulate noisy button press
        btn_in = 0;
        #20 btn_in = 1; // button pressed
        #10 btn_in = 0; // noise
        #10 btn_in = 1; // stable press
        #10000000; // wait long enough for debounce
        #20 btn_in = 0; // button released
        #10000000; // wait for debounce
        $finish;
    end

    initial begin
        $monitor("Time=%0t btn_in=%b btn_out=%b", $time, btn_in, btn_out);
    end
endmodule
Output
Time=20 btn_in=1 btn_out=0 Time=1000020 btn_in=1 btn_out=1 Time=1000040 btn_in=0 btn_out=1 Time=2000040 btn_in=0 btn_out=0
⚠️

Common Pitfalls

Common mistakes when writing a debouncer include:

  • Not synchronizing the input signal to the clock domain, causing metastability.
  • Using too short or too long a counter threshold, leading to false triggers or slow response.
  • Not resetting the counter when input matches output, causing delayed updates.

Always synchronize inputs and choose a threshold based on your clock frequency and desired debounce time.

verilog
/* Wrong: No input synchronization and no counter reset */
module bad_debouncer(
    input wire clk,
    input wire btn_in,
    output reg btn_out
);
    reg [19:0] counter = 0;

    always @(posedge clk) begin
        if (btn_in != btn_out) begin
            counter <= counter + 1;
            if (counter == 20'd1000000) btn_out <= btn_in;
        end
    end
endmodule

/* Right: Synchronize input and reset counter */
module good_debouncer(
    input wire clk,
    input wire btn_in,
    output reg btn_out
);
    reg [19:0] counter = 0;
    reg btn_sync_0 = 0, btn_sync_1 = 0;

    always @(posedge clk) begin
        btn_sync_0 <= btn_in;
        btn_sync_1 <= btn_sync_0;
    end

    always @(posedge clk) begin
        if (btn_sync_1 == btn_out) counter <= 0;
        else begin
            counter <= counter + 1;
            if (counter == 20'd1000000) begin
                btn_out <= btn_sync_1;
                counter <= 0;
            end
        end
    end
endmodule
📊

Quick Reference

Tips for writing a Verilog debouncer:

  • Use a multi-stage synchronizer for input signals crossing clock domains.
  • Choose a counter width and threshold based on your clock frequency and debounce time (e.g., 10-20 ms).
  • Reset the counter whenever input matches output to avoid false triggers.
  • Test with noisy input signals to verify stability.

Key Takeaways

Synchronize noisy inputs to the clock domain before debouncing to avoid glitches.
Use a counter to confirm input stability over a set time before changing output.
Reset the counter when input matches output to ensure timely updates.
Choose debounce delay based on clock frequency and button characteristics.
Test your debouncer with noisy signals to ensure reliable output.