Project 9: Button Debouncer
Filter noisy button presses into clean pulses using synchronizers and counters.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Intermediate |
| Time Estimate | 4-5 hours |
| Main Programming Language | Verilog (Alternatives: VHDL, SystemVerilog) |
| Alternative Programming Languages | VHDL, SystemVerilog |
| Coolness Level | Medium |
| Business Potential | Medium |
| Prerequisites | Flip-flops, Counters, Metastability basics |
| Key Topics | Synchronizers, Debouncing, Edge detection |
1. Learning Objectives
- Protect against metastability
- Filter bounce noise
- Generate single-cycle pulses
2. All Theory Needed (Per-Concept Breakdown)
Debouncing and Synchronizers
Description/Expanded Explanation of the concept
Mechanical buttons bounce: they produce multiple rapid transitions on a single press. Also, a button is asynchronous to your clock, so it can cause metastability. A synchronizer chain plus a stability counter creates a clean, single transition.
Definitions & Key Terms
- Bounce -> rapid oscillation at a switch transition
- Synchronizer -> chain of flip-flops to reduce metastability
- Stable window -> required time with no changes
Mental Model Diagram (ASCII)
raw_btn -> FF -> FF -> stable_counter -> clean_btn

How It Works (Step-by-Step)
- Synchronize the raw input with two flip-flops.
- Count how long the input stays the same.
- Once stable for N cycles, update the clean signal.
Minimal Concrete Example
if (sync_btn == stable_btn) count <= 0;
else count <= count + 1;
Common Misconceptions
- “A button is just a logic input.” -> It is noisy and asynchronous.
- “One flip-flop is enough.” -> Two are standard for safety.
Check-Your-Understanding Questions
- Why is metastability a risk with buttons?
- What is the trade-off between stability time and responsiveness?
- Why not debounce in software on an FPGA-only design?
Where You’ll Apply It
- This project: used in Section 3.2 and Section 5
- Also used in: P19-calculator-with-7-segment-display.md, P20-pong-game-on-vga.md
Counters and Clock Enables
Description/Expanded Explanation of the concept
Counters are registers that increment or decrement each clock cycle. Clock enables allow you to update a register only when needed, which is safer than gating clocks. They are essential for timing, PWM, and pacing visible outputs.
Definitions & Key Terms
- Counter -> register that increments/decrements
- Clock enable -> condition that allows a register update
- Prescaler -> counter used to slow down a signal
Mental Model Diagram (ASCII)
clk -> [counter] --tick--> [slow logic]

How It Works (Step-by-Step)
- Increment counter each clock edge.
- When counter reaches a limit, assert a tick.
- Use tick as a clock enable for slower logic.
Minimal Concrete Example
if (tick) out <= out + 1;
Common Misconceptions
- “Gating the clock is simpler.” -> It can break timing and glitch.
- “Counters are always free.” -> Wide counters consume resources.
Check-Your-Understanding Questions
- Why use clock enables instead of gating clocks?
- How do you choose counter width for a target frequency?
- What happens when a counter overflows?
Where You’ll Apply It
- This project: used in Section 3.2 and Section 5
- Also used in: P08-shift-register-led-chaser.md, P10-pwm-generator-led-dimmer-servo-control.md
Verification with Testbenches and Waveforms
Description/Expanded Explanation of the concept
Testbenches are simulation-only modules that apply stimulus and check outputs. Waveforms (VCD) are the hardware engineer’s microscope; they reveal timing, glitches, and ordering problems. A good testbench is deterministic and covers edge cases.
Definitions & Key Terms
- Testbench -> a non-synthesizable module that drives a DUT
- VCD -> Value Change Dump waveform file
- Deterministic test -> same inputs produce same outputs every run
Mental Model Diagram (ASCII)
[Testbench] -> [DUT] -> [VCD] -> [GTKWave]

