SELECTIVE NOTES FOR
A DIGITAL DESIGN USING VHDL COURSE
Introduction
Traditionally, digital design was a manual
process of designing and capturing circuits using schematic entry tools. This
process has many disadvantages and is rapidly being replaced by new methods.
System designers are always competing to
build cost-effective products as fast as possible in a highly competitive
environment. In order to achieve this, they are turning to using a top-down
design methodologies that include using hardware description language and
synthesis, in addition to just the more traditional process of simulation. A
product in this instance, is any electronic equipment containing Application
Specific Integrated Circuits (ASICs), or Field
Programmable Gate Arrays (FPGAs).
In recent years. Designers have increasingly
adopted top-own design methodologies even though it takes them away from logic
and transistor level design to abstract programming. The introduction of
industry hardware description languages and commercially available synthesis
tools have helped establish this revolutionary design methodology. The
advantages are clear and engineers’ design methods must change. Some of the
advantages are:
Standard “off-the-shelf” integrated circuits
have a fixed functional operation defined by the chip manufacturer. Contrary to
this, both ASIC and FPGAs are types of integrated
circuit whose function is not fixed by the manufacturer. The function is
defined by the designer for a particular application. An ASIC requires a final
manufacturing process to customize its operation while an FPGA does not.
An Application Specific Integrated
Circuit is a device that partially manufactured by an ASIC vendor in
generic form. This initial manufacturing process is the most complex, time
consuming, and expensive part of the total manufacturing process. The result is
silicon chips with an array of unconnected transistors. Another shape of ASICs is based on a standard cells in which there are no
components prefabricated on the silicon chip. The manufacturer creates custom
masks for every stage of the device’s process and means silicon is utilized
more efficiently.
The Field Programmable Gate
Array is a device that is completely manufactured, but that remains design
independent. Each FPGA vendor manufactures devices to a proprietary
architecture. However, the architecture will include a number of programmable
logic blocks that are connected to programmable switching matrices. To
configure a device for a particular functional operations these matrices are
programmed to route signals between the individual logic blocks. Of the most
famous manufacturer of FPGAs are: Actel,
Altera, Atmel, and Xilinx.
In an ideal world, a true top-down system
level design methodology would mean describing a complete system at an abstract
level using a Hardware Description Language (HDL) and the use of automated
tools, for example, partitioners and synthesizers.
This would drive the abstract level description to implementation on PCMs or MCMs (Multichip
Modules), which contain: standard ICs, ASICs, FPGA, PLDs, and full-custom ICs. This ideal is not fulfilled,
however, EDA tools are constantly being improved in the strive towards this
vision. This means designers must constantly take on new rolls and learn new
skills. More time is now spent designing HDL models, considering different
architectures and considering system test and testability issues. Practically
no time is spent designing at the gate level.
Technology advancement over the last six
years or so has seen a tenfold increase in the number of gates that an ASIC can
contain: 100K gates is now common. This has increased the complexity of
standard ICs and ASICs and resulted in the concept,
“system on a chip”. A top-down design methodology is the only practical option
to design such chips.
Any ASIC or FPGA designs in a hardware
development project are usually on the critical path of the development
schedule. Traditionally, such designs have been produced by entering them as
circuit diagrams using a schematic entry tool. In rare cases for reasons of
cost, this may still be a viable design method for small devices such as PLDs. Provided the budget is available for simulation and
synthesis tools, a top-down design approach using a Hardware Description
Language (HDL), is by far the best design philosophy to adopt.
Imagine using schematic to design a 100K
gate ASIC; a small design change could result in major time consuming changes
to the schematics. The philosophy of using a hardware description language to
develop electronic hardware is similar to that of a software development
project using a high-level programming language such as C.
The levels of hierarchical refinement of
electronic hardware in a top-down design process, is shown in Figure x.x. It indicates how synthesis is the key link in this
process.



![]()

![]()
![]()
![]()

![]()
![]()
![]()

Figure x.x Hierarchical refinement of electronic hardware in a top
down design environment
A top-down design methodology takes the HDL
model of hardware, written at a high level of abstraction (system or
algorithmic), down through intermediate levels, to a low (gate or transistor)
level; Figure x.x.
The
term behavior represents the behavior of intended hardware and is independent
of the level of abstraction by which it is modeled. A design represented at the
gate level still represents the behavior of hardware intent. As hardware models
are translated to progressively lower levels they become more complex and
contain more structural detail. The benefit of modeling hardware at higher
levels of behavioral abstraction is that designers are not overwhelmed with
large amounts of unnecessary detail and the complexity of the design task is
reduced.
![]() |
A typical ASIC design flow using simulation
and RTL level synthesis is shown in Figure x.x. The
same test vectors are used to verify the RTL and synthesis netlist
level models. The netlist level corresponds to the
gate level, but may also include larger macro cells, or even bigger mega cells.
By comparing the simulation results at each level testing, netlist
level testing can be automated.

