Thursday, October 31, 2019

Revised Verilog debounce module

A while back I wrote a post (here) about the Verilog module I used to debounce input pins. As I noted in a reply to that post, that module had a flaw that caused a single sample of the opposite polarity to be reported as an edge, even though the state output didn't change.

Later I found another problem. My modified Lattice iCEblink demo board has a clock frequency of 10 MHz. My Digilent Spartan 3E Starter board has a clock frequency of 50 MHz, and my Digilent Atlys board has a clock frequency of 100 MHz. There is no point in sampling the input pin at 100ns, 20ns, or 10ns intervals, as doing so would require a very long debounce FIFO. To address this I divided the system clock down to provide a clock enable input of a more reasonable 100 KHz, resulting in the input being sampled every 10 microseconds (remember, the printer position sensor signals are only asserted for 500us, and exhibit no appreciable bounce). However, this meant that the edge detection outputs on the iCEblink were asserted for 100 clock cycles, which then required the modules using the debounce module to do edge detection on the edge detection outputs. That's silly.

These issues prompted me to rewrite the debounce module. The original had one Verilog "always" block which handled everything, whose logic was gated by the "clk_en" input. Since hardware logic operates in parallel, any Verilog "always" block involving non-blocking assignments to multiple nets can be rewritten as separate "always" blocks with one block per net. Thus I split the debounce FIFO handling into one block (lines 25-33) and the state and edge detection in another (lines 35-57), then removed the gating on the "clk_en" input from the second block. I also renamed the "clk_en" input to "sample", as this seemed to describe its function more accurately.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
`timescale 1ns / 10ps
`default_nettype none

//
// Input pin syncronizer/debouncer
//
module debounce #(
    parameter DEPTH = 6         // 60us @ 10us/cycle
) (
    input  wire     clk,        // Clock
    input  wire     rst_n,      // Asynchronous reset active low
    input  wire     sample,     // Sample Enable
    input  wire     pin,        // External input pin
    output reg      state,      // Debounced state
    output reg      leading,    // going-Active edge
    output reg      trailing    // going-Inactive edge
);

    // Combination synchronizer and debouncer
    (* ASYNC_REG = "TRUE" *) reg              sync;
    (* ASYNC_REG = "TRUE" *) reg  [DEPTH-1:0] fifo;

    // Clock the input signal through the synchronizer
    // and into the fifo.
    always @(posedge clk or negedge rst_n) begin
        if (~rst_n) begin
            sync <= 1'b0;
            fifo <= {DEPTH{1'b0}};
        end else if (sample) begin
            // Update the debounce FIFO
            {fifo, sync} <= {fifo[DEPTH-2:0], sync, pin};
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (~rst_n) begin
            leading <= 1'b0;
            trailing <= 1'b0;
            state <= 1'b0;
        end else begin
            // Assume no edges
            leading <= 1'b0;
            trailing <= 1'b0;

            // Check for a stable 0 state
            if (~|fifo) begin
                trailing <= state == 1'b1;
                state <= 1'b0;
            end

            // Check for a stable 1 state
            if (&fifo) begin
                leading <= state == 1'b0;
                state <= 1'b1;
            end
        end
    end

endmodule

Since the logic managing state and edge detection isn't gated by the "sample" input anymore, the leading and trailing edge outputs are now asserted for only one clock period regardless of the sample period.

Another advantage of this rewritten form is that the debounce FIFO logic could be replaced with a counter-based implementation, which would allow for longer debounce periods with reduced resource usage, without replacing the state and edge detection logic.

No comments:

Post a Comment