Notes from
“Digital System Design with VHDL” Mark Zwolinski, Pearson/Prentice Hall, 2nd Edition
See also
“The Student’s Guide to VHDL” Peter Ashenden, 2nd edition Morgan Kaufman (under £20)
Ian McCrum
Room 5B16
Tel: 90 366364 voice mail on 6th ring
Email: [email protected]
Web site: http://www.eej.ulst.ac.uk
VHDL: Resources
• “The Student’s Guide to VHDL” Peter Ashenden, 2nd
edition Morgan Kaufman (under £20)
His online guides
• VHDL Cookbook (dated, older version of VHDL)
• VHDL-quick-start.pdf (42 slides)
• VHDL_Tutorial, (84 Pages) online - check copyright status
Do the quick start then come back here – this highlights
usage instead of reference/understanding.
Combinational logic
using VHDL gate models
entity And2 is
port (x, y
end entity And2;
architecture ex1
z <= x and
end architecture
BIT is a predefined
type with two
values: ‘0’ and ‘1’.
: in BIT; z: out BIT);
of And2 is
We can also use
inout or buffer or
omit the in
The entity name
at the end is
The entity part describes a black box. We can see the inputs and outputs of the black
box, together with their types, but we know nothing of the internals of the circuit.
The architecture describes the function and/or
structure of the circuit. In this example, the
functionality of the circuit is described in terms of
Boolean operations. The reason for having this split
is that it is possible to have more than one
architecture for each entity, perhaps describing
alternative implementations or different levels of
description. For instance, we can describe an AND
gate in terms of a Boolean operator, as shown, but
we could also write a truth table. In either case, the
entity, i.e. the ‘black box’, is the same, but there
would be two architectures, one for each model.
VHDL has the following operators defined for type BIT:
not, and, or, nand,
xor and xnor
Structural VHDL
-- in addition to the AND gate
entity Or2 is
port (x, y : in BIT; z: out BIT);
end entity Or2;
architecture ex1 of Or2 is
z <= x or y;
end architecture ex1;
entity Not1 is
port (x : in BIT; z: out BIT);
end entity Not1;
architecture ex1 of Not1 is
z <= not x;
end architecture ex1;
architecture netlist of comb_function is
signal p, q, r : BIT;
g1: entity WORK.Not1(ex1) port map (a, p);
g2: entity WORK.And2(ex1) port map (p, b, q);
g3: entity WORK.And2(ex1) port map (a, c, r);
g4: entity WORK.Or2(ex1) port map (q, r, z);
end architecture netlist;
Note several styles exist for structural VHDL.
We will not use structural except for
testbenches if not using the simple simulator.
Note the signal keyword
Also work is the current working library –
more tool and system independent than
specifying folder location!
Structural VHDL 2
architecture netlist2 of comb_function is
component And2 is
port (x, y : in BIT; z: out BIT);
end component And2;
component Or2 is
port (x, y : in BIT; z: out BIT);
end component Or2;
component Not1 is
port (x : in BIT; z: out BIT);
end component Not1;
signal p, q, r : BIT;
g1: Not1 port map (a, p);
g2: And2 port map (p, b, q);
g3: And2 port map (a, c, r);
g4: Or2 port map (q, r, z);
end architecture netlist2;
Signal assignments
z <= x and y;
z <= not ((x and y) or (a and b));
z <= x after 4 NS;
This is an inertial delay. In other words, the signal is delayed by 4
ns, and in addition, any pulse that is less than 4 ns wide is
suppressed, as shown below.
A pure or transport delay is
modelled with:
z <= transport x after 4 NS;
(Note the space between 4
and NS.) Any pulse is now
transmitted. We can include
keyword inertial if we want to
be very specific about the
delay model.
z <= x and y after 5 ns; -- better to be general…
z <= x and y after delay;
To define delay as a parameter to the VHDL
model using a generic:
entity And2 is
generic (delay : DELAY_LENGTH);
port (x, y : in BIT; z: out BIT);
end entity And2;
If we wish to simulate our circuit to verify that it really does work as expected,
we need to apply some test stimuli. We could, of course, write out some test
vectors and apply them, or, more conveniently, write the test data in VHDL.
This type of VHDL model isoften known as a testbench. Testbenches have a
distinctive style. Below is a testbench for a two-input AND gate.
entity TestAnd2 is
end entity TestAnd2;
architecture io of TestAnd2 is
signal a,b,c : BIT;
g1: entity WORK.And2(ex2) port map (x=>a, y=>b, z=>c);
a<= '0', '1' after 100 NS;
b<= '0', '1' after 150 NS;
end architecture io;
Because this is a testbench, i.e. a description of the entire world that affects
the model we are testing, there are no inputs or outputs in the entity. This is
characteristic of testbenches.
Three-state buffers
A buffer with input and output that has an additional
input called EN or ENABLE. When active it switched
the output off, (both pullup and pulldown transistors)
Usually called the high impedance state ‘Z’
BIT is no longer adequate to represent logic signal
values. We can define a new type to represent logic
signals in VHDL:
type tri is ('0', '1', 'Z'); -- can declare signals & ports..
In VHDL, functions and operators can be
For example, the and operator normally takes two operands of
type bit and returns the Boolean AND of the two operands. We
can write a new AND operator to take two operands of type tri
and return the values shown in the truth table. The syntax of
this function will become clear later.
function "and" (Left, Right: tri) return tri is
type tri_array is array (tri, tri) of tri;
constant and_table : tri_array := (('0', '0', '0'),('0', '1', '1'),('0', '1', '1'));
return and_table(Left, Right);
end function "and";
Standard logic package
Having defined a new type with values ‘0’, ‘1’ and ‘Z’, we would
have to write VHDL functions for the various logical operations.
Moreover, we might wonder whether three states are sufficient
for everything we might wish to model. IEEE standard 1164
defines an enumerated type with nine values:
‘U’ Uninitialized
‘X’ Forcing (i.e. strong) unknown
‘0’ Forcing 0
‘1’ Forcing 1
‘Z’ High impedance
‘W’Weak unknown
‘L’Weak 0
‘H’Weak 1
‘–’ Don’t care
The standard logic type is defined by:
type std_ulogic is ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '–');
The and function for std_ulogic is given by the following truth
table. The two inputs are given by the first row and column.
If we write a model using signals of type BIT or std_ulogic, we must
ensure that two models do not attempt to put a value onto the same
signal. In VHDL terms, a signal may have one or more sources.
A source may be an out, inout or buffer port of an instantiated
component or a driver. In simple terms, a driver is the righthand side of
a signal assignment. The one occasion when we do try to connect two
or more outputs together is when we use three-state buffers.
We still have to be careful that no more than one output generates a
logic 1 or 0 and the rest of the outputs are in the high-impedance state,
but we want the simulator to tell us if there is a design mistake. This
cannot be done with std_ulogic – a VHDL simulator does not treat ‘Z’
as a special case.
The IEEE 1164 standard defines std_logic, which allows more
than one output to be connected to the same signal. Std_logic is
defined as a subtype of std_ulogic, for which a resolution
function is declared. The resolved function defines the state of a
signal if, for example, a ‘Z’ and a ‘1’ are driven onto the same
Because VHDL is strongly typed, operations involving two or
more types must be explicitly defined. A subtype may, however,
be used in place of the type from which it is derived, without
causing an error.
subtype std_logic is resolved std_ulogic;
Ideally, we should use std_ulogic for all signals unless we intend that
any contention should be resolved. If we were to do this, the simulator
would immediately tell us (by halting) if we were erroneously trying to
force two conflicting values onto the same piece of wire.
In practice, however, some synthesis tools have difficulties with
The use of std_logic now seems to be the accepted industry standard, so
in the rest of this book we will use std_logic as the types of all Boolean
Contention can be recognized by the unexpected appearance of ‘X’
values in a simulation.
Use std_logic_1164 for all real signals!
library IEEE;
use IEEE.std_logic_1164.all;
-- The various standard logic types and the
-- functions needed to use them are gathered
-- together in a package. The various standard
-- logic types and the functions needed to use
-- them are gathered together in a package.
every VHDL model that uses the standard logic
package must be prefixed with the 2 lines above
VHDL Concurrent statements
When . . . else
library IEEE;
use IEEE.std_logic_1164.all;
entity three_state is
port (a, enable : in std_logic;
z : out std_logic);
end entity three_state;
architecture when_else of three_state is
z <= a when enable = '1' else 'Z'; -- a tri state
-- if you want a delay use z <= a after 4 NS when enable = '1' else 'Z';
end architecture when_else;
type std_logic_vector is array (NATURAL range <>) of std_logic;
library IEEE;
use IEEE.std_logic_1164.all;
entity decoder is
port (a : in std_logic_vector(1 downto 0); -- could use to if you
z : out std_logic_vector(3 downto 0));
end entity decoder;
architecture when_else of decoder is
z <=
"0001" when a = "00" else
"0010" when a = "01" else
"0100" when a = "10" else
"1000" when a = "11" else
end architecture when_else;
If the final
else is
omitted, z
continues to
take the last
assigned to it.
VHDL, a signal
takes a value
until a new
value is
assigned. This
may be
interpreted as
z holding its
value in a
With . . . select statement
An alternative to the when . . . else statement is the
with . . . select statement. Another model of the 2 to 4 decoder is shown below.
architecture with_select of decoder is
with a select
z <= "0001" when "00",
"0010" when "01",
"0100" when "10",
"1000" when "11",
"XXXX" when others;
end architecture with_select;
-- all tests done in parallel
-- a can have non 0 or 1 values
-- so must have others just in case
Seven segment decoder
library IEEE;
use IEEE.std_logic_1164.all;
entity seven_seg is
port (a : in std_logic_vector(3 downto 0);
z : out std_logic_vector(6 downto 0));
end entity seven_seg;
architecture with_select of seven_seg is
with a select
z <= "1110111" when "0000",
"0010010" when "0001",
"1011101" when "0010",
"1011011" when "0011",
"0111010" when "0100",
"1101011" when "0101",
"1101111" when "0110",
"1010010" when "0111",
"1111111" when "1000",
"1111011" when "1001",
"1101101" when
"0000000" when others;
end architecture with_select;
n to 2n decoder – shift operators
We have seen two ways to describe a 2 to 4 decoder. The same structures could
easily be adapted to model a 3 to 8 decoder or a 4 to 16 decoder. Although these
devices are clearly more complex than the 2 to 4 decoder, conceptually there is
little difference. It would be convenient to have a general n to 2n decoder that
could be described once but used for any application. We saw in the previous
chapter that generics can be used to pass parameters, such as delays, to VHDL
models. We can similarly use a generic to define the size of a structure.
library IEEE;
use IEEE.std_logic_1164.all;
entity decoder is
generic (n : POSITIVE);
port (a : in std_logic_vector(n-1 downto 0);
z : out std_logic_vector(2**n-1 downto 0));
end entity decoder;
We can also define constants:
constant z_out : std_logic_vector(2**n-1 downto 0) := (0 => '1', others => '0');
VHDL shift operators
• sll, sla, rol, srl, sra, and ror
VHDL’s strong typing
These operators are defined, by default, to shift a BIT_VECTOR by an
integer number of places. We will want to shift a std_logic_vector by a
number of places given by the integer interpretation of another
std_logic_vector. Therefore, it would be easier to declare z_out as a
BIT_VECTOR, to convert a to an INTEGER and to convert the final result to
a std_logic_vector.
This last conversion can be done by a function in the std_logic_1164
package. The other conversion function is not, however, provided. To do
this we need to use another package, numeric_std, that provides a set of
numeric operators for vectors of std_logic – but not std_logic_vectors!
Because vectors of bits can be interpreted to be either signed (two’s
complement) or unsigned integers, we need to distinguish the operations
performed on such vectors. Therefore the numeric_std package defines
two new types: signed and unsigned. VHDL’s strong typing means that
we cannot mix signed, unsigned and std_logic_vector by accident, but
because all three types consist of arrays of std_logic,
we can explicitly convert from one to the other using statements of the
x <= unsigned(y);
y <= std_logic_vector(x);
where x is of type unsigned and y is of type std_logic_vector. Although
these look like function calls, no such function has been defined. These are
known as type conversions (sometimes such a conversion is known as a
On the other hand, to convert from an unsigned to an INTEGER does
require a function call because the possible values (‘X’, ‘Z’, etc.) of the
std_logic type need to be interpreted.
The function to_integer is provided in numeric_std to achieve this.
To convert from an INTEGER to an unsigned type, the to_unsigned(i, n)
function should be used, where i is the integer and n is the number of bits
in the result.
Complete Model for Generic decoder
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity decoder is
generic (n : POSITIVE);
port ( a : in std_logic_vector(n-1 downto 0);
z : out std_logic_vector(2**n-1 downto 0));
end entity decoder;
architecture rotate of decoder is
constant z_out : BIT_VECTOR(2**n-1 downto 0) := (0 => '1', others => '0');
z <= to_StdLogicVector (z_out sll to_integer(unsigned(a)));
end architecture rotate;
library IEEE;
use IEEE.std_logic_1164.all;
entity mux is
port (a, b, c, d : in std_logic;
s: in std_logic_vector(1
downto 0);
end entity mux;
architecture mux1 of mux is
with s select
y <= a when "00",
b when "01",
c when "10",
d when "11",
'X' when others;
end architecture mux1;
Can use either architecture,
or tristates to implement a
architecture mux2 of mux is
y <= a when s = "00" else
b when s = "01" else
c when s = "10" else
d when s = "11" else
end architecture mux2;
Using three-state logic to build a
library IEEE;
use IEEE.std_logic_1164.all;
entity mux is
port (a, b, c, d: in std_logic;
s: in std_logic_vector(1 downto 0);
y: out std_logic);
end entity mux;
architecture three_state of mux is
y <= a when s = "00" else 'Z';
y <= b when s = "01" else 'Z';
y <= c when s = "10" else 'Z';
y <= d when s = "11" else 'Z';
end architecture three_state;
Priority encoder
library IEEE;
use IEEE.std_logic_1164.all;
entity priority is
port (a: in std_logic_vector(3 downto 0);
y: out std_logic_vector(1 downto 0);
valid: out std_logic);
end entity priority;
architecture DontCare of priority is
with a select
y <= "00" when "0001",
"01" when "001-",
"10" when "01--",
"11" when "1---",
"00" when others;
valid <= '1' when a(0) = '1' or a(1) = '1' or a(2) = '1'
or a(3) = '1' else '0';
end architecture DontCare;
the don’t
know value
‘-’ is just
treated as a
Note how to get at bits of a vector
architecture Ordered of priority is
y <= "11" when a(3) = '1' else
"10" when a(2) = '1' else
"01" when a(1) = '1' else
"00" when a(0) = '1' else
valid <= '1' when a(0) = '1' or a(1) = '1' or a(2) = '1'
or a(3) = '1' else '0';
end architecture Ordered;
The numeric_std package includes a function, std_match, that treats the
don’t care value as a real don’t care condition. We can’t use std_match in a
with . . . select statement because the choices must be constant. We can
write instead:
use IEEE.numeric_std.all; -- can out this here – only applies to this arch
architecture Match of priority is
y <= "00" when std_match(a, "0001") else
"01" when std_match(a, "001-") else
"10" when std_match(a, "01--") else
"11" when std_match(a, "1---") else
valid <= '1' when a(0) = '1' or a(1) = '1' or a(2) = '1'
or a(3) = '1' else '0';
end architecture Match;
Sequential VHDL
There are three styles of VHDL:
dataflow and
All the examples in this chapter have used the dataflow
style. Dataflow statements are concurrent signal
assignment statements.
Structural VHDL consists of component instantiations.
Sequential VHDL resembles a conventional programming
language. Sequential VHDL statements can be used only in
subprograms (procedures and functions) or processes.
Sequential VHDL
architecture Sequential of priority is
The process has a sensitivity list
process (a) is
with one signal, a. The process is
evaluated only when the signals
if a(3) = '1' then
in the sensitivity list change. Thus
y <= "11";
valid <= '1';
it is important that the sensitivity
elsif a(2) = '1' then
list includes all signals that might
y <= "10";
cause an output to change. In
valid <= '1';
elsif a(1) = '1' then
this case, a is a vector and the
y <= "01";
process is evaluated when any
valid <= '1';
bit of a changes.
elsif a(0) = '1' then
y <= "00";
valid <= '1';
the following rule should be observed: if
an assignment is made to a signal in one
y <= "00";
valid <= '0';
path through a process, an assignment
end if;
should be made to that signal in all paths.
end process;
end architecture Sequential; www.eej.ulst.ac.uk/~ian/modules/EEE515
library IEEE;
use IEEE.std_logic_1164.all, IEEE.numeric_std.all;
entity NBitAdder is
generic (n: NATURAL :=4);
port ( A, B: in std_logic_vector(n-1 downto 0);
Cin : in std_logic;
Sum : out std_logic_vector(n-1 downto 0);
Cout : out std_logic);
end entity NBitAdder;
We can use the arithmetic operator, +, defined in numeric_std to
perform the adding operation.
This operator takes two vectors, of type signed or unsigned, and returns
a result of the same length as the longest operand.
The addition of two n-bit integers produces a result of length n 1,
where the most significant bit is the carry out bit.
Therefore within the VHDL description we must convert Cin from a
single bit to a vector of length n 1, convert A and B to vectors of length
n1 and separate the result into an n-bit sum and a carry out bit.
The code below performs these actions for unsigned addition.
The ampersand, ‘&’, is the concatenation operator.
Thus '0' & unsigned(A) concatenates a single bit and an n-bit vector to give a
vector of length n +1.
So A and B are converted to type unsigned.
After addition of three n 1 bit vectors,
the lowest n bits of the result are converted back to a std_logic_vector and the
most significant bit is taken as the carry out.
Adder for positive numbers
architecture unsgned of NBitAdder is
signal result : unsigned(n downto 0);
signal carry : unsigned(n downto 0);
constant zeros : unsigned(n-1 downto 0) := (others =>'0');
carry <= (zeros & Cin);
result <= ('0' & unsigned(A)) + ('0' & unsigned(B)) + carry;
Sum <= std_logic_vector(result(n-1 downto 0));
Cout <= result(n);
end architecture unsgned;
Adder for signed numbers
The major difference here is that the most significant bits of the A and B vectors
are used to extend those vectors to the left. If A or B is negative, its most
significant bit would be ‘1’ and this must be preserved.
architecture sgned of NBitAdder is
signal result : signed(n downto 0);
signal carry : signed(n downto 0);
constant zeros : signed(n-1 downto 0) := (others => '0');
carry <= (zeros & Cin);
result <= (A(n-1) & signed(A)) + (B(n-1) & signed(B)) + carry;
Sum <= std_logic_vector(result(n-1 downto 0));
Cout <= result(n);
end architecture sgned;
Ripple adder
library IEEE;
use IEEE.std_logic_1164.all;
This model contains two
assignments, to Sum and
Cout. Note that in VHDL,
entity FullAdder is
these two
port (a, b, Cin : in std_logic;
assignments are concurrent
Sum, Cout: out std_logic);
– it does not matter in which
end entity FullAdder;
order statements are written.
The simple rule to
architecture concurrent of FullAdder is
remember is that unless
otherwise stated, all
Sum <= a xor b xor Cin;
Cout <= (a and b) or (a and Cin) or (b and Cin); statements in VHDL
are concurrent.
end architecture concurrent;
Parity checker
library IEEE;
use IEEE.std_logic_1164.all;
entity parity is
port (a : in std_logic_vector;
y : out std_logic);
end entity parity;
architecture iterative of parity is
process (a) is
variable even : std_logic;
even := '0';
for i in a'RANGE loop
If a(i) = '1' then
even := not even;
end if;
end loop;
y <= even;
end process;
end architecture iterative
Note the loop structure here and the
use of attributes, put after an object
with a tick symbol. Various attributes
exist. Also the use of a variable:
A variable can only be declared inside
a process (a signal may not be
declared in a process) and an
assignment to a variable (denoted by
‘:=’) takes immediate effect. A signal
assignment does not take effect until
the process restarts.
State machines in VHDL
Although state machines can be described using concurrent VHDL
constructs, the task is far easier using sequential VHDL. We have seen
that a VHDL process is evaluated when a signal in its sensitivity list
A process may alternatively contain one or more wait statements.
A process cannot have both a sensitivity list and wait statements. A sensitivity
list is equivalent to putting a wait statement with the signals listed at the end
of the process. A state machine changes state at a clock edge. Therefore, the
sensitivity list of a process modelling a state machine must include the clock,
or the clock must be included in a wait statement. A decision to change state
then has to be made on the appropriate clock edge.
The state of the system must be held in an internal variable. The state can be
represented by an enumerated type. The possible values of this type are the state
names, e.g.
type state_type is (G, R); -- e.g for traffic lights
Traffic lights
If a car is detected on the minor road, the
signals change to red for the major road and
green for the minor road. When the lights
change, a timer is started. Once that timer
completes, a ‘TIMED’ signal is asserted,
which causes the lights to change back to
their default state.
library IEEE;
use IEEE.std_logic_1164.all;
entity traffic is
port (
clock, timed, car : in std_logic;
start_timer, major_green, minor_green : out std_logic);
end entity traffic;
Traffic Light Problem
There are two graphical
design tools commonly used:
State diagrams (above) or
ASM charts as on the left
process (clock) is
type state_type is (G, R);
variable state : state_type;
start_timer <= '0';
wait until clock = '1'
case state is
when G =>
major_green <= '1';
minor_green <= '0';
if (car = '1') then
start_timer <= '1';
state := R;
end if;
when R =>
major_green <= '0';
minor_green <= '1';
if (timed = '1') then
state := G;
end if;
end case;
end process;
Synthesis Tools
As most synthesis tools and the 1076.6 RTL Synthesis standard
expect there to be one edge-sensitive statement in a process, it is
not possible to correctly model state machines with Mealy outputs
using a single VHDL process.
A common modelling style for state machines therefore uses two
One process is used to model the state registers, while the second
process models the next state and output logic.
Communication between the
two processes is achieved using the present and next values of the
state registers.
Classical two process FSM – USE this!!!
architecture asm2 of traffic is
type state_type is (G, R);
signal present_state, next_state : state_type;
-- First of two processes…
process (clock) is
if(rising_edge(clock)) then
present_state <= next_state;
end if;
end process seq;
com: process (car, timed, present_state) is
start_timer <= '0';
case present_state is
when G =>
major_green <= '1';
minor_green <= '0';
if (car = '1') then
start_timer <= '1'; -- note this is a Mealy machine.
next_state <= R;
next_state <= G;
end if;
when R =>
major_green <= '0';
minor_green <= '1';
if (timed = '1') then
next_state <= G;
next_state <= R;
end if;
end case;
end process com;
end architecture asm2;
VHDL models of
sequential logic blocks
JK and T flip-flops
Registers and shift registers
Sequential multiplier
Testbenches for sequential building blocks

similar documents