Introduction to VHDL and MAX+plus II

VHDL, or VHSIC (Very High Speed Integrated Circuits) Hardware Description Language, was developed by the Department of Defense in 1983 for describing hardware design from a software level.

The basic building blocks of VHDL design are the ENTITY declaration and the ARCHITECTURE body. Note that comments in VHDL code can be inserted by adding -- comment here anywhere in the code. The comment will continue until the end of the line. There is no way to terminate a comment except an end-of-line.

In the following code examples, we use the convention that UPPER CASE words are reserved words in VHDL, while lower case words are identifiers chosen by the programmer.

ENTITY

The ENTITY declaration describes only the input and output ports of the design. The highest level of any VHDL design is a single ENTITY declaration. For MAX+plus II to compile your project, the VHDL file name must be the same as the entity name. For instance, the following ENTITY declaration must be stored in the file counter_4.vhd.

The internal relationship between the inputs and the outputs is not specified. At this stage, the entire design is treated as a "black box", with no knowledge of its internal workings. Signals that enter and leave this box must be declared in the PORT declaration.

ENTITY counter_4 IS PORT(

clk, reset, load_counter: IN BIT;

data: IN BIT_VECTOR( 3 DOWNTO 0 );

count_zero: OUT BIT;

count: BUFFER BIT_VECTOR( 3 DOWNTO 0 )

);

END counter_4;

(Note that the last entry in the PORT declaration is not followed by a semicolon.)

Each signal has a signal mode (IN, OUT, BUFFER) and a signal type (BIT, BIT_VECTOR).

The signal mode determines how the entity will read and write to each signal. There are four different signal modes:

  • IN: Data flows in to the entity, and the entity cannot write to these signals. The IN mode is used for clock inputs, control inputs, and unidirectional data inputs.
  • OUT: Data flows out of the entity, and the entity cannot read these signals. The OUT mode is only used when the signal is not used in any way by the entity.
  • BUFFER: Data flows out of the entity, but the entity can read the signal (allowing for internal feedback). However, the signal cannot be driven from outside the entity, so it cannot be used for data input.
  • INOUT: Data can flow both in and out of the entity, and the signal can be driven from outside the entity. This mode should be used only when necessary (for instance, a bidirectional data bus). Otherwise, error checking of the design is reduced, and the code becomes more difficult to understand. You should not use mode INOUT in your designs for this class.

The signal type must also be declared in the PORT declaration. Signal type describes the possible values the signal can take, and can describe a vector of similar signal types.

