This is part 5 of my Building a Processor series, where I try to build a processor on an FPGA board. This post is about adding some simple counter functionality to the push buttons.
Previous: using a dcm.
I now have a working display, but my board right now has no state to it. I can change it to have the display show more interesting functions of the switch values, but let’s do something simple that has some state: a simple counter. The basic idea is pretty simple: we’ll create a 16-bit register, and wire up the push buttons so that the center one increases the counter value by one, and the left one resets it to zero. The issue is that we don’t want to just increase the value of the counter if we see that the button is pushed — this would result in the counter increasing uncontrollably. You can see that behavior with this code — whenever you press the center button, the display shows “8888” since the value is changing too fast to make out, and when you release the button it will land on a random value.
Instead of incrementing the counter every clock cycle that the button is down, lets increment the counter only the first cycle it’s down. We can detect that by storing the state of the button from the last cycle, and only increment the counter if the previous state was 0 and the current state is 1. You can see that implementation here.
If you try and run this, though, you’ll notice that when you press the center button, sometimes the counter will increase by more than one, and sometimes it will increase when we release the button! Wikipedia explains that this is because the switch “bounces”, or mechanically transitions back and forth a few times when you press it. To address this, I’m going to add a “debounce” circuit; the way this will work is by using hysterisis — the debounce circuit will hold its output at the previous level until it sees a certain number of cycles elapse where the input is set to the new level. You can see the final circuit here.
There are a couple things to note in the circuit: first, I’ve used a “generate” block to generate definitions for the 5 pushbutton debouncers. With only 5 modules to instantiate, it’d certainly be easy to write them all out by hand, but I wanted to learn how to do this, and it’s certainly more extensible. Also, ISE will group all the generated modules together, rather than representing them as five unrelated blocks.
Another thing to notice is that I added some “pipelining” registers after the debouncer (though I’m thinking of moving them before the debouncer): this is because I noticed in the “Post-Place & Route Static Timing Report” that the debounce->ctr route was the critical timing path. I then looked at the “Analyze Timing / Floorplan Design” process to see why that was, and I noticed that the debounce circuits were being placed by the input pins (makes sense), but the counter was being placed on the opposite side of the fpga, since that’s where the sseg outputs are. So there’s this long trace where the FPGA has to haul the debounced signal across the entire fpga, which resulted in that path being the critical timing path. We don’t care about single-cycle waits on the pushbutton inputs, so I just put a register on this path, to pipeline the debounce computation and the routing delay. Yes, this is overkill at this stage in the project, but I was just curious about how to optimize the circuit (more on this in the next part!). Also, for reasons I’ll go into in another post [metastability], I moved the register to be before the debouncer, in case we ever want the non-debounced input.
Next up: optimizing the debouncer circuit.