Getting started with VHDL

VHDL is descriptive[1] language used to describe digital systems. It's an acronym which stands for VHSIC Hardware Description Language. VHSIC is also an acronym which stands for Very High Speed Integrated Circuits[1:1]. It was designed by our good friends, the United States Department of Defense to be used to describe and document the behavior of circuits for later synthesis[1:2]. It's strongly typed; means that everything must declared and executes completely in parallel.

In digital system design

One major use for VHDL is in describing hardware for reconfigurable chips such as Field Programmable Gate Arrays (FPGA). This is because it tends to be a bit of a jack of all trades. It can be used to describe, document, verify and synthesize large designs.

There are two major methodologies that you can follow in programming in VHDL; the behavioral model and the Structural model.

The behavioral model

This focuses on describing the behavior of the system rather than trying to flesh out connections between components. This is the highest level of abstraction you can get with VHDL as you only specify the relationship between input and output signals. This can be as Register Transfer Level (RTL) - Arithmetic level, Algorithmic Level which defines a set of statements that define a sequence of operations or a boolean expression. Here is a small example of what this looks like[1:3]

Alarm = Key_In_Ignition and (Door_open or Seatbelt_off)

Structural model

This describes a system as a collection of logic gates. These components can then be inter-connected to form a larger component and perform a desired function. This representation tries to flesh out how the system would actually be implemented physically rather than abstract as much as possible. Here is an e

Door
[Not supported by viewer]
Seatbelt
[Not supported by viewer]
Ignition
[Not supported by viewer]
Alarm
[Not supported by viewer]

I'll be focusing on the behavioral model going forward. To practice execute VHDL code, you need a development board. There are a plethora of boards and devices to choose from. I used an FPGA developed by Xilinx - The Spartan 6 on a Xilinx Nexys 3 development board.

Nexys 3 development board

For my board, the recommended IDE is ISE Design Suite also available from Xilinx although it is possible to use other tools like Vivado and Sigasi. Your board and tools may and probably will be different so I will not be talking about setup, see your manufacturer's support page for that.

Building a comparator

A comparator is a simple circuit that, as the name suggests, compares two signals (voltages or currents) and tells you which is larger, or if they are all the same.

----------------------------------------------------------------------------------
-- Company: Stark Industries
-- Engineer: Tony Stark
-- 
-- Create Date:    04:26:13 05/29/1970 
-- Design Name: 
-- Module Name:    Comparator - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity Comparator is
    Port ( in1 : in  STD_LOGIC;
           in2 : in  STD_LOGIC;
           eq_o : out  STD_LOGIC;
           gr_o : out  STD_LOGIC;
           ls_o : out  STD_LOGIC);
end Comparator;

architecture Behavioral of Comparator is

begin

	eq_o <= in1 xnor in2;
	gr_o <= in1 and not in2;
	ls_o <= not in1 and in2;

end Behavioral;

The code here is pretty simple and straight forward, although it's key to keep in mind that the code is not executed sequentially. Every line executes in parallel. It all happens at the same time.

... it's key to keep in mind that the code is not executed sequentially. Every line executes in parallel. It all happens at the same time.

This is because we are basically describing how the chip should be configured. So we are pretty much describing a circuit.

Port ( in1 : in  STD_LOGIC;
           in2 : in  STD_LOGIC;
           eq_o : out  STD_LOGIC;
           gr_o : out  STD_LOGIC;
           ls_o : out  STD_LOGIC);

This is our port declaration where we describe our I/O. We have 2 input and 3 outputs signals. These are variable names so you can call them what ever you want; in my description eq_o represents equal, gr_o represents greater, and ls_o represents less than. in1 and in2 stand for input 1 and 2 respectively.

Now we move on to describing how these ports will be used.

eq_o <= in1 xnor in2;

This is the equality testing portion of our circuit. Here we take advantage of xnor which is a negated exclusive or gate. Taking a look at its truth table,

A B A nor B
0 0 1
0 1 0
1 0 0
1 1 1

Here we can see that the output signal is 1 (true) when A and B are the same. This is why xnor is known as a logical equality detector.

gr_o <= in1 and not in2;

This tests to see if in1 is greater than in2. This is done by taking in1 and the inverse (not) if in2.

Lets take a look at the truth table for this expression

in1 in2 not in2 in1 and not in2
0 0 1 0
0 1 0 0
1 0 1 1
1 1 0 0

We can see that the output is only true when in1 is 1 and in2 is 0. This is the only case where in1 is greater than in2. To get this to work for the reverse case where in1 is less than in2, all we need to do is move the not.

ls_o <= not in1 and in2;

I'm pretty sure you've already guessed what the truth table looks like, but let's take a look.

in1 not in1 in2 not in1 and in2
0 1 0 0
1 0 0 0
0 1 1 1
1 0 1 0

As expected, the output is only 1 when in1 is 0 and in2 is 1. This means that in1 is less than in2.

Now it's important to remember that what we have just done is basically describe a circuit which when drawn out, looks like this -

in1
[Not supported by viewer]
in2
[Not supported by viewer]
eq_o
[Not supported by viewer]
ls_o
[Not supported by viewer]
gr_o
[Not supported by viewer]
xnor
[Not supported by viewer]
and
[Not supported by viewer]
and
[Not supported by viewer]
not
[Not supported by viewer]
not
[Not supported by viewer]

Now all that's left is to map your ports to different I/O on the board. This is usually done by creating a *.ucf file. The exact specifics of how this is done differs by board, and IDE, so it's best to check with your vendor for full details on how to do that.

Conclusion

Getting into VHDL can be a bit of a complicated affair, but it does help in taking load off the design, modeling and synthesis processes in digital systems. It doesn't function in the same way as conventional programming because of the inherent parallelism that exist. Ultimately, it all comes together to show that code is really only a small fraction of the battle.


  1. Credit for this example goes to Penn Engineering's VHDL tutorial. Check out the link for far more detailed tutorials. ↩︎ ↩︎ ↩︎ ↩︎