Figure x.x Typical ASIC design flow using simulation and RTL level
synthesis
Hardware Description Language: (HDLs):
A Hardware Description Language
(HDL) is a software programming language used to model the intended operation
of a piece of hardware.
VHDL
VHDL which stands for (Very high
speed integrated circuit Hardware Description Language) is
a programming language that has been designed and optimized for describing the
behavior of digital systems.
Just as high-level
programming languages allow complex design concepts to be expressed as computer
programs, VHDL allows the behavior of complex electronic circuits to be
captured into a design system for automatic circuit synthesis or for system
simulation.
Like
Pascal, C and C++, VHDL includes features useful for structured design
techniques, and offers a rich set of control and data representation features.
Unlike these other programming languages, VHDL provides features allowing
concurrent events to be described. This is important because the hardware
described using VHDL is inherently concurrent in its operation.
History of VHDL
1980:
The
1983:
The development of VHDL began with a joint
effort by IBM, Texas Instruments and Intermetrics.
1987:
The institute of Electrical and Electronics
Engineers (IEEE) was presented with a proposal to standardize the language. The
resulting standard, IEEE 1076-1987, is the basis for virtually every simulation
and synthesis product sold today.
1993:
The VHDL language was revised to IEEE
1076-1993
1996:
A VHDL package for use with synthesis tools
become part of the IEEE 1076 standard, specifically it is 1076.3. This greatly
improved the portability of designs between different synthesis vendor tools.
Another part of the standard, IEEE 1076.4 (VITAL), has been completed and sets
a new standard for modeling ASIC and FPGA libraries in VHDL. This made life
considerably easier for ASIC, FPGA and EDA
tools vendors.
Verilog
Verilog was introduced first before VHDL, thus
established itself as the de facto standard language for ASIC simulation
libraries, Verilog has some advantage in availability
of simulation models. Another important feature that is defined in Verilog is a programming language interface PLI. The PLI
makes it possible for simluation model writers to go
outside of Verilog when necessary to create faster
simulation models, or to create functions (using the C language) that would be
difficult or inefficient to implement directly in Verilog.
1981:
A CAE (Computer Aided Engineering)
software company called Gateway Design Automation was founded by Prabhu Goel.
1983:
Gateway released the Verilog
hardware description language known as “Verilog HDL”
together with a Verilog simulator.
1985:
The language and simulator has enhanced; the
new version of the simulator was called “Verilog-XL”.
1987:
Verilog-XL was becoming very popular and has been
used by many ASIC vendors. Another start-up company, Synopsys,
began to use the proprietary Verilog behavioral
language as an input to their synthesis product.
1989:
Cadence bought Gateway.
1995:
The Verilog
language was reviewed and adopted by IEEE as IEEE standard 1364.
First VHDL example:
As
we previously declared that VHDL is a language that describes a hardware, so
let
us
examine a very simple description of a schematic using VHDL. Our first example
is a simple OR gate.
In VHDL we start our implementation of the
hardware by describing its interface and this piece of information will be
located under a section called the entity, we began by giving the entity a
unique name and then continue to describe its inputs and outputs.
A typical entity description of the OR gate
will be like that:
entity or_gate is
port (a, b:
in bit;
y:
out bit);
end entity or_gate;
In this simple description we defined a
block and then give it a name which is “or_gate” and
then we described its interface, we specified “a, b, y” as the name of the
ports of this block, and then classify these ports with “in” and “out” into
inputs and outputs, we also declared the type pf these ports as “bit’ which
specifies a type that can only handle two values “0” or “1”.
After that we need to write a description
that specifies the function of this block or entity, that can be expressed as:
architecture behavior of or_gate
is
begin
y <= a
or b;
end architecture behavior;
The functionality of the block was specified
using the “architecture” structure which inside it we wrote the line “y <= a
or b” to describe how the inputs and outputs are related.
The entity and architecture are called
design units in VHDL. There are five kinds of design units in VHDL. These are:
entity;
architecture;
package;
package body; and
configuration.
The five kind of design unit are further
classified as primary or secondary units. A primary design unit can exist on
its own.
A secondary design unit cannot exist without its
corresponding primary unit. In other words, it is not possible to analyze a
secondary unit before its primary unit is analyzed.
The entity is a
primary design unit that defines the interface to a circuit as we already
declared. Its corresponding secondary unit is the architecture that defines the
contents of the circuit. There can be many architectures associated with a
particular entity, but this feature is rarely used
The package is
also a primary design unit. A package declares, types, subprograms, operations,
components and other objects which can then be used in the description of the
circuit. The package body is the corresponding secondary design unit which
contains the implementations of subprograms and operations declared in its
package.
The configuration
declaration is a primary design unit with no corresponding secondary. It is
used to define the way in which a hierarchical design is to be built from a
range of subcomponents. However, it is not used for logic synthesis and will
not be covered in this book.
More on entities and architecture
An entity defines the interface to a circuit
and the name of the circuit. An architecture defines the contents of the
circuit itself. Entities and architecture therefore exist in pairs; a complete
circuit will generally have both an entity and an architecture. It is possible
to have an entity without an architecture, but such examples are generally
trivial and of no real use. It is not possible to have an architecture without
an entity.
An example of an
entity is:
entity full_adder
is
port (a, b, c: in bit; sum, count: out bit);
end entity full_adder;
In this case the circuit full_adder
has five ports: three input ports and two output ports. Note that the repeat of
the circuit name full_adder after the end is
optional.
The structure of
an architecture is given by the following example:
architecture behavior of full_adder is
signal sum1, sum2, c1, c2: bit;
begin
sum1 <= a xor b;
c1 <= a and b;
sum2 <= sum1 xor c;
c2 <= sum1 and c;
sum <= c1 or c2;
end architecture behavior;
The architecture has the name behavior and
belongs to the entity full_adder. It is common
practice to use the architecture name behavior for synthesisable
architecture. As with the entity, the repeat of the architecture name after the
end is optional. Common alternatives to architecture behavior are architecture
RTL or architecture synthesis. Architecture names do not need to be unique;
indeed, the use of the same architecture name throughout a VHDL design makes it
easy to tell at a glance whether a VHDL description is system-level (architecture
system), RTL (architecture behavior), or gate-level (architecture netlist). It does not matter what naming convention is used
for architecture, but it is recommended that a consistent naming convention is
adhered to.
The architecture
has two parts: the declarative part and the statement part.
The declarative
part is the part before the keyword begin. In this example, additional
internal signals have been declared here
signals are similar to ports but are internal to the circuit.
A signal declaration
looks like:
signal sum1 : bit;
This declares a signal called sum1,
which has a type, called bit. Types will be dealt with in chapter 4 but
for now it is sufficient to say that bit is a logical type which can be used
for Boolean equation.
The statement
part is the part after the begin. This is the description of the circuit
itself, in this example the statement part only contains signal assignments
describing the full adder as two half adders described by Boolean equations.
The simple signal
assignment looks like:
sum1 <= a xor
b;
The left-hand side
of the assignment is known as the target of the assignment (in this case sum1).
The assignment itself has the symbol “<=” which is usually read “gets”, as
in “signal sum1 gets a xor b”.
The right-hand side
of the assignment is known as the source of the assignment. The source
expression can be as complex as you like. For example, the circuit of the full_adder example could have been written using just two
signals assignment:
sum <= a xor b xor c;
count <= (a and b) or (a and c)
or (b and c);
In this example the statements have been
written in sequence so that the dataflow is from up to bottom. However, this is
done readability only; the ordering of the statements is irrelevant. This is
because each statement simply defines a relationship between its inputs (the
source, on the right-hand side of the assignment) and its output (the target,
on the left-hand side).
For example, the
following architecture is functionally equivalent to the previous version:
architecture behavior of full_adder is
signal sum1, sum2, c1, c2: bit;
begin
count <= c1 or c2;
sum <= sum2;
c2 <= sum1 and c;
c1 <= a andb;
sum2 <= sum1 xor b;
sum1 <= a xor b;
end architecture behavior;
Signals and ports
Signals
are the carriers of data values around an architecture. Ports are the same as
signals but also provide an interface through the entity so that the entity can
be used as a subcircuit in a hierarchical design.
A signal is
declared in the declarative part of an architecture (between the
keywords is and begin) and the declaration has two parts:
architecture behavior of adder is
signal a, b, c: bit;
begin
…
The first part is a list of signal names; in
this case there are three signals, a, b, and c. the second part, after the
colon, is the type of the signals: in this case bit.
There can be many
signal declaration in an architecture, each terminated by a semi-colon. The
above declaration could be rewritten as three separate declarations:
architecture behavior of adder is
signal a: bit;
signal b: bit;
signal c: bit;
begin
…
Port declarations are enclosed by a port
specification in the entity. The port specification has the following
structure:
entity adder is
port (port specification);
end entity adder;
Note that the port specification is always
terminated by a semi-colon which is outside the parentheses.
A port is declared
within the port specification. A port declaration has three parts:
entity adder is
port (a, b, c : in bit);
end entity adder;
The first part is a list of port names: in
this case a, b and c. the second part is the mode of the port; in this case the
mode is in. the third part is the type as in the signal declaration; in this
case the type is bit.
Each port
declaration within the specification is separated by semi-colons from the
others. Note that, unlike the signal declarations which are each terminated by
semi-colon, port declarations are separated (not terminated) by semi-colons, so
there is no semi-colon after the last declaration before the closing
parenthesis. The full_adder entity example is
reproduced here to show how multiple port declarations are listed:
entity full_adder
is
port (a, b, c: in bit; sum, count: out bit);
end entity full_adder;
The mode of the
port determines the direction of data-flow through the port. There are five
port modes: in, out, inout, buffer, and linkage. If a
mode is not given, then mode in will be assumed.
The meaning of the
modes as they are used for logic synthesis are:
in input port – cannot be assigned to in
the circuit, can be read;
out output port – must be assigned to in the
circuit, cannot be read;
inout
bidirectional port – can only be used for tristate
buses;
buffer output port – like mode out but can also be
read;
linkage not used
by synthesis.
There is often confusion between mode out
and mode buffer. Mode buffer is
an anachronism and it is an understatement to say that the reason for its
existence in the language is obscure. The full behavior of a buffer port is a
restricted form of mode input. However, to make the mode usable for synthesis,
the rules for buffer ports are constrained so that they act like mode out ports
with the added convenience that it is possible to read from the port within the
architecture.
The behavior of
the modes is illustrated by the following example; inout
mode is omitted because it is specific to tristates
which are dealt with in chapter 12.
entity modes is
port (input: in bit;
out1: buffer bit;
out2: out bit);
end entity modes;
architecture behavior of modes is
begin
out1 <= input;
out2 <= out1;
end architecture behavior;
Port inout, being
mode in, cannot be assigned to and so can only appear on the right-hand side of
a signal assignment. Port out2, being mode out, can only be assigned to and so
can only appear on the left-hand side of a signal assignment. Port out1,
however, being mode buffer, can be assigned to and read and so can appear on
either side of an assignment. It has been named out1 in this example because it
is acting as an output.
Unfortunately, the
rules for mode buffer are more complex than this; this interpretation of mode
buffer is a simplification to make the mode useful for synthesis and is common
to most synthesizers. The full rules also prevent mode buffer and mode out from
being mixed in a hierarchical design. That is to say, it is not possible to
connect a buffer mode port of a subcomponent to an out mode port of a higher
level component.
This restriction
on the mixing of modes can cause inconvenience, at the very least. It is
therefore recommended that only one of the output modes is ever used.
Furthermore, owing to the anachronistic nature of mode buffer, it is
recommended that only out mode is used.
The problem of
needing to read from an out mode port is illustrated by the following example
of an and gate with true and inverted output. The first version shows an illegal
description, because the out port z is read:
entity and_nand
is
port (a, b: in bit;
z, zbar: out
bit);
end entity and_nand;
architecture illegal of and_nand is
begin
z <= a and b;
zbar <= not z;
end architecture illegal;
The solution is to use an intermediate
internal signal and read from that. The intermediate signal can then be
assigned to the out mode ports.
The corrected
example is:
entity and_nand
is
port (a, b: in bit;
z, zbar: out
bit);
end entity and_nand;
architecture behavior of and_nand is
signal result: bit;
begin
result <= a and b;
z <= result;
zbar <= not result;
end architecture behavior;
It is good practice always to use
intermediate signals for outputs and to assign them to the out ports at the end
of the architecture. By doing this consistently, the pitfall of trying to read
an out port is always avoided.
Simple signal assignment
The simple signal assignment statement has
already been used in the full_adder example. The
section examines the signal assignment
statement in more detail.
The simple signal
assignment looks like this:
x <= a xor
b;
The left-hand side of the assignment is
known as the target of the assignment (in this case x). The right-hand
side of the assignment is known as the source expression of the
assignment (in this case a xor b).
The assignment itself is the symbol <=,
which is formed by combining the less-than and the equals symbols to form an
arrow. There must be no space between the two characters in the symbol. Beware
of confusion with the less-than-or-equal operator <= which looks exactly the
same. Fortunately, there is no real chance of confusion because there are no
situations where both meanings would be allowed by the language. Nevertheless,
it takes some getting used to.
The rules of VHDL
insist that the source of an assignment is the same type as the target.
This example uses type bit. The xor of two signals of
type bit gives a result which is also a bit. Therefore, the source and target
are of the same type.
The source
expression can be as complex as desired. For example, the circuit of the full_adder example could have been written using just two
signal assignments:
sum <= a xor
b xor c;
count <= (a and b) or (a and c)
or (b and c);
Combinational logic blocks
Multiplexers
A multiplexer selectively passes the value
of one, of two or more iput signals, to the output.
One or more control signals control which input signal’s value is passed to the
output, see figure x.x. Each input signal, and the
output signal, may represent single bit or multiple bits busses. The select
inputs are normally binary encoded such that n select inputs can select from
one of up to 2n inputs.

