Saturday 16 July 2016

Switch De-bouncing in VHDL for the Mimas V2 FPGA Development Board

It's been a while since I blogged about my adventures in relearning VHDL.  I am slowly regaining the knowledge I used to have when I studied at University (That seems a very long time ago now). Slowly and steadily I'm building up the knowledge needed to use an FPGA for something useful.

I intend writing a couple of connected blog posts over the next week or so in order to demonstrate something more involved (and useful).

This post will concentrate on reading input from a momentary switch.  The Mimas V2 board has six momentary switches on the right side of the board below the micro-SD card slot.  Lets write some code which reads in one these switches with de-bounce code.  We can then use this code to implement a counter which we can display on the seven segment display.  The counter and the seven segment display section will be in later posts.  For now lets concentrate on reading in the switch properly.  For now we can display the output onto an LED

Mimas V2 Momentary Switches
Here is the general idea:
  1. Start
  2. User Presses a momentary switch.
  3. The FPGA reads in the button switch.
  4. The button read is de-bounced.
  5. The corresponding LED is illuminated until the button is released.
  6. Go back to step 1.
  7. End.
Lets fire up Xilinx WebISE and start a new project:


I called mine Mimas_switchDebounce - use any name you like!  Click next when ready:


Make sure all the above settings are entered so that the project is setup correctly for the Mimas V2. Click next when you are ready:


Click Finish to return to the main project screen.

Now left click on the project menu and select 'add new source':


In the dialog window that appears select VHDL Module and give it a suitable name - I called mine switchDebounce:


Click 'Next' when you are ready to continue:

We need to define the inputs and outputs for the debounce code.  We need to read in the raw button presses and then we need to display the results on an LED.  This all needs to be synchonised with the 100 MHz clock on the Mimas V2.  


Complete the form with the above settings and then click 'Next' to continue:


Click 'Finish' to return to the main project screen in WebISE:

Xilinx Webise has helpfully automatically generated some code for us....Let's examine the code.

LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY switchDebounce IS PORT ( CLK : IN STD_LOGIC; Switch_0 : IN STD_LOGIC; LED_0 : OUT STD_LOGIC ); END switchDebounce; ARCHITECTURE Behavioral OF switchDebounce IS BEGIN END Behavioral;

The entity statement defines the inputs and outputs to the FGPA - three standard logic pins, the switch input, the LED output and the 100 MHz system clock.

The architecture statement is where the 'behaviour' of the code will be defined.  There isn't anything in it yet but there will be!  Lets decide how we are going to achieve things.  There is more than one way of reading in buttons, checking that the button state has changed and then producing a result.

Lets make things easier for ourselves and draw a flow diagram:

This should make writing the code a little easier for us.  Every diamond is an if statement, every rectangle is a part of a process....simples!


From the flow diagram we should be able to write the behavioural section of the code:

ARCHITECTURE Behavioral OF switchDebounce IS
SIGNAL inputFlipFlop : STD_LOGIC_VECTOR(1 DOWNTO 0); CONSTANT countMax : INTEGER := 100000000; SIGNAL count : INTEGER RANGE 0 TO countMax := 0; BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk = '1') THEN inputFlipFlop <= inputFlipFlop(0) & Switch_0; IF (inputFlipFlop(0) /= inputFlipFlop(1)) THEN count <= 0; ELSIF (count < countMax) THEN count <= count + 1; ELSE LED_0 <= inputFlipFlop(1); END IF; END IF; END PROCESS; END Behavioral;

Lets go though how the above code works:

SIGNAL inputFlipFlop : STD_LOGIC_VECTOR(1 DOWNTO 0); -- input flip flops CONSTANT countMax : INTEGER := 100000000; -- 100 MHz clock, delay is 100 ms and 100 ms = 10,0000000 ns SIGNAL count : INTEGER RANGE 0 TO countMax := 0;

The above lines create internal signals.  The first signal inputFlipFlop is a simple flip flop.  It is used to check if the button has been pressed or released (changed state).  The constant countMax is the delay period that is set to ensure that a button has actually been pressed.  It has been set to 100 ms although any number can be used.  It was calculated from the 100 MHz source clock.  1/100,000,000 = 1 ns and 10,000,000 ns is equivalent to 100 ms.  If a second was needed then the constant should be set to 1000000000 etc.  The signal count is the value of the number of clock pulses that has passed.  It is dynamically set to the maximum delay period set by countMax.

BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk = '1') THEN inputFlipFlop <= inputFlipFlop(0) & Switch_0; -- read the switch state IF (inputFlipFlop(0) /= inputFlipFlop(1)) THEN -- has flipflop changed state from previous state count <= 0; -- if flipflop has changed then reset count ELSIF (count < countMax) THEN -- if flipflop has changed is count less than countMax count <= count + 1; -- increment count by one ELSE -- count has reached countMax LED_0 <= inputFlipFlop(1); -- set LED_0 to the same state as the flip flop END IF; END IF; END PROCESS; END Behavioral;

The above lines perform the function that is required - the subject of the flow diagram:  
  • If the clock has changed state and is high then check if the flip flop has changed state.  
  • If the flip flop has changed state from it's previous state - This means a button has been pressed and if a button has been pressed then the count needs to be reset.  
  • If the count is less than the delay period then the count must be incremented.  
  • If the count has reached the delay period then the LED state needs to match the flip flop state.   
Here is the entire source code in case it's needed:

LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY switchDebounce IS PORT ( CLK : IN STD_LOGIC; Switch_0 : IN STD_LOGIC; LED_0 : OUT STD_LOGIC ); END switchDebounce;

ARCHITECTURE Behavioral OF switchDebounce IS
SIGNAL inputFlipFlop : STD_LOGIC_VECTOR(1 DOWNTO 0); CONSTANT countMax : INTEGER := 100000000; SIGNAL count : INTEGER RANGE 0 TO countMax := 0; BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk = '1') THEN inputFlipFlop <= inputFlipFlop(0) & Switch_0; IF (inputFlipFlop(0) /= inputFlipFlop(1)) THEN count <= 0; ELSIF (count < countMax) THEN count <= count + 1; ELSE LED_0 <= inputFlipFlop(1); END IF; END IF; END PROCESS; END Behavioral;

Save the file just in case - it's always a good idea to save your work!

Now we need to create an implementation constraints file to tell the system which pins are connected to the clock, switch and the LED.

Create an implementation constraints file using the same method used to create the VHDL source code.  Call it something sensible like switchDebounce.  Copy the code below into the file:

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
# This file is a .ucf for Mimas V2                                                                    #
# To use it in your project :                                                                         #
# * Remove or comment the lines corresponding to unused pins in the project                           #
# * Rename the used signals according to the your project                                             #
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

#*****************************************************************************************************#
#                                            UCF for MIMAS V2 Spartan 6 Development Board             #
#*****************************************************************************************************#

CONFIG VCCAUX = "3.3" ;

   NET "CLK"      LOC = V10 | IOSTANDARD = LVCMOS33 | PERIOD = 100MHz;

#######################################################################################################
#                                              Push Buttons Switches                                  #
####################################################################################################### 
    NET "Switch_0"           LOC = M18   | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = FAST | PULLUP; 

#######################################################################################################
#                                                    LEDs                                             #
####################################################################################################### 
    NET "LED_0"              LOC = P15  | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = FAST ;


Now it's time to 'compile' the code into a bit file and upload it to the Mimas V2:


Click on the green 'implement top module' arrow and wait for the code to compile.  There should not be any errors or warnings.

Now connect up the Mimas V2 board to your computer via a suitable USB cable and then fire up the Mimas V2 configuration tool.  If you are using Linux then use the appropriate python tool - I don't use linux often....only so much time to learn new things at the moment!  Select the appropriate COM port for your board and then navigate to the project...select the newly created bin file and click program!


Once programming is complete you should see something similar to the following:


The more astute among us will notice that the LED starts high and is turned low when a button press is detected.  That is because the switches on the Mimas V2 board are active low....the code logic will need to be inverted in order to take the LED from low to high.  It's a simple enough edit.  We could also remove the seven segment display ghosting if needed...

That's all for now though people - more to come on this - next it's count the button press and display the result on the seven segment!

Hint change the line of code from LED_0 <= inputFlipFlop(1); to LED_0 <= not inputFlipFlop(1);