You are on page 1of 8

An Introduction to VHDL

Richard E. Haskell

1.1

SOME BASIC VHDL MODELS

VHDL is a double acronym: the V stands for VHSIC (Very High Speed Integrated Circuit) and the HDL stands for Hardware Description Language. The VHSIC program was launched in 1980 with the goal of achieving significant productivity gains in VLSI technology. During the 1980s VHDL was developed under government contract and became an IEEE standard (IEEE 1076) in 1987 and updated in 1993.

A 2 x 1 Multiplexer
As a first example of using VHDL consider the n-line 2 x 1 multiplexer shown in Fig. 1.3. A VHDL program for this multiplexer is given in Listing 1.1. The first line is a VHDL comment line that begins with --. The next two lines must be at the start of all VHDL programs. It indicates that all of the contents of the library file std_logic_1164.vhd located in the IEEE directory is to be included in this program. This system file contains definitions of the basic VHDL data types and logic functions.

a(n-1:0) b(n-1:0)

n-line 2x1 MUX

y(n-1:0)

sel

Figure 1.3 An n-line 2 x 1 multiplexer

All VHDL programs are made up of two basic components: an entity and an architecture. The entity in Listing 1.1 is named mux2g and describes the input and output signals shown in Fig. 1.1. The optional generic statement in the second line of the entity defines width to be the size of the data busses a, b, and y in Fig. 1.1 (i.e. width = n). In Listing 1.1 a, b, and y are defined to be of type STD_LOGIC_VECTOR and sel is defined to be of type STD_LOGIC. The type STD_LOGIC can have any of the nine values shown in Table 1.2. The architecture of a VHDL program describes what the module defined by the entity does. In Listing 1.1 the behavior of the multiplexer is described by the ifthenelse statement. Note that <= is used in VHDL as the signal assignment operator. The ifthenelse statement is an example of a VHDL sequential statement. All sequential statements must occur within a process. The process in Listing 1.1 is defined by the statement process (sel, a, b). The signals sel, a, and b within the parentheses of the process statement are called the sensitivity list. The statements within a process are executed any time that any values of the signals in the sensitivity list 1

change. All signals whose values are assigned within a process (e.g. y in Listing 1.1) are updated at the end of a process. An architecture can have any number of processes. These processes execute concurrently.

Listing 1.1

mux2g.vhd

-- A width-line, 2 to 1 multiplexer library IEEE; use IEEE.std_logic_1164.all; entity mux2g is generic(width:positive); port ( a: in STD_LOGIC_VECTOR (width-1 downto 0); b: in STD_LOGIC_VECTOR (width-1 downto 0); sel: in STD_LOGIC; y: out STD_LOGIC_VECTOR (width-1 downto 0) ); end mux2g; architecture mux2g_arch of mux2g is begin process (sel, a, b) begin if sel = '0' then y <= a; else y <= b; end if; end process; end mux2g_arch;

Table 1.2 Values of type STD_LOGIC Value Meaning 'U' Uninitialized 'X' Forcing Unknown '0' Forcing 0 '1' Forcing 1 'Z' High Impedance 'W' Weak Unknown 'L' Weak 0 'H' Weak 1 '-' Don't care

A 4 x 1 Multiplexer
As a second example of a VHDL program consider the 4 x 1 multiplexer shown in Fig. 1.4. The VHDL program for this multiplexer is given in Listing 1.2. Note the use of a case statement within the process instead of an ifthenelse statement. The case statement must include all possible cases. It is therefore necessary to use the word others instead of 11 in the last case because you must include all of the possible combinations of the nine values in Table 1.2. 2

a(n-1:0) b(n-1:0) c(n-1:0) d(n-1:0) n-line 4x1 MUX y(n-1:0)

sel(1:0)

Figure 1.4 An n-line 4 x 1 multiplexer

Listing 1.2 mux4g.vhd -- A width-line, 4 to 1 multiplexer library IEEE; use IEEE.std_logic_1164.all; entity mux4g is generic(width:positive); port ( a: in STD_LOGIC_VECTOR (width-1 downto 0); b: in STD_LOGIC_VECTOR (width-1 downto 0); c: in STD_LOGIC_VECTOR (width-1 downto 0); d: in STD_LOGIC_VECTOR (width-1 downto 0); sel: in STD_LOGIC_VECTOR (1 downto 0); y: out STD_LOGIC_VECTOR (width-1 downto 0) ); end mux4g; architecture mux4g_arch of mux4g is begin process (sel, a, b, c, d) begin case sel is when "00" => y <= a; when "01" => y <= b; when "10" => y <= c; when others => y <= d; end case; end process;