Figure x.x circuit symbol of 2-1 multiplexer
|
S |
A |
B |
Y |
|
0 |
0 |
- |
0 |
|
0 |
1 |
- |
1 |
|
1 |
- |
0 |
0 |
|
1 |
- |
1 |
1 |
Table x.x Truth table of 2-1 multiplexer

Figure x.x logic implementation of 2-1 multiplexer
The model of the 2-1 multiplexer described
above is shown modeled by two different methods: first, using a concurrent
selected signal assignment, second using the if statement in its most
simplest form.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity mux_2_1 is
port (s, a,
b: in std_logic;
y:
out std_logic) ;
end entity mux_2_1;
architecture first of mux_2_1 is
begin
y <= a
when s = ‘1’ else
b;
end architecture first;
architecture second of mux_2_1 is
begin
process (s,
a, b)
begin
if (s =
‘1’) then
y <=
a;
else
y <=
b;
end if;
end
process;
end architecture second;
Example x.x
Modeling of a 4-1
multiplexer
Four ways of modeling a 4-1 mutliplexer are indicated. They are:
There is no incorrect modeling method,
however using the case statement requires less code and is easier to read when
compared with the if statement. This becomes more distinct with increasing
inputs per output. The first and second models use concurrent signal
assignments so reside outside a process. This means they are always active and
so will usually take longer to simulate.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity mux4_1 is
port (sel: in std_logic_vector (2 downto 0);
a, b,
c, d: in std_logic;
y: out std_logic);
end entity mux4_1;
architecture first of mux4_1 is
begin
y <= a
when sel = “00” else
b when
sel = “01” else
c when
sel = “10” else
d; --when sel
= “11”
end architecture first;
architecture second of mux4_1 is
begin
with sel select
y <= a when “00”,
b
when “01”,
c
when “10”,
d
when “11”,
a
when others;
end architecture second;
architecture third of mux4_1 is
begin
process (sel, a, b, c, d)
begin
if (sel = “00”) then
y <= a;
elsif (sel =
“01”) then
y <= b;
elsif (sel = “10”) then
y <= c;
else
y <=
d;
end if;
end
process;
end architecture third;
architecture fourth of mux4_1 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 “11” => y <= d;
when
others => y <= a;
end case;
end
process,
end architecture fourth;
A four-bit wide 8-1 multiplexer is modeled
using selected signal assignment and case statement. The use of case
statement instead of the if will make the codes easier to read. It is
different from the previous example in that an integer data type is used for
the select input sel which eliminates the need of the
others clause as a default action.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity mux8_1 is
port (sel: in
std_logic_vector(2 downto 0);
a0, a1, a2, a3, a4, a5, a6, a7:
in std_logic_vector(3 downto
0);
y: out std_logic_vector(3
downto 0));
end entity mux8_1;
architecture first of mux8_1 is
begin
with sel
select
y <= a0 when “000”,
a1
when “001”,
a2 when “010”,
a3 when “011”,
a4 when “100”,
a5 when “101”,
a6 when “110”,
a7 when “111”,
a0
when others;
end architecture first;
architecture second of mux8_1 is
begin
process (sel,
a0, a1, a2, a3, a4, a5, a6, a7)
begin
case sel is
when “000” => y <= a0;
when “001” => y <= a1;
when “010” => y <= a2;
when “011” => y <= a3;
when “100” => y <= a4;
when “101” => y <= a5;
when “110” => y <= a6;
when “111” => y <= a7;
when
others => y <= a0;
end case;
end process;
end architecture second;
Encoders
Discrete quantities of digital information,
data are often represented in a coded form; binary being the most popular.
Encoders are used to encode data into a coded form and decoders are used to
convert it back into its original undecoded form. An
encoder that has 2n (or less) input lines encodes input data to
provide n encoded output lines. The truth table for an 8-3 binary encoder (8
inputs and 3 outputs) is shown in Table x.x. It is
assumed that only one input has a value of 1 at any given time, otherwise the
output has some undefined value and the circuit is meaningless.

