|
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.
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:
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.
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.
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";
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; COMPONENT and_gate PORT ( a, b : IN BIT; a_and_b : OUT BIT ); END COMPONENT and_gate; COMPONENT or_gate PORT ( a, b : IN BIT; a_or_b : OUT BIT ); END COMPONENT or_gate; COMPONENT xor_gate PORT ( a, b : IN BIT; a_xor_b : OUT BIT ); END COMPONENT xor_gate; BEGIN and0 : and_gate PORT MAP( a, b, x1 ); and1 : and_gate PORT 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.
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;
To simplify your coding, you can define data objects other than signals.
CONSTANT target_value: BIT_VECTOR( 3 DOWNTO 0 ) := "0101";
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 );
The compiler used to compile and execute (simulate) your VHDL programs is called MAX+plus II and is designed by Altera.
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.
It might not be possible to directly print from the Print menu in MAX+plus II. In this case follow these steps:
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).
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:
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.