How It Works (Step-by-Step)
- Initialize inputs to known values.
- Apply stimulus over time.
- Dump waveforms and check outputs.
- Add assertions or PASS/FAIL messages.
Minimal Concrete Example
initial begin
$dumpfile("wave.vcd");
$dumpvars(0, tb);
a = 0; b = 1; #10;
$finish;
end
Common Misconceptions
- “If it simulates once, it’s correct.” -> Cover all relevant cases.
- “Waveforms are optional.” -> They are often the only way to debug timing.
Check-Your-Understanding Questions
- Why keep testbench and DUT separate?
- What is the purpose of
$dumpvars? - How do you make a testbench deterministic?
Where You’ll Apply It
- This project: used throughout Section 6 (testing)
- Also used in: all other projects in this folder
3. Project Specification
3.1 What You Will Build
A debouncer that outputs a clean button press signal.
3.2 Functional Requirements
- Requirement 1: Synchronize raw button input
- Requirement 2: Require stability for N cycles
- Requirement 3: Output a single clean pulse
3.3 Non-Functional Requirements
- Performance: Stable operation at the target clock and interfaces.
- Reliability: Deterministic outputs on all defined inputs.
- Usability: Clear ports and documented behavior.
3.4 Example Usage / Output
{p['example_usage']}
3.5 Data Formats / Schemas / Protocols
{p[‘data_format’]}
3.6 Edge Cases
- Very short press
- Long press
3.7 Real World Outcome
3.7.1 How to Run (Copy/Paste)
vvp debounce_tb
3.7.2 Golden Path Demo (Deterministic)
Run the demo command above with the provided testbench and confirm the outputs match the golden transcript.
3.7.3 CLI Transcript
raw:10111010001111
clean:10000000000001
3.7.4 Failure Demo (Expected)
# Example failure case
ERROR: Output mismatch at vector 3
Expected: 0x0A, Got: 0x0B
EXIT CODE: 1
Notes:
- Exit code 0 indicates all tests passed
- Exit code 1 indicates a test failure
4. Solution Architecture
4.1 High-Level Design
[inputs] -> [core logic] -> [outputs]

4.2 Key Components
| Component | Responsibility |
|---|---|
| sync | 2-flop synchronizer |
| debounce | Counter-based filter |
4.3 Data Structures (No Full Code)
// Example signals (adapt to your design)
reg [7:0] state_reg;
reg [7:0] data_reg;
4.4 Algorithm Overview
Key Algorithm: Core control flow
- Initialize state/reset conditions.
- Apply inputs and compute outputs.
- Update state on clock edges (if sequential).
Complexity Analysis:
- Time: O(1) per cycle
- Space: O(N) for registers and logic
5. Implementation Guide
5.1 Development Environment Setup
iverilog -v
# Ensure GTKWave is installed for waveform viewing
5.2 Project Structure
project-root/
|-- src/
| |-- top.v
| |-- core.v
|-- tb/
| |-- tb.v
|-- Makefile
|-- README.md