Figure x.x Encoder
|
inputs |
outputs |
|||||||||
|
A7 |
A6 |
A5 |
A4 |
A3 |
A2 |
A1 |
A0 |
Y2 |
Y1 |
Y0 |
|
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
|
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
|
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
|
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
|
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
|
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
|
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
|
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
Table x.x Truth table of a 8-3 binary encoder
All models use a default assigned output
value to avoid having to explicitly define all 28 – 8 = 248 input
conditions that should not occur under normal operating conditions. The default
assignment is a “don’t care” value to minimize synthesized logic. If all 248
input conditions that are not explicitly defined are made like that they
default to other value than “don’t care” (for example binary 000), more logic
would be synthesized than is necessary.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity encoder8_3 is
port (a: in std_logic_vector(7
downto 0);
y: out std_logic_vector(2
downto 0));
end entity encoder8_3;
architecture first of encoder8_3 is
begin
y <= “000” when a = “00000001” else
“001” when a = “00000010” else
“010” when a = “00000100” else
“011” when a = “00001000” else
“100” when a = “00010000” else
“101” when a = “00100000” else
“110” when a = “01000000” else
“111” when a = “10000000” else
“---”;
end architecture first;
architecture second of encoder8_3 is
begin
with a select
y <= “000” when “00000001”,
“001” when “00000010”,
“010” when “00000100”,
“011” when “00001000”,
“100” when “00010000”,
“101” when “00100000”,
“110” when “01000000”,
“111” when “10000000”,
“---” when others;
end architecture second;
architecture third of encoder8_3 is
begin
process (a)
begin
if (a = “00000001”) then y <=
“000”;
elsif (a = “00000010”) then y <= “001”;
elsif (a =
“00000100”) then y <= “010”;
elsif (a =
“00001000”) then y <= “011”;
elsif (a =
“00010000”) then y <= “100”;
elsif (a =
“00100000”) then y <= “101”;
elsif (a =
“01000000”) then y <= “110”;
elsif (a =
“10000000”) then y <= “111”;
else y <= “---”;
end if;
end process;
end architecture third;
architecture fourth of encoder8_3 is
begin
process (a)
begin
case a is
when “00000001” => y <=
“000”;
when “00000010” => y <=
“001”;
when “00000100” => y <=
“010”;
when “00001000” => y <=
“011”;
when “00010000” => y <=
“100”;
when “00100000” => y <=
“101”;
when “01000000” => y <=
“110”;
when “10000000” => y <=
“111”;
when others
=> y <= “---”;
end case;
end process;
end architecture fourth;
Decoders are used to decode data that has
been previously encoded using a binary, or possibly other, type of coded
format. An n-bit code can represent up to 2n distinct bits of coded
information, so a decoder with n inputs can decode up to 2n outputs.
Various models of a 3-6 binary decoder are included in example x.x.

