Tuesday 15 September 2015

Elbert V2 VHDL Tutorial - Using Vectors

This is a quick instructional or tutorial on using Logic Vectors in VHDL and using them with the Elbert V2 FPGA development board from Numato Labs.

In a previous tutorial on this blog we learned how to implement a project within Xilinx WebISE 14.7, how to select the FPGA device and parameters, how to write some simple VHDL code which performed some logical operations and how to select which pins on the FPGA controlled four onboard LEDS and four switches.  The link to that tutorial is below if anyone needs a refresher ;)

Elbert V2 Logic Operators using VHDL

Lets now learn how to control all eight LEDS and Switches at the same time using a special function of VHDL called Vectors or more specifically standard logic vectors.  Standard Logic vectors are a little bit like arrays in a high level programming language but in VHDL they are a way of defining a collection of signals all at once.

This post was written based upon the Hamster tutorial which can be found here -

Hamster's VHDL course - Module 4

Using the information provided I have added my own extras and tailored the information for use with the Elbert V2 Development board from Numato Labs.

For example there are eight LEDS available for demonstration on the Elbert V2 Development board and there are also eight DIP switches.  If we were to write an entity statement for them we could write the following code:

library IEEE; use IEEE.STD_LOGIC_1164.ALL; 
entity Switch_LEDS is 
Port ( 
      Switch_one : in STD_LOGIC; -- make a switch input variable
      Switch_two : in STD_LOGIC; -- make a switch input variable
      Switch_three : in STD_LOGIC; -- make a switch input variable
      Switch_four : in STD_LOGIC; -- make a switch input variable
      Switch_five : in STD_LOGIC; -- make a switch input variable
      Switch_six : in STD_LOGIC; -- make a switch input variable
      Switch_seven : in STD_LOGIC; -- make a switch input variable
      Switch_eight : in STD_LOGIC; -- make a switch input variable

      LED_One : out STD_LOGIC; -- make an LED output variable
      LED_Two : out STD_LOGIC; -- make an LED output variable
      LED_Three : out STD_LOGIC; -- make an LED output variable
      LED_Four : out STD_LOGIC; -- make an LED output variable
      LED_Five : out STD_LOGIC; -- make an LED output variable
      LED_Six : out STD_LOGIC; -- make an LED output variable
      LED_Seven : out STD_LOGIC; -- make an LED output variable
      LED_Eight : out STD_LOGIC; -- make an LED output variable 
end Switch_LEDS;

Perfectly correct and valid however, it was also a lot if typing!!

The whole entity statement can be rewritten with exactly the same functionality like this:

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 

entity Switches_LEDs is 
Port ( 
       switches : in STD_LOGIC_VECTOR(7 downto 0); 
       LEDs : out STD_LOGIC_VECTOR(7 downto 0)
     );
end Switches_LEDs;

Which is a great deal less typing...We can still control each of the separate LEDS and switches and we can also perform logical operations on the vectors as before...rather than discuss VHDL coding syntax lets learn by doing (Also because I find this stuff boring to write about)...

Lets open up Xilinx WebISE 14.7 and start a new project - I called mine Elbert_vector_tutorial but you can use any name you like - just remember what it is and be sure the Top-level source selected is HDL (we are going to write code and not use a schematic entry for this tutorial).


Click next when you are ready to continue -


Make sure the above settings are selected (they reflect settings required for the Elbert V2 development board and FPGA device) - Click next when ready...


Finally click finish...The project summary window was displayed which confirms all of the settings we selected and where the project folders are etc...

The main WebISE project window will be displayed - right click in the hierarchy panel and select 'New Source'


A new window will appear - select VHDL module and provide a suitable file-name, I called mine vector_tutorial_module - See the picture below for details:


Click next when you are ready to continue - The define module screen will be displayed.  You can click on the drop-down menus to select the inputs and outputs if you like or you can leave things blank and click next.  If you enter the details for the inputs and outputs WebISE automatically generates an entity statement for you.  As a designer you can change things later if required.  I chose to enter the details for my project -


Click next when you are ready to display the summary window -


Click finish to return to the main project window and the newly generated code:


I like to delete the comments as I don't find them useful - but that's my personal preference....
Here is the code that has just been generated for us:

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 

entity vector_tutorial_module is 
Port ( 
       dip_switches : in STD_LOGIC_VECTOR (7 downto 0); 
               LEDS : out STD_LOGIC_VECTOR (7 downto 0)
     ); 
end vector_tutorial_module; 

architecture Behavioral of vector_tutorial_module is 
begin 

end Behavioral;
The above code performs the following: Include required libraries in order to get the 'compiler' to understand the logic functions Tell the compiler what the function the pins on the FPGA are going to be referred to as - This is what the entity statement actually does - it describes the function of the actual pins. In the above code: There are going to be eight DIP switches used and connected to the FPGA device (inputs) and there 
are eight LEDS used and connected to the FPGA device (outputs)... 

The behaviour of the pins has not yet been written - that is code that we will write in the architecture statement. 

Lets now decide what we want to switches to do - lets just have the LEDS mirror the switch states - when a switch is low (off) the corresponding LED is off and when a switch is high (On) the LED is on. We do that be assigning the value of the switch to the LED using the <= operator which is not Less than or equal to like in some high level programming language syntax. In VHDL it means the variable before the operator takes the value of the variable after the operator like this: 

LEDS <= dip_switches 
Here is the whole code:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity vector_tutorial_module is
 Port ( dip_switches : in STD_LOGIC_VECTOR (7 downto 0);
 LEDS : out STD_LOGIC_VECTOR (7 downto 0));
end vector_tutorial_module;

architecture Behavioral of vector_tutorial_module is

begin

LEDS <= dip_switches;

end Behavioral;

Save the file and then right click on the hierarchy window again - we need to add an implementation
constraints file - this is the file that tells the compiler which pins connect to the dip switches and 
LEDS.


Select implementation constraints file and give it a suitable name - 


Click next when ready and the summary window will be displayed:


Click finish to return to the main project window - The new file we
created will be open - copy the following code into the open file:

    # Inputs # 
    
    NET "dip_switches[0]"       LOC = P70   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "dip_switches[1]"       LOC = P69   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "dip_switches[2]"       LOC = P68   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "dip_switches[3]"       LOC = P64   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; 
    NET "dip_switches[4]"       LOC = P63   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "dip_switches[5]"       LOC = P60   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "dip_switches[6]"       LOC = P59   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "dip_switches[7]"       LOC = P58   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;

    # Outputs #

    NET "LEDS[0]"            LOC = P46  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[1]"            LOC = P47  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[2]"            LOC = P48  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[3]"            LOC = P49  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[4]"            LOC = P50  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[5]"            LOC = P51  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[6]"            LOC = P54  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
    NET "LEDS[7]"            LOC = P55  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;

Save the file.  The above code tells the compiler which pins on the FPGA device are connected to the LEDS and switches.  The information is available from the Elbert V2 Schematic diagram and manual.  I will probably discuss another method of generating the constraints file in a different post.

Assuming this is all correct we are now ready to compile the project and generate a bitstream file which can be uploaded to the Elbert V2 Development board - Exciting times!

Click on the Implement top module arrow (the green arrow below the hierarchy window) to compile the project...The green arrow will turn grey and lots of messages and stuff will flicker in the console window:


There should not be any errors or warnings - Now we need to generate the bitstream file.  Right click on Generate Programming file in the window below the Hierarchy window:


Select Process Properties - Make sure Create Binary Configuration File is checked...


Click Ok to continue and then right click on Generate Programming File and select Run...

Once that's complete, if you haven't done so already connect the Elbert V2 Development board to the PC using a suitable USB Cable.  Then load up the ElbertV2configuration tool and select the COM port that reflects the development board - mine is COM 12.  Then select the bin file we just created - mine was called   'vector_tutorial_module'....you will need to navigate to the project folder and select the appropriate file.


Click program to upload the code to the FPGA Development board:


If all goes according to plan you will be able to see something similar to the photograph below:

All Dip switches are low so the LEDS are On! 
If you manipulate the Dip switches you should see the LEDS turn on and off.  Because the LEDS are active low when the switches are off the LEDS come on.  The seven segment display is also active or ghosting when it shouldn't be...lets solve these issues.  We need to modify the code to remove the ghosting seven segments and get the LEDS to mirror the switch position not the inverse of the switch position.

Lets modify the VHDL module source code - we need to edit the entity section to add the pins required to remove the ghosting, copy the code below into the Entity section of the VHDL module:

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 

entity vector_tutorial_module is 
 Port ( 
        dip_switches : in STD_LOGIC_VECTOR (7 downto 0); 
        LEDS : out STD_LOGIC_VECTOR (7 downto 0); 

        Seven_Segment_Transistor_One : out STD_LOGIC;
        Seven_Segment_Transistor_Two : out STD_LOGIC;
        Seven_Segment_Transistor_Three : out STD_LOGIC
       );

end vector_tutorial_module;




Now lets modify the architecture section to invert the LED behaviour and turn off the seven segment 
display:


Architecture Behavioral of vector_tutorial_module is



begin


     -- LED output mirrors dip switch settings 
     LEDS <= NOT dip_switches;


     -- turn the seven segment off
     Seven_Segment_Transistor_One <= '1'; 
     Seven_Segment_Transistor_Two <= '1'; 
     Seven_Segment_Transistor_Three <='1'; 


end Behavioral;




Next we need to add the Seven_Segment_Transistor code to the constraints file:


NET "Seven_Segment_Transistor_One"          LOC = P124 | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ; 

NET "Seven_Segment_Transistor_Two"          LOC = P121 | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ; 

NET "Seven_Segment_Transistor_Three"        LOC = P120  | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;


Now we are ready to save everything and recompile and re-upload.  Click on the green arrow to implement the top module.  Once that is complete generate a new bitstream file and upload it to the Elbert V2 development board as before...
No Seven Segment ghosting and the LEDS mirror the switch states
If all goes according to plan then changing the switches from low to high now turns the leds on and off.  We can also perform logical operations on the vector statements (AND, OR, NOT).  This can be useful - I can't think of a reason to do this with switches and LEDS but it's very useful for performing mathematical operations on binary numbers!  


To illustrate the point lets modify the VHDL code in the vector module to turn the LEDS 1 to 4 ON when switches 1 to 4 and 4 to 8 are all ON demonstrating the AND function. 

We need to modify the line of code below:

LEDS <= NOT dip_switches;

To something that performs the 'AND' function on the first four bits  - the code below should do it:


LEDS(3 downto 0) <= NOT dip_switches (3 downto 0) AND NOT dip_switches (7 downto 4);
LEDS(7 downto 4) <= "0000";

The above lines of VHDL code set LEDS 1 to 4 to be controlled by the eight dip switches.  Each LED will come on when the correct switches are in the ON state.  To get LED 1 to illuminate switches 1 and 5 must be high, for LED 2 to come on switches 2 and 6 must be ON etc...


LEDS 4 to 8 will be Off permanently as they are not being controlled by the dip switches and have been set low.

Here is a short video showing the AND operation:



Now lets demonstrate the 'OR' function in the same way. This time LED 1 will come on if either Switch 1 or switch 5 and the same for the other switches and LEDS...

LEDS(3 downto 0) <= NOT dip_switches (3 downto 0) OR NOT dip_switches (7 downto 4);
LEDS(7 downto 4) <= "0000";

Here is another short video demonstrating the code:


It is also possible to manipulate each bit of an STD logic vector separately -

LEDS(3) <= dip_switches(3); 

The above line sets the control of LED 4 to mirror the status of switch 4.

If we wanted we could also perform logic functions on the a single bit of the vector:

LEDS(3) <= NOT dip_switches(3); 

It is also possible to set the value of a single bit directly: 

LEDS(2) <= '1';

This sets the bit LEDS(2) high...a zero would set it low etc. You can also use logic to manipulate each bit:

LEDS(1) <= '1' and '0';

It is also possible to combine statements using the ampersand symbol:

LEDS(1) <= '1' & LEDS(2) <= '0';


Well...that is about all for now - take care and have fun! More posts on VHDL to come soon...


No comments :

Post a Comment