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.

Our first post on this topic looked into how this sort of bouncing manifested. It wasn’t pretty, as many buttons from several different boards and even a keypad all demonstrated this problem.

Our second post discussed how to create the digital logic necessary to debounce a user input.

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.

Fig 1: Measuring Transitions
Counting how many transitions a button creates

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.

Fig 2: Measuring Button Press Duration
Counting a button press duration

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.

always @(posedge i_clk)
	// Two clocks to synchronize asynchronous data
	q_in   <= i_in;
	r_in   <= q_in; // SYNCHRONIZED!
	// Keep track of last value, so we can find changes
	r_last <= r_in;

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:

always @(posedge i_clk)
	if (i_reset)
		o_transitions <= 0;
	else if (r_last != r_in)
		o_transitions <= o_transitions + 1'b1;

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 triggered.

always @(posedge i_clk)
	if (i_reset)
		triggered <= 1'b0;
	else if (r_last != r_in)
		triggered <= 1'b1;

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:

always @(posedge i_clk)
	if (i_reset)
		counter <= 0;
	else if ((triggered)&&(!clock[31]))
		counter <= counter + 1'b1;

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.

always @(posedge i_clk)
	if (i_reset)
		o_max_clock <= 0;
	else if (r_last != r_in)
		o_max_clock <= counter;

This 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.

Let’s avoid this mistake by integrating our debouncing and unbouncing logic into a debugging bus, such as the one we built together earlier.

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.