Figure x.x Decoder
|
inputs |
outputs |
|||||||||
|
A2 |
A1 |
A0 |
Y7 |
Y6 |
Y5 |
Y4 |
Y3 |
Y2 |
Y1 |
Y0 |
|
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
|
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
|
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
|
0 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
|
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
|
1 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
|
1 |
1 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
|
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Table x.x Truth table of a 3-8 binary decoder
The models of the 3-8 binary decoders in
this example conform to the truth table in Table x.x.
Different model versions using
conditional, selected signal assignments along with typical if, and case
statements.
Again,
there is no write or wrong modeling technique. The case statement is
commonly used because of its clarity, and the fact it is not a continuous
assignment and so may simulate faster.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity decoder3_8 is
port (a: in std_logic_vector(2
downto 0);
y: out std_logic_vector(7
downto 0));
end entity decoder3_8;
architecture first of decoder3_8 is
begin
y <= “00000001” when a = “000” else
“00000010” when a = “001” else
“00000100” when a = “010” else
“00001000” when a = “011” else
“00010000” when a = “100” else
“00100000” when a = “101” else
“01000000” when a = “110” else
“10000000” when a = “111” else
“00000001”;
end architecture first;
architecture second of decoder3_8 is
begin
with a select
y <= “00000001” when “000”,
“00000010” when “001”,
“00000100” when “010”,
“00001000” when “011”,
“00010000” when “100”,
“00100000” when “101”,
“01000000” when “110”,
“10000000” when “111”,
“00000001” when others;
end architecture second;
architecture third of decoder3_8 is
begin
process (a)
begin
if (a = “000”) then y <=
“00000001”;
elsif (a =
“001”) then y <= “00000010”;
elsif (a =
“010”) then y <= “00000100”;
elsif (a =
“011”) then y <= “00001000”;
elsif (a =
“100”) then y <= “00010000”;
elsif (a =
“101”) then y <= “00100000”;
elsif (a =
“110”) then y <= “01000000”;
elsif (a =
“111”) then y <= “10000000”;
else y <= “00000001”;
end if;
end process;
end architecture third;
architecture fourth of decoder3_8 is
begin
process (a)
begin
case a is
when “000” => y <=
“00000001”;
when “001” => y <=
“00000010”;
when “010” => y <=
“00000100”;
when “011” => y <=
“00001000”;
when “100” => y <=
“00010000”;
when “101” => y <=
“00100000”;
when “110” => y <=
“01000000”;
when “111” => y <=
“10000000”;
when others
=> y <= “00000001”;
end case;
end process;
end architecture fourth;
Example
x.x Modeling of a 3-6 binary decoder with enable
|
Inputs |
outputs |
||||||||||
|
En |
A2 |
A1 |
A0 |
Y5 |
Y4 |
Y3 |
Y2 |
Y1 |
Y0 |
||
|
0 |
- |
- |
- |
0 |
0 |
0 |
0 |
0 |
0 |
||
|
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
||
|
1 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
||
|
1 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
||
|
1 |
0 |
1 |
1 |
0 |
0 |
1 |
0 |
0 |
0 |
||
|
1 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
||
|
1 |
1 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
||
|
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
- =don’t care |
||
|
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
||
Table x.x Truth table of a 3-6 binary decoder with enable
The model version of the 3-6 binary decoder
conforms to the truth table Table x.x.
This example is different from the previous one because it has a separate input
enable signal and there are two unused binary values for the 3-bit input A.
When the enable is inactive (En = 0), or A has an unused value, the 6-bit
output must be at logic 0.
The model below
uses an if statement to check the enable input En, separately from the
enclosed case statement.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity decoder3_6 is
port (en: in std_logic;
a: in
std_logic_vector(2 downto
0);
y: out std_logic_vector(5
downto 0));
end entity decoder3_6;
architecture first of decoder3_6 is
begin
process (a)
begin
if (en = ‘0’) then
y <=
“000000”;
else
case a
is
when “000” => y <=
“000001”;
when “001” => y <= “000010”;
when “010” => y <=
“000100”;
when “011” => y <=
“001000”;
when “100” => y <=
“010000”;
when “101” => y <=
“100000”;
when others => y <= “000000”;
end case;
end if;
end process;
end architecture first;
Comparators
A comparator compares two or more inputs
using one, or a number of different comparisons. When the given relationship(s)
is true, an output signal is given (logic 0 or logic1). Comparators are only
modeled using the if statement with an else clause and no else-if
clauses. Any two data objects are compared using equality and relational
operators in the expression part of the if statement. Only two data objects can
be compared at once, that is, statements like “if (a = b = c)” cannot be
used. However, logical operators can be used to logically test the result of
multiple comparisons, for example, “if ((a = b) and (a = c))”. These
equality, relational and logical operators are listed in Table x.x.
|
Operators |
VHDL |
|
Equality & Relational |
= |
|
/= |
|
|
|
< |
|
|
<= |
|
|
> |
|
|
>= |
|
Logical |
not |
|
|
and |
|
|
or |
Table x.x Equality, relational and logical operators
Example x.x 6-bit
two input equality comparator:

