0%

day5-verilog-button-debounce

20211018更新 後來發其實這樣的寫法有點問題
近期在玩DE1 verilog 程式設計,其中關於Button設計細節

  1. Debounce 機制
  2. 模組定義
  3. FSM 定義
  4. FSM 實作

20211018更新
在FSM 實作的部分我把count也加進去output logic,
如此不妥,
1是output邏輯汙染不再只是輸出,
2是讓output邏輯無法以@(*)輸出,造成clk delay
可以改為使用timer倒數, 在特定state enable, 直到倒數完成送個wire訊號回來跳state

Debounce 機制

簡單來說就是避免Button在按壓時的彈跳
預期的按壓訊號(button通常是low active):

───┐                              ┌───────  
└──────────────────────────────┘

然而,實際可能的按壓訊號:

───┐   ┌─┐   ┌─────────┐   ┌──┐   ┌───┐                    
└───┘ └───┘ └───┘ └───┘ └──────────────────

所以我們count那些可能錯誤的low active,超過一定數字就認定為使用者真的按下按鈕

模組定義

先來看一下模組定義

module btn_debounce
#(
parameter COUNT_NUM = 5000 // count次數
)
(
input iClk, // 外部頻率
input iRst_n, // reset訊號
input iBtn_n, // 按壓訊號
output reg oBtn, // 輸出訊號
);
  • count次數 : 監測可能錯誤的low active次數,數值太小會無法濾掉錯誤的按壓,數值太大按鈕反應太慢
  • 外部頻率 : 系統頻率,按鈕的反應時間以這個為準,例如使用50MHz,反應時間就是 20ns*5000 = 100us
  • reset訊號 : 系統重置訊號,若收到該訊號,要立刻讓按鈕停止輸出、數數等動作
  • 按壓訊號 : 來自真實按鈕的按壓訊號,帶有雜訊
  • 輸出訊號 : 經過debounce輸出的訊號,應該要乾淨、單一

FSM 定義

為了使此模組更好理解,此處使用 Moore FSM,定義四個狀態:
( Mealy 可以在轉state時輸出
參考 (原創) 有限狀態機FSM coding style整理 (SOC) (Verilog))

localparam IDLE             = 0; // wait for button press
localparam PRESS = 1; // check button press, start count, fire after enough count
localparam FIRE = 2; // fire signal and wait for btn release
localparam RELEASE = 3; // check button release, start count, back to WAIT_PRESS after enough count

// Moore FSM
reg [1:0] curr_state = IDLE;
reg [1:0] next_state;
reg [25:0] count;

並且定義目前狀態以及未來狀態, 以及定義了一個多bit的count register,作為count用

FSM 實作

共分三個部分:

  1. next state logic 根據 目前state 以及 外部輸入 判斷下一個 state
  2. state reg 根據 clk 跳至下一個 state 以及實作 reset
  3. output logic 根據目 state 輸出

架構如下:

           ┌────────────────┐     ┌──────────────┐    ┌────────────┐
input ────►│ ├────►│ │ │ │
│Next State logic│ │State register├─┬─►│Output Logic├────► output
┌─►│ │ ┌──►│ │ │ │ │
│ └────────────────┘ │ └──────────────┘ │ └────────────┘
clock ──┼─────────────────────┘ │
└──────────────────────────────────────────┘
// Moore FSM: next state logic
always @(*) begin
case (curr_state)
IDLE: next_state = (!iBtn_n) ? PRESS : IDLE;
PRESS: next_state = (count > COUNT_NUM - 1) ? FIRE : PRESS ;
FIRE: next_state = RELEASE;
RELEASE: next_state = (count > COUNT_NUM - 1) ? IDLE : RELEASE ;
default: next_state = IDLE;
endcase
end

// Moore FSM: state reg
always @(posedge iClk or negedge iRst_n)
curr_state <= (!iRst_n) ? IDLE : next_state;

// Moore FSM: output logic
always @(posedge iClk) begin
case (curr_state)
IDLE: begin
count <= 0;
oBtn <= 0;
end

PRESS: begin
oBtn <= 0;
count <= (!iBtn_n) ? count + 1 : 25'b0;
end

FIRE: begin
count <= 0;
oBtn <= 1;
end

RELEASE: begin
oBtn <= 0;
count <= (iBtn_n) ? count + 1 : 25'b0;
end
endcase
end

程式碼實作:

  1. IDLE 狀態: 等待按下按鈕,將counter清空
  2. PRESS 狀態: 確認以按下按鈕,開始count
  3. FIRE 狀態: 完成count,送出訊號
  4. RELEASE 狀態: 計算放開時count,放開足夠久就回到IDLE

注意

  1. 對於有試著將output logic設計為 always@(*) 但會引來 Error: (vsim-3601)
    其因Sensitive List已經與內部邏輯成為迴圈
  2. 此處設計State為一般版本,如果設計成one hot會更好移轉以及Debug

完整檔案