← All guides

Debouncing a button, properly

Wire a push-button straight into a counter and press it once: the counter jumps by five, or nine, or two. Nothing is wrong with your counter. A mechanical switch is two pieces of springy metal, and for a millisecond or so after contact they bounce, making and breaking the connection dozens of times. Your logic runs at nanoseconds; it faithfully counts every bounce as a press.

A button actually breaks your design twice, and the second way is sneakier: the press is asynchronous to your clock. Even a bounce-free signal can change inside a flip-flop’s setup/hold window, which is the metastability problem covered in the clock domain crossing guide. A proper debouncer therefore has two stages, and they aren’t interchangeable: synchronize first, then debounce.

The idea

After the synchronizer, the fix is one sentence: don’t believe a new level until it has held steady for long enough. A counter measures “steady”: any flicker back to the accepted level resets it, and only a full count changes your mind.

reg [3:0] count;
reg pressed;

// Require 16 stable cycles before believing the new level.
always @(posedge clk) begin
  if (btn_sync == pressed) count <= 4'd0;   // nothing to debounce
  else if (count == 4'd15) begin            // stable long enough
    pressed <= btn_sync;
    count   <= 4'd0;
  end else count <= count + 4'd1;
end

Live below. The schematic is worth reading for a moment: the counter’s adder, the comparator deciding “stable long enough”, and the pressed register that only updates when the comparator agrees. This is what “wait until it’s been quiet” looks like as hardware:

Interactive schematic by RapidRTL

The complete module

Synchronizer in front, counter behind, one clean level out:

Interactive schematic by RapidRTL

The order matters. Feed a bouncing, asynchronous signal directly into the counter’s comparator logic and different gates can momentarily disagree about what the input is (the CDC guide explains why); the counter then counts phantom events. The two synchronizer flops make the signal a clean citizen of your clock domain first, and the counter only ever sees stable, synchronous values.

Sizing the wait, honestly

The 4-bit counter here waits 16 cycles, chosen so the schematic above stays readable. Real switches bounce for 1 to 10 milliseconds, so size the counter from your clock: at 50 MHz, 10 ms is 500,000 cycles, a 19-bit counter. The structure doesn’t change at all; only the width and the compare value do, which is what a parameter is for:

parameter STABLE_CYCLES = 500_000;              // 10 ms at 50 MHz
localparam W = $clog2(STABLE_CYCLES);
reg [W-1:0] count;

If you’d rather not burn a wide counter per button, one divider can generate a ~1 kHz tick shared by every debouncer, each of which then counts a handful of ticks instead of half a million clocks.

From a level to an event

pressed is a clean level. Counters and state machines usually want a one-cycle pulse per press, which is one register and one gate away:

reg pressed_q;
always @(posedge clk) pressed_q <= pressed;
assign press_event = pressed & ~pressed_q;   // rising edge, one cycle

The pitfalls checklist