Figure x.x 6-bit two input equality comparator
Typical comparator is shown in figure x.x. The single bit output is at logic ‘1’ when the two
6-bit input buses are the same, otherwise it is at logic ‘0’. Note that the
inputs buses to be compared can’t be of type std_logic_vector,
they can be whether unsigned or signed types, as comparison must be preformed
between signals or variables that have a defined values and since std_logic_vector does not indicate a value of the bus, so
it should not be used in comparison.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity simple_comparator
is
port (a, b: in unsigned(5 downto 0);
y: out std_logic);
end entity simple_comparator;
architecture rtl of simple_comparator
is
begin
process (a, b)
begin
if (a = b) then
y <=
‘1’;
else
y <=
‘0’;
end if;
end process;
end architecture rtl;
Example x.x
Multiple Comparison Comparator:
Extra parentheses enclosing “c /= d or
e >= f” means that either one of the these conditions and “a = b” must be
true for the output to be at logic 1.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity multiple_comparator
is
port (a, b, c, d, e, f: in unsigned(2 downto 0);
y: out std_logic);
end entity multiple_comparator;
architecture rtl of multiple_comparator
is
begin
process (a, b, c, d, e, f)
begin
if (a = b and (c /= d or e >=f))
then
y <=
‘1’;
else
y <=
‘0’;
end if;
end process;
end architecture rtl;
A latch is a level sensitive memory cell
that is transparent to signals passing from
thr D input to Q output when enabled, and
holds the value of D on Q at the time when it becomes disabled. The Q-bar
output signal is always the inverse of the Q output signal, see Figure x.x.

