Tutorial 11: A Debounce Circuit

In this tutorial, we will design a switch debounce circuit, then test it using the design from tutorial 10.

The Debounce Module

Let’s design a module that will debounce a switch or push button. The module needs a clock and the switch signal as input and produces a debounced switch output, as well as switch rise and switch fall outputs. It will also take a parameter which indicates a bounce lockout period. The algorithm for the module just looks for a transition on the switch input and then locks the input in that state for the number of clocks specified by the parameter. Any bouncing by the switch during that time will be ignored by the circuit. Here is the module declaration:

`timescale 1ns / 1ns
module debounce
  #(
    parameter bounce_limit = 1024
    )
  (
   input clk,
   input switch_in,
   output reg switch_out,
   output reg switch_rise,
   output reg switch_fall
   );

We will also need a counter variable called bounce_count for the timer. Here is the declaration for that:

  reg [$clog2(bounce_limit)-1:0] bounce_count = 0;

Next we synchronize the switch input as before in a two bit shift register. Here is the code for that:

  reg [1:0] switch_shift;
  always @(posedge clk)
    switch_shift <= {switch_shift,switch_in};

Next, we’ll handle the actual debounce part of the design. Basically, we need an always block which does a couple things. First, if it sees the switch input rise or fall, it should start a timer. Second, while the timer is counting, it should ignore transitions on the switch input. And finally, if the timer is counting down it should decrement the timer. I have chosen to have the timer count down to zero in this example, but you could just as easily designed it to count up. Here is the always block:

  always @(posedge clk)
    if (bounce_count == 0)
      begin
        switch_rise <= switch_shift == 2'b01;
        switch_fall <= switch_shift == 2'b10;
        switch_out <= switch_shift[0];
        if (switch_shift[1] != switch_shift[0])
          bounce_count <= bounce_limit-1;
      end
    else
      begin
	switch_rise <= 0;
	switch_fall <= 0;
	bounce_count <= bounce_count-1;
      end

Let’s walk through the code. There are basically two cases to consider. If the timer is not zero, then it is counting down and we just decrement the counter and set the rise and fall signals to zero. If it is not counting down but rather is zero, then we set the rise and fall outputs dependig on the shift register value, we set the switch output to the switch_shift bit 0. and if we detected a rise or fall (because the shift register bits are different) we set the timer to the bounce_limit minus 1.

This is basically the switch debounce module. Don’t forget to add your endmodule statement at the end of your module.

Testing the Debounce Logic

Let’s test the debounce logic in our circuit from tutorial 10.

We first need to instantiate our debounce module and hook it up to the center button. Here is the code for that. Notice that I have chosen a 3ms debounce count which I deemed sufficient based on testing the previous design on my board.

  wire btnc_debounced;
  debounce #(.bounce_limit(ms_limit*3)) db_bntc
    (
     .clk(clk),
     .switch_in(btnc),
     .switch_out(btnc_debounced),
     .switch_rise(),
     .switch_fall()
     );

Then we will use the switch seven to select between debounced and non-debounced modes. To do this we modify the btnc_shift shift register to have a mux on its input which selects between the debounced and non-debounced center button based on the switch value. Here is the modified code for that:

  reg [1:0] btnc_shift;
  always @(posedge clk)
    btnc_shift <= {btnc_shift,switch[7] ? btnc_debounced : btnc};

This is all we need to test out the debounce module on our board. Go ahead and build the design and program your board. Verify that you get the same behavior as before when switch seven in in the low position, then switch it high. Can you get the design to detect a bounce on the center button anymore?

One final improvement

Usually, you need to have a number of switch or button inputs in your design. You could instantiate one of our debounce modules for each one, but it would be better, in my opinion, to design the debounce module so that it can debounce more than one button. That way, you can connect a bunch of switch inputs to the module and have it debounce all of them. We can do this using the generate statement like we saw in tutorial 8.

All we need to do it to add a parameter called width which specifies how many switch inputs there are, then modify our module to wrap everything in a generate block. The generate block will use a for loop and rather than instantiating modules like before we will just use the code from the body of the module. We also need to turn the input and output ports into busses. Here is the new module declaration:

`timescale 1ns / 1ns
module debounce
  #(
    parameter width = 1,
    parameter bounce_limit = 1024
    )
  (
   input clk,
   input [width-1:0] switch_in,
   output reg [width-1:0] switch_out,
   output reg [width-1:0] switch_rise,
   output reg [width-1:0] switch_fall
   );

Did you notice how the switch inputs and outputs changed to busses? Later, when we use these interfaces, we need to make sure we index into them appropriately.

Next we have our generate statement with a for loop. This for loop will iterate and build the debounce logic for each switch input bit.

  genvar  i;
  generate
    for (i=0; i<width;i=i+1)
      begin

Before you end the module, you need to end the generate for loop and the generate statement.

      end
  endgenerate

Within the body of the generate for loop, we declare reg and wire signals as before. The generate for loop is run before synthesis or simulation actually happens. This phase of execution is known as elaboration. Think of this as a program that runs that generates your Verilog code before it goes in to the simulator or synthesis tool. The variables you declare within the for loop only have scope within that instance of the loop. For example, the bounce_count counter in iteration 0 of the loop is completely distinct from the bounce_count counter in iteration 1. If you run a simulation and look at the objects for the debounce module, you will see that each iteration of the for loop creates its own set of variables.

Within the body of the generate for loop we need to change all occurrences of switch_in, switch_out, switch_rise, and switch_fall to switch_in[i], switch_out[i], switch_rise[i], and switch_fall[i].

We will use this debounce module in future tutorials.

Here are the files I have for this tutorial.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.