end mux4g_arch;

An Adder
Suppose you wish to design an n-bit adder as shown in Fig. 1.5. The simple VHDL program shown in Listing 1.3 will do this! However, this will only work if you include the statement
use IEEE.std_logic_unsigned.all;

at the beginning of the program. This will include another library file that will allow you to add two signals of type STD_LOGIC_VECTOR and produce a sum that is also of type STD_LOGIC_VECTOR. This is an example of overloading the + operator so that it can 3

add signals of different types. The + sign hides a lot of details, and will, in fact, implement a ripple adder.
b(n-1:0) a(n-1:0)

adder

y(n-1:0)

Figure 1.5 An n-bit adder

Listing 1.3 adder.vhd -- Title: adder library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; entity adder is generic(width:positive); port ( a: in STD_LOGIC_VECTOR(width-1 downto 0); b: in STD_LOGIC_VECTOR(width-1 downto 0); y: out STD_LOGIC_VECTOR(width-1 downto 0) ); end adder; architecture adder_arch of adder is begin add1: process(a, b) begin y <= a + b; end process add1; end adder_arch;

A Binary-to-BCD Converter
Suppose we want to convert two (or more) hex digits to the corresponding BCD number. Thus, the 2-digit hex numbers, 00 FF, will be converted to the 3-digit BDC numbers, 000 255. This will be useful if you want to display your hex result in decimal. One way to do this is to use the so-called shift and add 3 algorithm. To see how the shift and add 3 algorithm works consider the example shown in Fig. 1.6. The 2-digit hex number to be converted is written as an 8-bit binary number in the two right-most columns in Fig. 1.6. In this case the hex number to be converted is FF so that 11111111 is written in the Start row. The next three columns from the right will end up containing the converted three BCD digits. They are labeled Hundreds, Tens, and Units, and will end up containing the three BCD digits 255 in this case.

Operation B HEX Start Shift 1 Shift 2 Shift 3 Add 3 Shift 4 Add 3 Shift 5 Shift 6 Add 3 Shift 7 Add 3 Shift 8 BCD P z

Hundreds

Tens

Units 7

Binary 4 3

1 1 1 0 2
9 8 17 16

1 0 0 0
7 15

1 0 0 0 1

1 1 0 1 1 0 5

1 1 1 0 1 0 0 1
4 12

1 0 1 0 0 0 0 1 0
3 11

1 1 1 0 1 1 0 0 0 0 0 0 1 0 1 1 1 0 1 1 0 5

1 1 1 0 1 0 1 1 1 1 0 1
0 8

1 1 1 1 1 1 1 1 1 1 1 1

1 1 1 1 1 1 1 1 1 1

F 1 1 1 1 1 1 1 1

1 1 1 1 1 1 1

F 1 1 1 1 1 1 1 1 1 1 1

Figure 1.6 BCD-to-binary conversion

The shift and add 3 algorithm consists of the following steps. 1. Shift the binary number left one bit. 2. If 8 shifts have taken place, the BCD number is in the Hundreds, Tens, and Units column. 3. If the binary value in any of the BCD columns is 5 or greater, add 3 to that value in that BCD column. 4. Go to 1. Follow this algorithm in Fig. 1.6 to see how the hex value FF gets converted to the BCD value 255. Why does this algorithm work? Why add 3? And why add when the number is 5 or greater? First note that we will have to shift 3 times before a number in the Units column could be 5 or greater. At this point the only numbers that could be 5 or greater would be 5, 6, or 7. In Fig. 1.6 it is 7, or 0111. Suppose that the original hex number was just the hex digit E. Then after 3 shifts the situation would be as shown in Shift 3 row in Fig. 1.7. If we shift one more time we would get the value 1110 in the Shift 4 row in Fig. 1.7. This is not a valid BCD number because it is between hex A and F. Therefore, we must add 6 to skip over the 6 invalid hex digits A through F. Notice when we add 6 to E in Fig. 1.7 we get the correct BCD value 14.

Operation HEX Start Shift 1 Shift 2 Shift 3 Shift 4 6 Add 6 BCD

Tens

Units

Binary

1 1

1 1 1 0 1 0 1

1 1 1 1 0 4

1 1 1 0 0 0