5.3 The Core Question You’re Answering
“How do you filter real-world noise before it becomes state?”
5.4 Concepts You Must Understand First
- Flip-flops
- Counters
- Metastability basics
5.5 Questions to Guide Your Design
- How many cycles define stability?
- Should output be level or pulse?
5.6 Thinking Exercise
Sketch a noisy waveform and mark the clean transition.
5.7 The Interview Questions They’ll Ask
- Why is a synchronizer required?
- How do you balance latency vs noise filtering?
5.8 Hints in Layers
- Synchronize first, debounce second.
- Use a counter for stability time.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Timing and synchronizers | Digital Design and Computer Architecture | Ch. 3 |
5.10 Implementation Phases
Phase 1: Foundation
Goals:
- Establish core module structure
- Implement minimal behavior
Tasks:
- Scaffold module ports and internal signals
- Write a minimal testbench that compiles
Checkpoint: Simulation runs without errors
Phase 2: Core Functionality
Goals:
- Implement full logic
- Verify edge cases
Tasks:
- Complete core logic
- Add directed tests for edge cases
Checkpoint: All tests pass and waveforms match expectations
Phase 3: Polish & Edge Cases
Goals:
- Improve readability
- Document behavior
Tasks:
- Add comments and README notes
- Expand tests for unusual inputs
Checkpoint: Design is deterministic and documented
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Reset strategy | Sync / Async | Sync | Simpler timing closure |
| Test coverage | Directed / Exhaustive | Exhaustive for small logic | Prevents missed cases |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit Tests | Test core logic | Small vectors |
| Integration Tests | Test modules together | Full system |
| Edge Case Tests | Boundary conditions | Max/min values |
6.2 Critical Test Cases
- Test 1: Press and release with noise patterns
- Test 2: Ensure one pulse per press
6.3 Test Data
Use deterministic vectors and document expected outputs.
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Multiple pulses | Repeated triggers | Edge detect only debounced signal |
7.2 Debugging Strategies
- Inspect waveforms at key internal signals
- Add temporary debug outputs to verify state
- Reduce testcases to the smallest failing case
7.3 Performance Traps
- Overly wide counters or combinational paths can reduce max clock
8. Extensions & Challenges
8.1 Beginner Extensions
- Add parameterization for widths
- Add optional features (enable, reset)
8.2 Intermediate Extensions
- Add configuration registers
- Build a simple driver or demo program
8.3 Advanced Extensions
- Integrate with another project in this series
- Implement a hardware demo on FPGA
9. Real-World Connections
9.1 Industry Applications
- Digital control systems and embedded peripherals
- FPGA prototyping and validation
9.2 Related Open Source Projects
- Yosys / nextpnr toolchain for open-source FPGA flow
- Example HDL projects in the FPGA community
9.3 Interview Relevance
- Demonstrates RTL thinking and verification skills
10. Resources
10.1 Essential Reading
- Digital Design and Computer Architecture - Focus on Ch. 3
10.2 Video Resources
- Search for project-specific HDL walkthroughs and waveforms
10.3 Tools & Documentation
- Icarus Verilog
- GTKWave
10.4 Related Projects in This Series
- See adjacent projects in
VERILOG_FROM_ZERO_PROJECTS/
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the core concept without notes
- I can predict waveform behavior for basic inputs
11.2 Implementation
- All functional requirements are met
- All tests pass
- Edge cases are documented
11.3 Growth
- I can explain this project in an interview
- I documented at least one lesson learned
12. Submission / Completion Criteria
Minimum Viable Completion:
- Functional requirements implemented
- Testbench passes
- Waveforms inspected
Full Completion:
- All minimum criteria plus
- Edge cases covered and documented
Excellence (Going Above & Beyond):
- Hardware demo on FPGA
- Clear write-up of lessons learned
Appendix A: Deep Dive Walkthrough
A.1 Signal Map and Invariants
- Inputs:
clk,reset,btn_raw - Outputs:
btn_clean,btn_pulse(optional)
Invariant: btn_clean only changes after the input is stable for N cycles.
A.2 Debounce Pipeline
- Synchronize
btn_rawwith two flip-flops. - Count stable cycles.
- Update
btn_cleanwhen the counter reaches threshold.
A.3 Bounce Example
raw: 0 1 0 1 1 0 1 1 1 1
clean: 0 0 0 0 0 0 0 1 1 1 (after N stable cycles)

A.4 Parameter Selection
- For a 50 MHz clock and 5 ms debounce:
- threshold = 0.005 * 50,000,000 = 250,000 cycles
A.5 Deterministic Test Sequence
- Inject a bouncing pattern then hold high for > N cycles.
- Verify
btn_cleantoggles exactly once. - Verify no toggle if bounce never stabilizes.
13. Deep Dive Appendix
13.1 Timing and Resource Budget
- Debouncing is about time filtering: require stable input for N samples.
- Typical sample rates are 1-5 kHz, and stable time is 5-20 ms.
- The logic is small, but the counter width depends on your clock frequency.
13.2 Waveform Interpretation Guide
- Compare raw_btn (noisy) vs debounced (clean).
- Ensure the output changes once per press and does not chatter.
- Verify the synchronizer stages if you sample async inputs.
Example:
raw: 1010011100111
deb: 0000001111111
13.3 Hardware Bring-Up Notes
- Use internal pull-ups or external resistors to avoid floating inputs.
- Mechanical buttons bounce; measure on a scope if you can.
- If your button is wired to ground, idle should read 1 with pull-up.
13.4 Alternate Implementations and Trade-offs
- Counter-based: increment while stable, change output at threshold.
- FSM-based: explicit states for idle, detect, confirm, stable.
- RC + Schmitt trigger: analog debouncing before digital sampling.
13.5 Additional Exercises
- Add long-press detection (press held > 1s).
- Add double-click detection (two presses within 300 ms).
- Debounce a matrix keypad and detect multiple keys.