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