E 1 1 1 0 1 1 0 1 0 0

Figure 1.7 Example of converting a hex E to BCD

Back in Fig. 1.6 the reason we add 3 when the shifted BCD value is 5 or greater it is because it is equivalent to adding 6 after the next shift if the result would be A or greater. Note that shifting a 5 would produce an A or B (depending what next bit gets shifted in); shifting a 6 would produce a C or D; and shifting a 7 would produce an E or F. In each case we would need to add 6. Therefore, we add 3 before doing the next shift when the value is 5 or greater. We will now implement a binary-to-BCD converter in VHDL by using Fig. 1.6 as a guide to write a behavioral model that will implement this binary-to-BCD converter as shown in Listing 1.4. The definitions of the input B vector and the output P vector are shown in Fig. 1.6. Note in Listing 1.4 that we have defined a variable z that corresponds to the 18 values shown in the last row of Fig. 1.6. That is, it is basically a concatenation of the input B and the output P. Variables in VHDL can only be declared within a process and before the begin of the process. The process in Listing 1.4 contains two for loops. The first for loop initializes all elements of z to 0. The B is assigned to z(10 downto 3). This is equivalent to performing the first 3 shifts in Fig. 1.6. Note that variables must use the variable assignment operator := instead of the signal assignment operator <=. The index i in the for loop is an implied integer and does not need to be declared as a separate variable. The second for loop in Listing 1.4 loops 5 times and performs shifts 4 through 8 in Fig. 1.6 using the statement
z(17 downto 1) := z(16 downto 0);

with appropriate add-3s as necessary.

Listing 1.4 binbcd.vhd -- Title: Binary-to-BCD Converter library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; entity binbcd is port ( B: in STD_LOGIC_VECTOR (7 downto 0); P: out STD_LOGIC_VECTOR (9 downto 0) ); end binbcd; architecture binbcd_arch of binbcd is begin bcd1: process(B) variable z: STD_LOGIC_VECTOR (17 downto 0); begin for i in 0 to 17 loop z(i) := '0'; end loop; z(10 downto 3) := B; for i in 0 to 4 loop if z(11 downto 8) > 4 then z(11 downto 8) := z(11 downto 8) + 3; end if; if z(15 downto 12) > 4 then z(15 downto 12) := z(15 downto 12) + 3; end if; z(17 downto 1) := z(16 downto 0); end loop; P <= z(17 downto 8); end process bcd1; end binbcd_arch;

In Listing 1.4 the last statement in the process,


P <= z(17 downto 8);

assigns the upper 10 bits of the variable z to the output signal P. This is typical of how variables are used within a process and then assigned to a signal at the end of the process. An important property of variables is that the assignment operator := updates the value of the variable immediately. This must happen for the algorithm in Listing 1.4 to work! Recall, however, that the values of signals are not updated immediately, but only at the end of the process.

A Register
The n-bit register shown in Fig. 1.8 can be designed by using the VHDL program shown in Listing 1.5. The sensitivity list in the process statement contains the two inputs clr and clk. This means that the statements within the process will execute when either of the signals clr or clk change. The process contains a single ifthenelsif statement. If

clr = 1 then an asynchronous clear is implemented by clearing each bit of q(i) using a for loop.
d(n-1:0)

clr clk

reg

load

q(n-1:0)

Figure 1.8 An n-bit register

Listing 1.5 reg.vhd -- A width-bit register library IEEE; use IEEE.std_logic_1164.all; entity reg is generic(width: positive); port ( d: in STD_LOGIC_VECTOR (width-1 downto 0); load: in STD_LOGIC; clr: in STD_LOGIC; clk: in STD_LOGIC; q: out STD_LOGIC_VECTOR (width-1 downto 0) ); end reg; architecture reg_arch of reg is begin process(clk, clr) begin if clr = '1' then for i in width-1 downto 0 loop q(i) <= '0'; end loop; elsif (clk'event and clk = '1') then if load = '1' then q <= d; end if; end if; end process; end reg_arch;

In the elsif clause in Listing 1.5 the condition


clk'event and clk = '1'

is used to indicate a rising edge of the clock. The attribute event means that there is a change of the signal clk. Combined with the condition that after the change clk = 1 implies that it describes a rising edge of the clock. The remainder of the elsif clause states that if load = 1 then q <= d, which describes the behavior of a register. Note that if load = 0 then q does not change on a rising edge of the clock. 8

You might also like