Essential VHDL
Essential VHDL
Essential VHDL
Amit Raj
This page describes some essential VHDL needed for EE 475. We use VHDL for building hardware -
hence we concentrate on the synthesis subset of the language. VHDL can also be used to accurately
model digital systems. These simulation models can be extremely complex and are beyond the scope
of this course.
Library Declarations
Every VHDL file (actually, every ENTITY, see below) should begin with:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
The standard_logic_1164 library defines the variable types (std_logic and std_logic_vector)which we
will use. The standard_logic_unsigned library overloads certain operators, such as '+', '<' and '-' for the
types std_logic and std_logic_vector.
Entity Declarations
Every digital circuit has inputs and outputs. VHDL uses an ENTITY declaration to describe the
circuit's ports.
entity ENTITY_NAME is
port( SIGNAL_NAMES_AND_MODES_AND_TYPES );
end ENTITY_NAME;
In EE 475 we will use the TYPE std_logic and std_logic_vector to the exclusion of all other types,
with the possible exception of enumerated types later in the course. Std_logic is a scaler (one bit) and
std_logic_vector is composite, an array, or bus in digital hardware terms. Our std_logic_vectors will
always have a descending range - that is (N downto 0). And the LSB (the least significant bit
corresponding to the 2 0 bit) will be X(0) and the MSB would be X(N).
Ports, the inputs and outputs of a circuit have a mode. Modes used in EE 475 are IN, OUT, INOUT,
and BUFFER. Ports of IN can only be read, trying to write, or assign a value will result in a error.
Ports of mode OUT can only be written, attempting to read the value will result in an error. (Ports of
mode OUT can only appear on the LHS of an assignment statement. Ports of mode IN can only appear
on the RHS of an assignment statement.)
A port of INOUT can be both read and written and is usually used for bi-directional ports. BUFFER
ports can be written, and read, but only the value written by the entity can be read - in other words a
BUFFER is like an OUT tied to an IN. In our system, the mode BUFFER may synthesis incorrectly
with NO warning. Don't use it.
Port declarations will take the form -
SIGNAL_NAME is a identifier. The only thing to remember here is not to use a VHDL keyword (like
"entity") or start the identifier with a number. Popular signal_names might be AIN, Data_out, Clock,
reset, etc. VHDL literals are not case sensitive. In other words AIN, ain, Ain, aIn, etc all refer to the
same identifier.
Let's try to put this all together. Here's a typical entity declaration for this course:
entity design is
port( a, b: in std_logic_vector(7 downto 0);
dout: out std_logic_vector(7 downto 0);
reset: in std_logic;
clk: in std_logic
);
end design;
This entity, named DESIGN, has two input buses A and B, one output bus DOUT, and two more
inputs RESET and CLK. All but purely combinational circuits will have a clock and (usually) a reset.
Most circuits (at least in this course) will be synchronous sequential circuits.
Architecture Declarations
The entity declaration is used to describe the ports of the digital system, the architecture is used to
describe the behavior of the digital system.
Our architectures should adhere to the following style guidelines even if they appear to be simpler than
the syntax explained in the VHDL manual. A simple style is easier to maintain and sometimes
produces smaller circuits when compiled to the FPGA.
An architecture declaration for this course will look like the following:
SIGNAL_DECLARATIONS;
begin
THE_HARD_STUFF;
end ARCH_NAME;
Every architecture has a NAME (ARCH_NAME) and is a associated with some ENTITY
(ENTITY_NAME). There is a declarative region in which we will declare signals (circuit nodes)
internal to the digital system. (Ports are visible to other entities, internal signals are not.) Then after a
BEGIN is where the hard stuff is - where we will describe the behavior of the digital system.
A typical architecture (minus the hard stuff) should look like the following.
THE_HARD_STUFF_MISSING;
end synthesis1;
Use the architecture declarative region to declare signals internal to the design!
The 'hard stuff' is the statement declarative region which will contain concurrent statements that
describe the behavior of the entity. Unlike other programming languages (VHDL is really a hardware
description language, not a programming language), statements in an architecture execute
concurrently, not in sequential order.
end synthesis;
Concurrent Signal Assignment statements will always generate combinational logic. In the above
example an AND gate, and a OR gate. Operators available for our use include AND, OR, NOT,
NAND, NOR, XOR, + (binary addition), - (binary subtraction), and & (concatentation).
A <= X + Y;
A <= Z OR (X AND Y);
A <= X & Y;
VHDL is a strongly typed language. The types on the RHS and the LHS must be the same, and if
std_logic_vectors must be of the same length. This makes sense! Can't tie a 4 bit bus to an 8 bit bus
unless one resolves the dangling 4 bits. For instance if you had a 4 bit vector and an 8 bit vector, you
could zero-extend the shorter one, as follows:
begin
Concatention allows signals to be combined into larger composites (bigger buses). The opposite,
slicing apart a bus, is implied by indexing.
begin
X <= Z & Z; --COMBINE TWO 4 BIT BUSES INTO ONE 8 BIT BUS
end synthesis;
At this point we can write boolean equations in VHDL, and even arithmetic equations using the
overloaded "+" and "-" operators, but we are still forced to derive the boolean equations manuallly.
The advantage of VHDL comes from using complex VHDL concurrent signal assignment statements
and letting the synthesis tool derive the implied boolean (logical) equations.
In addition to assignment statments, there is a when-else conditional signal assignment statement that
allows the expression of conditionals in concurrent signal assignment statements:
There is also a selected signal assignment statement which corresponds to a case statement. In the
code fragment below, SELECTOR is a signal which is used to specify which alternative is assigned to
A. Since all cases must be covered in the statment, the OTHERS keyword must be included.
Sequential Statements
At this point we can write an entity, create an architecture, and even describe simple combinational
circuits in VHDL. But this does not allow us to describe more complex behaviors associated with
sequential circuits, nor to take full advantage of the synthesis system's ability to generate the digital
circuit implementation from the VHDL description.
If we want to introduce clocked behavior we need one more, very general concurrent statement, the
PROCESS statement. The process statement may contain other statements which appear to execute in
sequential order, and which appear more similar to familiar C-like languages. Do not be deceived.
Each process executes at the same time as every other process statement and at the same time as every
other concurrent statement. Further, within a single process there are quite limiting rules to how you
can use a signal.
process
--variables
begin
--Sequential Statements
end process;
Of all the VHDL statements we will use in this course, the process statement will be the most
simplified. In general we will use it for modeling clock circuits, register updates, and occasionally
complex combinational circuits.
The following impliments a synchronous reset of a register and a specification to update it when the
clock rises.
process
begin
end process;
This process waits until the clk goes from 0 to '1' then executes the sequential statements. The VHDL
(or circuit) tests the value of reset, and if it is '0' A is cleared, else B is transfered to A.
Skahill and other texts suggest various forms for process statement, but for this course this form will
work for most situations for clocked processes.
end if;
end process;
Some Details
At this point you probably saying "Ok here is the syntax - but what about the semantics!". You have
seen the form of the VHDL but you are not too sure about the meaning of the statements and how the
are interpreted by either a simulator or a synthesis tool. There is one specific point which causes lots
of bugs. Signals do not behave like C-language variables.
In a programming language like C, or C++, or ADA there are variables that hold values. If we wrote
the following variable assignment statements:
A := X + Y;
B := A + X;
The value of B would be? X + Y + X. And B is updated immediately. Meaning for the following
snippet of code we would expect that "DO THIS" would occur, not "DO THAT" - if coniditional
would be true, not the else.
A := X + Y;
B := A + X;
if B = ((2*X)+Y) then
--DO THIS
else
--DO THAT
end if;
This is also true in VHDL, if you use 'variables' but not if you use signals. VHDL has VARIABLES
(note we have been discussing mostly signals). In general we will discourage the use of variables.
process
variable A, B: integer;
begin
if B = ((2*X)+Y) then
X <= 0;
Y <= 0;
else
X <= X + 1;
Y <= Y + 1;
end if;
end process;
VHDL variables can be defined inside processes and behave like variables. They get updated
immediately and the new value is available for immediate use. So for the above contrived example B
will be equal to 2X+Y when the if conditional is evaluated. Signals don't behave that way!
process
begin
wait until clk='1';
if reset = '0' then
A <= "0000";
else
A <= A + 1;
end if;
end process;
The above clocked process can be read to say "when the clock ticks do what follows". And that is to
update A. Now A must hold its value between clock ticks so A is in fact the output of a register. And it
makes sense that A only changes when the clock ticks. But signals never can change immediately -
they always have some finite delay associated with them.
If we change the earlier process not to use variables for A and B, but instead use signals for A and B.
process
begin
A <= X + Y; --assume X and Y are signals defined else where
B <= A + X; --assume A and B are signals defined else where
if B = ((2*X)+Y) then
X <= 0;
Y <= 0;
else
X <= X + 1;
Y <= Y + 1;
end if;
end process;
In this process the signal assigment statements don't change A and B immediately - there is a delta
delay - so the conditional uses the old value of B what ever that might be, not the new value assigned
previously. One way to think about this is to consider that all signals change value ONLY at the end of
the process, after all statments in the process of executed. And after all, they are really not sequential
at all but concurrent. These semantics, with signals, allows processes to run in any order and still have
the same result.
the following code fragment behaves in the expected way from the 'sequential frame of mind', since
we test CNT before we set it.
process
begin
wait until clk='1';
if CNT = 5 then
CNT <= 0;
else
CNT <= CNT + 1;
end if;
end process;
However if you set the variable before you test it, you will actually be testing the value before it was
set.
if CNT = 5 then
CNT <= 0;
end if;
end process;
A useful stylistic rule is: For any possible execution of the process (execution of conditionals) the
process should only assign a value to a particular signal once. Assigning a value more than once is
syntactically correct, but only the last assigned value will have any effect.
Sequential Statements
VHDL has the full range of sequential statements found in any language - IF, CASE, and various
forms of LOOP. We will not use the LOOP constructs. The IF has the general form:
if conditional then
--sequential statements
elsif conditional then
--sequential statements
else
--sequential statements
end if;
process
begin
wait until clk = '1'
if A = "00" then
X <= X + 1;
end if;
end process;
You will get what you expected. A register to hold X that is only updated when A = 00.
process (A)
begin
if A = "00" then
X <= X + 1;
end if;
end process;
What happens is that it is possible when this process runs that X does not get a value assigned (A is
not equal to "00".) What is the value of X then? It must be the OLD value. Hence while this process
does not have a clock it still imples a register - the synthesis tool builds a latch controlled by the value
of A. It will latch X on A = 00. Conditionals in unclocked processes must cover every case, or they
will imply (perhaps) unwanted registers.
process
begin
if A = "00" then
X <= X + 1;
elsif A = "00" then
X <= X + 2;
elsif A = "00" then
X <= X + 3;
else
X <= X + 4;
end if;
end process;
The above unclocked process will generate a purely combinational circuit - notice the "else". Except in
very comlex cases it is safer to use a concurrent statement outside of a process for combinatorial logic.
In genera, IF statements should be used with care when generating combinational logic - first they add
logic for enforcing the implied priority of an IF statement and they can lead to unintended latches.
Instead use a CASE statement - it will enforce that every input is covered and has a more truth table
like form.
case X is
when "00" => Z <= "001";
when "01" => Z <= "111";
when "10" => Z <= "011";
when others => Z <= "101";
end case;
The above case statement implements a truth table.
Common Mistakes
Getting fancy - keep your VHDL simple. If you are lost - draw a block diagram and then write
the VHDL for each block as a process in an entity architecture pair.
Forgetting your boolean algebra - A NAND B NAND C is not equal to NOT (A AND B AND
C).
Precedence - VHDL does not assign precedence to the AND and OR operators. Hence given
the equation A AND B OR C AND D you will get an error. You must supply parenthesis. You
will likely want (A AND B) OR (C AND D). A NAND B NAND C also has problems since A
NAND B is really NOT (A AND B) you must supply parenthesis! But remember ((A NAND
B) NAND C) is not equal to NOT (A AND B AND C).
Duplicate assignment - In this course we can only have one CONCURRENT assignment to a
signal or port. If we write
architecture example of simple is
begin
A <= X + Y;
A <= X - Y;
end example;
process
begin
A <= X + Y;
end process;
process
begin
A <= X - Y;
end process;
end example;
we will get an error. Why? We are effectively connecting two outputs to a single point in the
circuit. This is a very common error. How to make this work this example work? We must add
a multiplexer or a tri-state bus.