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

  1. Protect against metastability
  2. Filter bounce noise
  3. 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

Debounce pipeline

How It Works (Step-by-Step)
  1. Synchronize the raw input with two flip-flops.
  2. Count how long the input stays the same.
  3. 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
  1. Why is metastability a risk with buttons?
  2. What is the trade-off between stability time and responsiveness?
  3. 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]

Clock divider tick pipeline

How It Works (Step-by-Step)
  1. Increment counter each clock edge.
  2. When counter reaches a limit, assert a tick.
  3. 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
  1. Why use clock enables instead of gating clocks?
  2. How do you choose counter width for a target frequency?
  3. 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]

Testbench DUT VCD GTKWave flow

How It Works (Step-by-Step)
  1. Initialize inputs to known values.
  2. Apply stimulus over time.
  3. Dump waveforms and check outputs.
  4. 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
  1. Why keep testbench and DUT separate?
  2. What is the purpose of $dumpvars?
  3. 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

  1. Requirement 1: Synchronize raw button input
  2. Requirement 2: Require stability for N cycles
  3. 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]

Core logic flow

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

  1. Initialize state/reset conditions.
  2. Apply inputs and compute outputs.
  3. 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

Project folder structure

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:

  1. Scaffold module ports and internal signals
  2. Write a minimal testbench that compiles

Checkpoint: Simulation runs without errors

Phase 2: Core Functionality

Goals:

  • Implement full logic
  • Verify edge cases

Tasks:

  1. Complete core logic
  2. 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:

  1. Add comments and README notes
  2. 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

  1. Test 1: Press and release with noise patterns
  2. 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
  • 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
  • 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

  1. Synchronize btn_raw with two flip-flops.
  2. Count stable cycles.
  3. Update btn_clean when 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)

Button debounce waveform

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_clean toggles 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.