Input
Button debouncer
debounce
The input passes a two-flop synchronizer; the output follows it only after it has held one value for STABLE_CYCLES clock cycles, so contact bounce and short glitches are ignored. rise and fall pulse for one cycle when the output changes.
Instantiate it
Paste into your design and connect the signals.
debounce #(.STABLE_CYCLES(1000000)) u_button (
.clk(clk), .rst(rst),
.noisy(button_pin), .clean(button), .rise(button_pressed), .fall(button_released)
);Parameters
| Name | Default | Meaning |
|---|---|---|
| STABLE_CYCLES | 1000000 | Cycles the input must hold before the output changes (1,000,000 is 10 ms at 100 MHz). |
Ports
| Name | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | Clock; everything changes on its rising edge. |
| rst | input | 1 | Synchronous reset, active high. |
| noisy | input | 1 | Button or switch input (asynchronous). |
| clean | output | 1 | Debounced level. |
| rise | output | 1 | Pulses when clean goes high (a press). |
| fall | output | 1 | Pulses when clean goes low (a release). |
Add it to a project
It is copied to ip/debounce/debounce.sv with a README (ip/debounce/README.md). When your design needs it, the Architect (new designs) or the Architect (changes) suggests it and asks before using it. Or add it yourself:
- In the workspace: the Tapeo Hub button in the project header, then Add to project.
- In a terminal (in the project folder):
$ tapeo hub add debounce - In VS Code: Tapeo: Add from Tapeo Hub in the Command Palette.
How it is verified
Before every release Tapeo checks this block: its self-checking testbench passes in Icarus Verilog, Verilator -Wall reports nothing, and Yosys synthesizes it (97 generic cells at the default parameters). That shows it works for the tested cases; your copy is part of your project, and your own testbench checks it in your design.
Source
// Button or switch debouncer. The input passes a two-flop synchronizer; the
// output follows it only after it has held one value for STABLE_CYCLES clock
// cycles (for example 1,000,000 for 10 ms at 100 MHz). `rise` and `fall`
// pulse for one cycle when the output changes. Tapeo Hub (verified by Tapeo).
module debounce #(
parameter integer STABLE_CYCLES = 1000000
) (
input logic clk,
input logic rst,
input logic noisy,
output logic clean,
output logic rise,
output logic fall
);
localparam integer COUNT_WIDTH = (STABLE_CYCLES > 1) ? $clog2(STABLE_CYCLES) : 1;
localparam logic [COUNT_WIDTH-1:0] LAST_TICK = COUNT_WIDTH'(STABLE_CYCLES - 1);
logic [1:0] sync;
logic [COUNT_WIDTH-1:0] stable;
always_ff @(posedge clk) begin
if (rst) sync <= 2'b00;
else sync <= {sync[0], noisy};
end
always_ff @(posedge clk) begin
if (rst) begin
stable <= '0;
clean <= 1'b0;
rise <= 1'b0;
fall <= 1'b0;
end else begin
rise <= 1'b0;
fall <= 1'b0;
if (sync[1] == clean) stable <= '0;
else if (stable == LAST_TICK) begin
stable <= '0;
clean <= sync[1];
rise <= sync[1];
fall <= !sync[1];
end else stable <= stable + 1'b1;
end
end
endmodule