The simplest signal types are the BIT and BIT_VECTOR types. The only allowed values for the BIT type are '0' and '1'. (Note that single bit values are assigned values by using single quotes:
bit_signal <= '0';
The BIT and BIT_VECTOR types are intrinsic to the VHDL language, and so they require no library declarations (see the description for std_logic below).

The BIT_VECTOR type must be declared with a value for the vector range. For this type, it is declared as
bit_vector_signal : BIT_VECTOR( maximum_index DOWNTO 0 );
and makes a vector with maximum_index+1 elements. These elements can be individually referenced in the architecture body as signal_vector_name( index_value )
where index_value must be between 0 and maximum_index. For instance:
bit_vector_signal(0) <= bit_signal;
assigns the value of bit_signal to the first element (index 0) of the bit vector bit_vector_signal.

An entire bit vector can be assigned a value by using double quotes:
bit_vector_signal <= "0001";
This statement assigns '1' to bit_vector_signal(0), and '0' to the other three bit values in this vector.

The std_logic and std_logic_vector types are part of the ieee library, and can have the values '0', '1', '-' (don't care), 'Z' (high impedance), or 'X' (indeterminate). The additional values allow more detailed simulations to be performed, and allow the layout editor to optimize logic in the case of assignments to the "don't care" value. To use these types, you must add LIBRARY and USE statements before your entity declaration:

LIBRARY ieee;

USE ieee.std_logic_1164.ALL;

ENTITY counter_4 IS PORT(

clock, reset, load_counter: IN std_logic;

data: IN std_logic_vector( 3 DOWNTO 0 );

reset_alert: OUT std_logic;

count: BUFFER std_logic_vector( 3 DOWNTO 0 )

);

END counter_4;

It is important not to mix BIT and std_logic types in your code. The two types must be converted using resolution functions ("To_bit", "To_bitvector", "To_StdULogic", "To_StdULogicvector") before assigning std_logic signals to bits and vice versa. These functions will considerably complicate your code. In your design, use exclusively BIT and BIT_VECTOR types, or exclusively std_logic and std_logic_vector types. The remainder of this discussion will use only BIT and BIT_VECTOR types.

ARCHITECTURE

The functional relationships between the IN signals and the OUT signals (and the BUFFER signals) are specified in the ARCHITECTURE body. While you can write several different architectures for one entity, only one may appear in your finished VHDL code.

ENTITY full_adder IS PORT (

a, b, carry_in : IN BIT;

sum, carry_out: OUT BIT

);

END full_adder;

ARCHITECTURE architecture_name OF full_adder IS

-- List of internal signals here

-- List of components here

BEGIN

--

-- Architecture statements here

--

END architecture_name;

There are three styles of ARCHITECTURE statements. You can mix several styles in your design, but you should be aware of the different statement types in each style, since some cannot be interchanged between two styles.

  • Dataflow Descriptions: In this style, all assignments occur simultaneously in the design, and generally describe the flow of data from the inputs to the outputs. Signals are assigned values by using the assignment operator "<=".
  • ARCHITECTURE dataflow OF full_adder IS
  • SIGNAL x1, x2, x3, x4, y1 : BIT;
  • BEGIN
  • x1 <= a AND b;
  • x2 <= a AND carry_in;
  • x3 <= b AND carry_in;
  • x4 <= x1 OR x2;
  • carry_out <= x3 OR x4;
  • y1 <= a XOR b;
  • sum <= y1 XOR carry_in;
  • END dataflow;

We could easily eliminate the need for additional signals by consolidating the logical equations:

ARCHITECTURE dataflow OF full_adder IS

BEGIN

carry_out <= ( a AND b ) OR ( a AND carry_in ) OR ( b AND carry_in );

sum <= a XOR b XOR carry_in;

END dataflow;

The layout editor will determine what signal wires might be necessary to implement the design.

In a dataflow architecture, signal assignments can be written with logical equations (as above) or with two types of conditional expressions:

output_vector <= "00" WHEN ( a = b )

ELSE "01" WHEN ( a = c ) -- and a != b

ELSE "10" WHEN ( a = d ) -- and a != b and a != c

ELSE "11";

WITH selecting_vector SELECT

output_vector <= "0001" WHEN "00",

"0010" WHEN "01",

"0100" WHEN "10",

"1000" WHEN "11";

  • Structural Descriptions: In this style, all assignments are performed by mapping signals to components defined before the architecture body.
  • ENTITY full_adder IS PORT (
  • a, b, carry_in : IN BIT;
  • sum, carry_out : OUT BIT
  • );
  • END full_adder;
  • ARCHITECTURE structural OF full_adder IS
  • SIGNAL x1, x2, x3, x4, y1 : BIT;
  • COMPONENTand_gatePORT (
  • a, b : IN BIT;
  • a_and_b : OUT BIT
  • );
  • END COMPONENT and_gate;
  • COMPONENTor_gatePORT (
  • a, b : IN BIT;
  • a_or_b : OUT BIT
  • );
  • END COMPONENT or_gate;
  • COMPONENTxor_gatePORT (
  • a, b : IN BIT;
  • a_xor_b : OUT BIT
  • );
  • END COMPONENT xor_gate;
  • BEGIN
  • and0 : and_gatePORT MAP( a, b, x1 );
  • and1 : and_gatePORT MAP( a, carry_in, x2 );
  • and2 : and_gate PORT MAP( b, carry_in, x3 );
  • or0: or_gate PORT MAP( x1, x2, x4 );
  • or1: or_gate PORT MAP( x3, x4, carry_out );
  • xor0: xor_gate PORT MAP( a, b, y1 );
  • xor1: xor_gate PORT MAP( y1, carry_in, sum );
  • END structural;

This type of description can get very long, and is generally applied when a library of complex components is already available. Note that except for the substitution of components for logical statements, it is identical to the dataflow description, and all assignments occur simultaneously.

Note: the COMPONENT syntax used in the textbook is outdated and will not compile! Use the syntax in the example here, or in the ripple carry adder example below.

  • Behavioral Descriptions: This style is the most complex, but it allows for the conversion of sequential algorithms to VHDL code. The process statement is the essential part of a behavioral description. It is composed of a label (optional), the word PROCESS, and a sensitivity list. This is a list of signals which activate the process every time one of their values change.
  • process_name: PROCESS( sensitivity_list_signal_1, ... )
  • BEGIN
  • -- process statements here
  • END PROCESS process_name;

The most common use of behavioral descriptions is in a state machine, where a device has an internal memory that determines the device's output. A clock input determines the times at which the device's internal state changes. This is generally on the rising edge of the clock, during its transition from '0' to '1'.

For instance, a simple four-bit counter is a state machine with 16 internal states (the 16 possible values of the count signal). This design is presented in counter_4.vhd and below:

ENTITY counter_4 IS PORT(

clock, reset, load_counter: IN BIT;

data: IN BIT_VECTOR( 3 DOWNTO 0 );

reset_alert: OUT BIT;

count: BUFFER BIT_VECTOR( 3 DOWNTO 0 )

);

END counter_4;

ARCHITECTURE behavioral OF counter_4 IS

BEGIN

upcount: PROCESS( clock ) BEGIN

IF( clock'event AND clock= '1' ) THEN

IF reset = '1' THEN

count <= "0000";

ELSIF load_counter = '1' THEN

count <= data;

ELSE

count(0) <= NOT count(0);

count(1) <= count(0) XOR count(1);

count(2) <= ( count(0) AND count(1) ) XOR count(2);

count(3) <= ( count(0) AND count(1) AND count(2) ) XOR count(3);

IF count = "0000" THEN

reset_alert <= '1';

ELSE

reset_alert <= '0';

END IF; -- IF count = "0000"

END IF; -- IF load = '1'

END IF; -- IF( clock'event AND clock = '1' )

END PROCESS upcount;

END behavioral;

The rising clock edge is determined by the presence of clock in the process sensitivity list, then by the statement
IF( clock'event AND clock= '1' )
The clock event test is added to prevent the statements from being performed on any brief error in the clock signal.

In addition to IF-THEN-ELSIF-ELSE statements, a behavioral description can include CASE-WHEN statements to simplify writing the code.

CASE selecting_vector IS

WHEN "0001" => output_vector <= "000";

WHEN "0010" => output_vector <= "001";

WHEN "0100" => output_vector <= "010";

WHEN "1000" => output_vector <= "011";

WHEN OTHERS => output_vector <= "100";

END CASE;

Other Data Objects

To simplify your coding, you can define data objects other than signals.

  • Constants: A constant holds a value which you may use at several points within the code, and may want to change throughout the code without searching for every instance.
  • CONSTANT target_value: BIT_VECTOR( 3 DOWNTO 0 ) := "0101";
  • Aliases: An alias is an alternative identifier for an existing object or part of an existing object. It may be useful if you have a very large vector, but generally reference only certain groups of bits.
  • SIGNAL address: BIT_VECTOR( 31 DOWNTO 0 );
  • ALIAS top_address: BIT_VECTOR( 3 DOWNTO 0 ) IS address( 31 DOWNTO 28 );
  • ALIAS memory_bank: BIT_VECTOR( 3 DOWNTO 0 ) IS address( 27 DOWNTO 24 );
  • ALIAS row_address: BIT_VECTOR( 11 DOWNTO 0 ) IS address( 23 DOWNTO 12 );

Altera VHDL compiler (MAX+plus II)

The compiler used to compile and execute (simulate) your VHDL programs is called MAX+plus II and is designed by Altera.

  • Where do I get it?
  • MAX+plus II version 9.6 is available through XTERM on the SEASnet NT machines (3436 and 4442 Boelter). Use XTERM to start a telnet session to ugrad.ses (select the icon Xtelnet ugrad.ses on the desktop). Log into the server from XTERM, then type maxplus2 at the UNIX prompt to start. If you cannot find it try /usr/local/bin/maxplus2.
  • Student version 7.21 is available on the CD-ROM at the back of the textbook. Once you install it, you will need an authorization code from the Altera University Program Licensing website.
  • Student version 9.23 adds more devices for design implementation, and is available for download on the Altera MAX+plus II website. Once you install it, you will need an authorization code from Altera University Program Licensing website.
  • The student versions are limited to a simulation time of 1 microsecond, and the clock period on the waveform editor is limited to 200 nanoseconds. Since this allows only 5 clock periods, you will want to use the following file to build your waveform: convert.exe. Once you have downloaded the self-extracting executable, expand into your max2work directory. (If you prefer a zip file, use convert.zip.) Open this file in the waveform editor, then save it as an SCF file with the same name as your project file. If you have already compiled your design, you can then use the menu option Node -> Enter Nodes from SNF to add signals from your design. Do not add your clock signal. Instead rename the existing clk signal using the "Edit Node" option (click the right mouse button on the clk signal).
  • Good Features
  • Does thorough error checking.
  • Maps the design onto a real FPGA.
  • Provides macro-functions for modules (RAM, adders, multipliers, et cetera).
  • Compiles using a top-down approach.
  • Bad features
  • One typo or syntax error can generate many error messages!
  • Compile time can be painfully slow for big designs.
  • Because of top-down approach, locating errors can be tricky (it may indicate error in the instantiator, not the actual module).

Using MAX+plus II Examples on an NT Workstation

  1. Create a directory called max2work in your home directory.
  2. Download the self-extracting executable VHDL_examples.exe into the max2work directory.
  3. Extract these files by double-clicking on the file's icon; it should be set to extract to the destination directory "Z:\max2work". (If you prefer a zip file, the same files are in VHDL_examples.zip.)
  4. Double-click on the desktop icon Xtelnet ugrad.ses.
  5. Log in to the XTERM server (same user ID and password as for NT), and click on the "Click here when done" bar at the "Message of the Day" prompt.
  6. Run the compiler by typing maxplus2 at the UNIX command line prompt.
  7. Choose File -> Open and select "max2work\full_adder.vhd".
  8. Select File -> Project -> Set Project to Current File. This makes a project out of the VHDL file.
  9. Select File -> Project -> Save, Compile and Simulate.
  10. If it compiles, choose Open SCF to enter the waveform editor.
  11. If it does not compile, go to the compiler window (choose MAX+plus II -> Compiler) then select: Interfaces -> VHDL Netlist Reader Settings. From this window, select VHDL 1993, then hit OK. Then select File -> Project -> Save, Compile and Simulate again.

Complete VHDL Example in MAX+plus II

The problem: To construct a full adder that adds 2 bits and a carry_in bit, producing a single bit sum and carry out. Then, build a 4-bit ripple carry adder using this single bit full adder.

The high level structure of the full adder is:

The full adder is implemented as a VHDL program in full_adder.vhd. If you have downloaded the Examples file, it is in the folder "full_adder_dataflow" along with an SCF file showing the 8 possible inputs.

The high level structure of the ripple carry adder is:

The implementation is in ripple_adder.vhd. This file uses the previously specified full adder as a COMPONENT. The complete example is in the folder "ripple_adder" along with an SCF file showing some of the possible inputs.

Printing from the UNIX machines

It might not be possible to directly print from the Print menu in MAX+plus II. In this case follow these steps:

  1. Go to File -> Print Setup. Click Options
  2. Choose Encapsulated Postscript file and give a name. Click OK.
  3. Now when you print from MAX+plus II, a postscript file is created in your current directory. This can be printed from a UNIX prompt using lpr filename. In case of problems, consult the lab assistant in the room. Also read the printing directions that are put up in the lab.

Using the Graphic Editor in MAX+plus II

If you have extra time on your projects, you may want to also build your design using gates and flip-flops. The graphic editor in VHDL is quite good, and if you name your inputs and outputs the same as in your VHDL file, you can use the same waveform editor file! One of the examples in the VHDL_examples file is a graphic design, in the directory counter_4_graphic. It uses D-flip flops to store the four bits of the counter, with similar combinational logic to the VHDL counter example (in counter_4_vhdl).

HELP!

There is online help for MAX+plus II, but it can be very confusing. A better place to start is the introduction to VHDL in sections 2.6, 3.11, and 4.5 of the textbook. You can also try some resources on the web:

  • Green Mountain Computing Systems
  • A Hardware Engineer's Guide to VHDL
  • Professor Wolfram H. Glauert's VHDL Tutorial, Universität Erlangen-Nürnberg

The best way to learn VHDL and MAX+plus II is by working with the software, and creating working projects that use an increasing set of VHDL syntax. Start with a simple example, and once you have it working, expand on it piece by piece. If you still have questions, you can email the teaching assistant.

This introduction was written by John Pipan, Bob McIlhenny, and Anand Panangadan.