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
- Debouncing without synchronizing. The counter itself must never sample a metastable input. Two flops first, always.
- Counting bounces as presses: any “count edges of the raw input” scheme is broken by design; count stability, not events.
- A wait that’s too short: if the LED still stutters, your switch bounces longer than your counter waits. Measure or size generously; 10 ms covers almost everything with a spring in it.
- Forgetting the edge detector and treating the held level as repeated presses, which reintroduces the original symptom with extra steps.