Buttons and switches on FPGAs can be very useful to work with. This extra user I/O on a development board can make life easier when trying to direct what is going on within it, or when trying to debug logic within the board. The problem is that these forms of inputs often “bounce”, and produce multiple transitions when only one was directed.
This problem is particularly problematic in the fact that it is difficult to simulate hardware bounces, since they tend to be quite random.
Today, we’ll discuss how to measure the effect bouncing has on your input logic.
Measuring the Reality of Buttons: Bouncing
To try to capture this phenomena, let’s look at two things in particular.
First, we’ll count the number of state transitions as in Fig 1 below. In the case of a button, the first transition should be when the button is pressed, and the second transition when the button is released. In the case of a switch, only one transition should ever take place. It either of the two bounces, we should be able to tell. In particular, the number of transitions would be something other than two for a button or one for a switch.
The second thing we we might wish to measure is how many clocks it takes for the transitions to take place. For example, with a button, this will be the number of clocks from the first press to the last bounce on release, as shown in Fig 2 below. It won’t tell us as much about bouncing, though, since the bounces will have settled long before this measurement is concluded. It might still tell us something about the nature and character of a button press in general. We can then use this information to understand how to tune our debouncing logic.
Building the code: The Unbouncer
We’ll handle the first two measurements in a routine I’m going to call unbounced. This unbouncer will help to tell us what has been going on within our design.
As we discussed during our discussion on debouncing logic, the first step of necessity is going to be to synchronize our inputs. This synchronization is our approach to take the incoming button logic signal, and make sure that any logic depending upon this information has its setup and hold requirements met. It also helps us avoid any issues with metastability, lest our logic act in an unpredictable (and incomprehensible) manner later.
Once the inputs have been synchronized, we can then count the number of times they change. We’ll reset our count upon any reset request, but ever afterwards we can just increment our counter on any changes:
As we mentioned above, a button press should produce two transitions: one when the button is pressed, and one when it is released. Anything else is an indication of bouncing. This simple counter can be used to test whether there was a bounce or not.
The other thing we’re going to want to do is count how much time it takes
from the initial change (button press) until the final change (button release).
We’ll use the
triggered signal to determine when to run our counter, and
so run the counter any time after the button was
Let’s just remember that we’re going to need to reset our logic between events, so we can re-trigger on the next event.
Next, we’ll keep track of the time since the last trigger:
Note that this “counter” measure will saturate once the top bit sets. This will give us about 21~seconds of measurement before losing track of what’s going on (assuming a 100MHz clock). That should be plenty for our purposes.
Once we have this “time-since-event” above, we can then measure the maximum
amount of time that takes place between the initial button press and the
last change. We’ll set this to zero upon an external reset, and ever
afterwards if our button input
r_in doesn’t match what it was once clock
ago, we’ll update our maximum clock duration value.
o_max_clock value will therefore capture the number of clocks between
a trigger and the last transition.
Coming back to the Debug Bus
At this point, many students would turn to a 7-segment display, LEDs, an LCD, or another display output to return these numbers. This becomes a trap for the student, though, because the problems associated with displaying these values (particularly the 7-segment display or LCD) can be just as complicated, if not more so, than the simple button pressing logic we’ve presented above. When this student then wants to isolate the bug he is getting to be either the debouncing module or the display module, he can’t tell which is causing the problem.
This will be our next step: How to integrate our debouncer together with today’s unbounced logic and the debugging bus, so that we can get bounce information from within an FPGA. Even better, if we include a compressed wishbone scope into that project, you’ll be able to generate your own button bouncing traces.
And this is the fashion which thou shalt make it of: The length of the ark shall be three hundred cubits, the breadth of it fifty cubits, and the height of it thirty cubits. (Genesis 6:15)