Figure x.x The level sensitive D-type transparent latch
|
inputs |
outputs |
||||
|
D |
EN |
Q+ |
|
||
|
- |
0 |
Q- |
|
||
|
0 |
1 |
0 |
- =don’t care |
||
|
1 |
1 |
1 |
0 |
||
Table x.x Characteristic table of a D-type transparent latch

Figure x.x Simulation of the level sensitive D-type transparent
latch
The if statement without else
clause is the simplest implementation of D-type transparent latch.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity d_latch is
port (d, en: in std_logic;
q: out std_logic);
end entity d_latch;
architecture rtl of d_latch
is
begin
process (d, en)
begin
if (en = ‘1’) then
q <=
d;
end if;
end process;
end architecture rtl;
Latches with clear will set the output to ‘0’
whenever the clear signal goes to ‘1’. Latches with preset will preset the
output to ‘1’ instead to ‘0’. Clear and preset inputs to a latch are always
asynchronous with the enable. The example code above models a latch with a
clear input
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity d_latch is
port (d, en, clear: in std_logic;
q: out std_logic);
end entity d_latch;
architecture rtl of d_latch
is
begin
process (d, en, clear)
begin
if (clear = ‘1’) then
q <=
‘0’;
elsif (en = ‘1’) then
q <=
d;
end if;
end process;
end architecture rtl;
The D-type flip-flop is an edge triggered
memory device that transfers a signal’s value on its D input, to its Q output,
when an active edge transition occurs on its clock input. The output value is
held until the next active clock edge. The Q-bar output signal is always the
inverse of the Q output signal, see Figure x.x.

