Altera MAX+PlusII contains a Library of Parameterized Modules(LPM) that allows implementation of devices such as RAM, ROM, arithmetic devices, etc. The size of the devices are parameterized. That is, the number of bits in the operands are specified at the time an instance of the component is made. In order to use these components, you must declare the LPM library(LIBRARY lpm;) and specify which package to use in this library(USE lpm.lpm_components.all;). The following example shows how to use a LPM add/subtract device to create a 32-bit add/subtract unit.
LPM Example
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY lpm;
USE lpm.lpm_components.all;
ENTITY add_subt IS
PORT(a, b: IN std_logic_vector(31 downto 0);
a_s: IN std_logic;
answer: OUT std_logic_vector(31 downto 0));
END add_subt;
ARCHITECTURE struct OF add_subt IS
BEGIN
-- u1 is an arbitrary name of the instance
u1: lpm_add_sub -- This is the name of the component
GENERIC MAP(lpm_width => 32)
-- data, datab, add_sub, result are the formal parameter names
PORT MAP( dataa => a, datab => b, add_sub => a_s,
result => answer);
END struct;
A list of LPM components is available using HELP-> megafunctions/LPM in Altera MAX+PlusII.
VHDL encourages hierarchical design. That is, previous designs may be used as components in a higher level design. For example, suppose that we previously designed a full adder and now would like to use it in a design. In order to re-use a design, it must be declared as a COMPONENT. The component declaration for a full adder may appear as follows:
COMPONENT full_add IS
PORT(a, b, cin: IN std_logic;
Sum, cout: OUT std_logic);
END COMPONENT;
The PORT statement is copied and pasted from the full_add design file. This component declaration is placed in the declarative part of the Architecture in which it is used. The component declaration is not necessary if the component declaration is placed in your package as follows:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
PACKAGE mypack IS
-- Component declaration
COMPONENT full_add IS
PORT(a, b, cin: IN std_logic;
Sum, cout: OUT std_logic);
END COMPONENT;
-- Function prototype
FUNCTION lsl(in1: std_logic_vector)
RETURN std_logic_vector;
-- There may be any number of function prototypes
END mypack;
PACKAGE BODY mypack IS
FUNCTION lsl(in1: std_logic_vector)
RETURN std_logic_vector IS
VARIABLE d: std_logic_vector(3 downto 0);
BEGIN
FOR i IN 0 TO 2 LOOP
d(i + 1) := in1(i);
END LOOP;
d(0) := ‘0’;
RETURN d;
END lsl;
END mypack;
Do not forget to include “USE work.mypack.all;” in your higher level design. You may now make an instance of the full adder in the concurrent part of the Architecture or in a Process. See Lab 2 for an example.
Packages are contained in a library. For example, in the library called IEEE, there are packages called std_logic_1164, std_logic_unsigned and std_logic_arith. There is a library called WORK which is your default working directory. It is so fundamental that it does not have to be declared. The WORK library is the directory in which you store your projects. You can declare it with the statement:
LIBRARY work;
This is not necessary however.
Packages are a convenient place to locate your function prototypes and implementations. They can the be called from a design without declaring them as long as your design contains the statement: USE work.package_name.all;
Packages are made up of two parts, the Declarative part and the Body. The general form for the package is:
-- Declarative part
PACKAGE package_name IS
{function prototypes and component declarations}
END package_name;
-- Body of package – The place where function implementations are located.
PACKAGE BODY package_name IS
{function implementations}
END package_name;
Package example
LIBRARY ieee;
USE ieee.std_logic_1164.all;
PACKAGE mypack IS
-- Function prototype
FUNCTION lsl(in1: std_logic_vector)
RETURN std_logic_vector;
-- There may be any number of function prototypes
END mypack;
PACKAGE BODY mypack IS
FUNCTION lsl(in1: std_logic_vector)
RETURN std_logic_vector IS
VARIABLE d: std_logic_vector(3 downto 0);
BEGIN
FOR i IN 0 TO 2 LOOP
d(i + 1) := in1(i);
END LOOP;
d(0) := ‘0’;
RETURN d;
END lsl;
END mypack;
This package should be compiled in the same directory where projects that use it are located. The compiling of a package takes much less time than it normally takes. Don’t be surprised when it appears to terminate early.
Using the package
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE work.mypack.all;
ENTITY pack_try IS
PORT(a: IN std_logic_vector(3 downto 0);
b: OUT std_logic_vector(3 downto 0));
END pack_try;
ARCHITECTURE trial OF pack_try IS
BEGIN
b <= lsl a;
END trial;
A function executes a sequential program and returns a single value to a calling program. A function may be used any place an expression may be used.
General form:
FUNCTION func_name(formal parameter list)
RETURN return_type IS
-- Declarative part
-- declarations of variables or constants go here
BEGIN
{sequential statements}
RETURN return_value;
END func_name;
Parameters are inputs to the function and may not be changed by the function. Therefore, they can not appear on the left side of an assignment statement.
Function example
-- This function performs a logical shift left on a std_logic_vector.
FUNCTION lsl(in1: std_logic_vector(3 downto 0))
RETURN std_logic_vector IS
Variable d: std_logic_vector(3 downto 0);
BEGIN
FOR i IN 0 TO 2 LOOP
d(i + 1) := in1(i);
END LOOP;
d(0) := ‘0’;
RETURN d;
END lsl;
The function definition may be placed in the declarative part of the Architecture. To call the function a statement such as the following can be used:
q <= lsl(b);
This statement would be located after the BEGIN statement in the Architecture. It can also be located within a PROCESS.
--==============================================================
-- The following is the design of a 4-to-1 multiplexer where the inputs to the multiplexer
-- are 4-bit numbers
--==============================================================
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
USE ieee.std_logic_arith.all;
--==============================================================
ENTITY mux4to1 IS
PORT( a, b, c, d: IN std_logic_vector(3 downto 0);
sel: IN std_logic_vector(1 downto 0);
q: OUT std_logic_vector(3 downto 0));
END mux4to1;
--==============================================================
ARCHITECTURE bill OF mux4to1 IS
BEGIN
PROCESS(sel, a, b, c, d)
BEGIN
CASE sel IS
WHEN “00” =>
q <= a;
WHEN “01” =>
q <= b;
WHEN “10” =>
q <= c;
WHEN “11” =>
q <= d;
WHEN OTHERS =>
q <= “0000”;
END CASE;
END PROCESS;
END bill;
The FOR loop has the following format:
FOR identifier IN range LOOP
{sequential statements}
END LOOP;
The following is an example of the use of a FOR loop:
PROCESS
-- The signal clk has been declared previously. The statement below waits for a
-- change in the value of clk and for clk = ‘1’. This corresponds to a rising edge.
WAIT UNTIL clk’EVENT AND CLK = ‘1’;
FOR i IN 0 to 2 LOOP -- i does not have to be declared
reg(i) <= reg(i + 1); -- reg previously declared
END LOOP;
reg(3) <= ‘0’;
END PROCESS;
In the above process, there is no sensitivity list. Altera VHDL does not allow the use of a WAIT statement and a sensitivity list.
The format of the CASE statement is as follows:
CASE(expression) IS
WHEN value1 =>
{sequence of statements}
WHEN value2 =>
{sequence of statements}
WHEN OTHERS =>
{sequence of statements}
END CASE;
Note that all possible values of the expression must be accounted for as was the case with the selected signal assignment. The following is an example of the use of a CASE statement:
PROCESS(sel, a, b, c, d)
BEGIN
CASE sel IS
WHEN “00” =>
q <= a;
WHEN “01” =>
q <= b;
WHEN “10” =>
q <= c;
WHEN “11” =>
q <= d;
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS;
IF-THEN:
The format of the IF-THEN construct is as follows:
IF (condition) THEN
{sequence of statements}
ELSIF (condition) THEN
{sequence of statements}
ELSE
{sequence of statements}
END IF;
There may be 0 or more ELSIF clauses and 0 or 1 ELSE clause. The following is an example of an IF-ELSE statement.
PROCESS (sela, selb, a, b, c) -- process executes if any of these signals change
BEGIN
IF sela = ‘1’ THEN
q <= a;
ELSIF selb = ‘1’ THEN
q <= b;
ELSE
q <= c;
END IF;
END PROCESS;
Signals may only be declared in the declarative part of an Architecture(i.e. before BEGIN). Variables may only be declared in the declarative part of the process(i.e. before the BEGIN).
Signals Variables
Assignment
Operator <= :=
Use Represents Represents local
circuit interconnect storage
Scope Global Local to process
Signals Variables
Behavior Updated at Updated immediately
end of process
execution(new
value not immediately
available)
As mentioned earlier, the statements in a process are executed in sequence. Within processes, special sequential statements can be used. Remember, the following sequential statements may not be used outside of a process.
As mentioned earlier, variables may only be declared within a process. Signals may not be declared within a process. When an assignment is made to a variable, its value updates immediately. This is not true of signals. Variables have their own assignment operator. The operator used by signals is <= while the one for variables is :=. Variables are used for temporary storage of data similar to a conventional programming language.
Example of a variable declaration(done in the declarative part of the process):
VARIABLE temp: std_logic_vector(7 downto 0);
Examples of variable assignment(done after the BEGIN statement):
temp := “10101010”;
temp := x”aa”; -- only in 1993 VHDL
temp(7) := ‘1’;
temp (3 downto 0) <= “1010”;
c := a AND b;
Variables may be assigned to signals:
y <= c;
As mentioned earlier, CSA’s allow combinational logic circuits to be implemented. They are not sufficient for implementation of sequential circuits, however. Processes are needed to describe the operation of sequential circuits. Processes have the following form:
-- In the PROCESS statement below (a, b) is a sensitivity list(optional). The
--process executes whenever a signal in this list changes(i.e. has an event).
--======================================================
PROCESS (a, b)
-- this is the declarative part of the process.
-- constants, variables and types are declared here.
BEGIN
-- sequential statements go here
END PROCESS;
Important notes regarding processes:
- Processes execute in zero time.
- Processes execute endlessly unless broken by:
- WAIT statement or
- Sensitivity list (Processes with a sensitivity list have an implied WAIT at the end)
- An Architecture can have multiple processes.
- All processes execute concurrently.
- Variables are local to the process in which they are declared.
- Variables may only be declared within processes
The following examples assume that the signals have been previously declared. Signals have a “type” associated with them. A few examples of signal types are integer, bit, bit_vector, std_logic, and std_logic_vector. Signals of type std_logic can take on 9 different values. This is necessary to accurately model signals in real circuits. In real circuits signals have more values than just ‘0’ or ‘1’. For example, signals could be high impedance(Z) or unknown(x) as well. A bus composed of multiple std_logic signals is called a std_logic_vector.
Signal declarations(done in the declarative part of the Architecture)
SIGNAL a: std_logic;
SIGNAL b: std_logic_vector(3 downto 0);
Simple signal assignments(done in the concurrent part of the Architecture):
a <= ‘0’; -- assign value to std_logic signal, use single quotes
b <= “0010”; -- assign values to a std_logic_vector, use double quotes
An alternative way to assign values to std_logic_vector is to use hexadecimal:
b <= x”2”; -- can only do this in VHDL 1993
b(1 downto 0) <= “11”; -- assigns values to a bit slice of a std_logic_vector
We will always use VHDL 1993. When you compile a project for the first time, with the compiler window open:
INTERFACES
VHDL netlist reader
Choose 1993
INTERFACES
VHDL netlist writer
Choose 1993
Simple signal assignment can also be done using an expression:
q <= a AND (b XOR c); -- use parentheses to specify order
Conditional signal assignments(done in the concurrent part of the Architecture):
q <= a WHEN sela = ‘1’ ELSE
b WHEN selb = ‘1’ ELSE
c;
Selected signal assignments(done in the concurrent part of the Architecture):
The following assumes that sel is std_logic_vector(1 downto 0).
WITH sel SELECT
q <= a WHEN “00”,
b WHEN “01”,
c WHEN “10”,
d WHEN “11”,
NULL WHEN OTHERS;
The WHEN OTHERS clause is needed because the selected signal assignment must evaluate all possible conditions. Recall that std_logic signals have 9 possible values.
Important note:
Selected and conditional assignments are CSA’s and may not be used within processes.
Template for a VHDL file
VHDL files are composed of Entity-Architecture pairs. The Entity portion of the file is analogous to a symbol for the design. It describes all of the external connections to the design. The Architecture portion of the file is analogous to the circuit diagram of the design. It defines the implementation of the design. Every VHDL design will have the following general appearance:
--==========================================
-- Library and package declarations
--==========================================
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
USE ieee.std_logic_arith.all;
--==========================================
-- The following is the Entity portion
--==========================================
ENTITY name IS --Keywords are capitalized(optional)
PORT( list of all external connections);
END name;
--==========================================
-- The following is the Architecture portion
--==========================================
ARCHITECTURE anyname OF name IS
-- This is the declarative part of the Architecture
-- Declare signals, enumeration types, constants here
BEGIN
-- This is where the implementation is described. Concurrent signal
-- assignments go here. Therefore this is called the concurrent part.
-- Order of the statements does not matter since all statements are executed -- concurrently.
PROCESS
-- The architecture may contain zero or more processes.
-- This is the declarative part of the process.
-- Variables used in the process are declared here.
BEGIN -- Beginning of the process implementation.
-- The process is implemented using sequential statements.
-- For example, FOR LOOP, IF-THEN-ELSE, CASE
END PROCESS;
END anyname;
In the above, note the use of -- to indicate a comment. “C-style” comments are not used in VHDL.