버튼을 누르면 물리적인 접점 떨림(bounce) 때문에 짧은 시간 동안 HIGH/LOW가 수차례 튀게 됩니다. 이로 인해 버튼을 한 번 눌러도 여러 번 눌렸다고 오인할 수 있습니다.

실제 버튼을 눌렀을 때 bounce가 일어나는 시간은 대략 50~100μs라고 가정해보겠습니다. 이때 버튼 신호는 다음처럼 불안정하게 튀는(high/low 반복) 형태입니다.

시간 (μs) 0 10 20 30 40 50 60 70 80 90
버튼(i_btn) 0 1 0 1 0 1 1 1 1 1

📍 여기서 구간을 나눠보면:

✅ 클럭 주파수에 따른 샘플링

📍(1) 100MHz 클럭으로 샘플링 (10ns 간격)

📍(2) 100KHz 클럭으로 샘플링 (10μs 간격)

노이즈를 무시하고 안정적인 눌림만 감지 가능

ℹ️ Verilog Code

i_btn는 짧은 시간 동안 튀는 노이즈가 섞일 수 있는 외부 버튼 입력입니다. o_btn은 디바운싱을 통과한 후, 처음 눌릴 때만 1을 출력하는 펄스 신호입니다.

module btn_debounce (
    input clk,      // 100MHz 시스템 클럭
    input rst,      // 비동기 리셋
    input i_btn,    // 버튼 입력 (노이즈 가능성 있음)
    output o_btn    // 디바운싱 후 rising edge만 출력 (펄스)
);

아래 always 블록은 100MHz 입력 클럭을 1000으로 나눠서 100KHz 펄스 (r_clk) 생성하는 클럭 분주기 코드입니다. r_clk10μs마다 1클럭 동안만 1이 되는 펄스이고 이 펄스로 버튼 입력을 일정 간격으로 샘플링 할 수 있습니다.

parameter F_COUNT = 1000;
reg [$clog2(F_COUNT)-1:0] r_counter;
reg r_clk;

always @(posedge clk, posedge rst) begin
    if (rst) begin
        r_counter <= 0;
        r_clk <= 0;
    end else begin
        if (r_counter == (F_COUNT - 1)) begin
            r_counter <= 0;
            r_clk <= 1'b1;   // 펄스 발생
        end else begin
            r_counter <= r_counter + 1;
            r_clk <= 1'b0;   // 평소에는 0
        end
    end
end

아래 코드는 버튼 디바운싱 로직의 핵심, 즉 시프트 레지스터(shift register)를 이용한 입력 샘플링 및 필터링을 구현한 부분입니다. q_reg는 버튼 입력을 8비트로 저장하는 시프트 레지스터이고 q_next는다음 클럭에서 q_reg에 저장될 값입니다.