Figure x.x The level sensitive D-type transparent latch
|
inputs |
outputs |
||
|
D |
CLK |
Q+ |
|
|
0 |
|
Q- |
|
|
1 |
|
|
|
|
- |
0 |
Q- |
|
|
- |
1 |
Q- |
|
Table x.x Characteristic table of a D-type transparent latch

Figure x.x Simulation of a +ve edge
sensitive D-type flip-flop
Different models of flip-flops with +ve and –ve edge triggered are
shown in architectures below. The first and the second architectures use a VHDL
attributes to detect the clk signal edge, while the
third and fourth architectures use a function call for the same purpose.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity d_flip_flop is
port (d, clk:
in std_logic;
q: out std_logic);
end entity d_flip_flop;
architecture first of d_flip_flop is
begin
process (clk)
begin
if (clk’event
and clk = ‘1’) then
q <=
d;
end if;
end process;
end architecture first;
architecture second of d_flip_flop
is
begin
process (clk)
begin
if (clk’event
and clk = ‘0’) then
q <=
d;
end if;
end process;
end architecture second;
architecture third of d_flip_flop
is
begin
process (clk)
begin
if (rising_edge(clk)) then
q <=
d;
end if;
end process;
end architecture third;
architecture fourth of d_flip_flop
is
begin
process (clk)
begin
if (falling_edge(clk)) then
q <=
d;
end if;
end process;
end architecture fourth;
Different flip-flops with asynchronous and
synchronous resets are modeled. The first architecture models a D-type
flip-flop with an asynchronous reset input, this is the commonly used D-type
flip-flop, while the second architecture models D-type flip-flop with a
synchronous reset as appears from the process sensitivity list and the if
statements sequence.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity d_flip_flop is
port (d, clk,
rst: in std_logic;
q: out std_logic);
end entity d_flip_flop;
architecture first of d_flip_flop is
begin
process (rst,
clk)
begin
if (rst = ‘1’) then
q <=
‘0’;
elsif (rising_edge(clk)) then
q <=
d;
end if;
end process;
end architecture first;
architecture second of d_flip_flop
is
begin
process (clk)
begin
if (rising_edge(clk)) then
if (rst = ‘1’) then
q
<= ‘0’;
else
q
<= d;
end if;
end if;
end process;
end architecture second;
Flip-flop with enable input and asynchronous
reset is modeled to illustrate the addition of the enable signal control, which
is synchronous, accordingly with the asynchronous reset.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity d_flip_flop is
port (d, clk,
rst, en: in std_logic;
q: out std_logic);
end entity d_flip_flop;
architecture rtl of d_flip_flop
is
begin
process (rst,
clk)
begin
if (rst =
‘1’) then
q <=
‘0’;
elsif (rising_edge(clk)) then
if (en
= ‘1’) then
q
<= d;
end if;
end if;
end process;
end architecture rtl;
A parallel register is simply a bank of
D-type flip-flops, its implementation has the same structure as for one
flip-flop but with extended inputs and outputs, see Figure x.x.
input bus output bus

Figure x.x Register with n inputs and n outputs
A typical example for a parallel 8 bits
register is modeled, based on the flip-flop model shown previously but with
inputs and outputs are extended as buses.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity reg_par is
port (clk, rst: in std_logic;
reg_in: in std_logic_vector(7 downto 0);
reg_out:
out std_logic_vector(7 downto
0));
end entity reg_par;
architecture rtl of reg_par
is
begin
process (rst,
clk)
begin
if (rst =
‘1’) then
reg_out <= “00000000”;
elsif (rising_edge(clk)) then
reg_out <= reg_in;
end if;
end process;
end architecture rtl;
The same as the previous example but with an
added enable signal to provide control over the register, so that the register
only register the value at its inputs when enable is active, otherwise
remaining the contents unchanged. Also, an others statement in the reset
action line is added to fill registers with any size with zeros. This model is
also based on the flip-flop model shown in example x.x.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity reg_par is
port (clk, rst, en: in std_logic;
reg_in: in std_logic_vector(7 downto 0);
reg_out:
out std_logic_vector(7 downto
0));
end entity reg_par;
architecture rtl of reg_par
is
begin
process (rst,
clk)
begin
if (rst =
‘1’) then
reg_out <= (others => ‘0’);
elsif (rising_edge(clk)) then
if (en
= ‘1’) then
reg_out <= reg_in;
end if;
end if;
end process;
end architecture rtl;
Shift registers are registers with added
features that enables them to shift their contents in either directions. A
typical parallel shift register that has an added controls to achieve these
features; signals shift_left and shift_right
see Figure x.x. These signals control whether the
register is to shift its contents to left or right. Depending on the usage of
the register, the empty place appears after shifting is filled whether with
‘0’, or ‘1’, or the shifted bit from the other side of the register (barrel
shifters), see Figure x.x.
signals simply a bank of D-type flip-flops,
its implementation has the same structure as for one flip-flop but with
extended inputs and outputs, see Figure x.x.

Figure x.x Parallel shift register with added control shift_left, and shift_right