Free Range VHDL 2023 1
Free Range VHDL 2023 1
The electronic version of this book could originally be downloaded free of charge
from: http://www.freerangefactory.org. At the time of this update, the original
website was still unavailable. The improvements were based on the LATEX source kindly
released by the authors.
The authors have taken great care in the preparation of this book, but make no expressed
or implied warranty of any kind and assume no responsibility for errors or omissions.
No liability is assumed for incidental or consequential damages in connection with or
arising out of the use of the information or programs contained in this book.
Acknowledgments v
1 Introduction To VHDL 5
1.1 Golden Rules of VHDL 8
1.2 Tools Needed for VHDL Development 8
2 VHDL Invariants 11
2.1 Case Sensitivity 11
2.2 White Space 11
2.3 Comments 12
2.4 Parentheses 13
2.5 VHDL Statements 13
2.6 if, case and loop Statements 14
2.7 Identifiers 14
2.8 Reserved Words 15
2.9 VHDL Coding Style 15
3.3 Architecture 22
3.4 Signal and Variable Assignments 24
3.5 Summary 26
3.6 Exercises 27
6 VHDL Operators 75
6.1 Logical Operators 76
6.2 Relational Operators 76
6.3 Shift Operator 76
6.4 Other Operators 78
6.5 Concatenation Operator 78
6.6 Modulus and Remainder Operators 78
iii
The authors would like to thank Christina Jarron for her invaluable contri-
bution to proofreading this book and for her useful suggestions. Special
thanks also to Rob Ash for helping us make the cover of the book distinct-
ive with his original artwork. A massive thank you goes to Keith Knowles
for his time and effort in reviewing and editing the final draft of this book.
Finally, the authors would like to thank all the people who have provided
feedback and suggestions.
The main incentive for the update came from the students who liked
the style of the book due to the quick results that it made possible.
Corrections and improvements kept the spirit of allowing this dynamic and
fun introduction to VHDL.
Purpose of this book
The purpose of this book is to provide students and young engineers with
a guide to help them develop the skills necessary to be able to use VHDL
for introductory and intermediate level digital design. These skills will also
give you the ability and the confidence to continue on with VHDL-based
digital design. In this way, you will also take steps toward developing
the skills required to implement more advanced digital design systems.
Although there are many books and on-line tutorials dealing with VHDL,
these sources are often troublesome for several reasons. Firstly, much of
the information regarding VHDL is either needlessly confusing or poorly
written. Material with these characteristics seems to be written from the
standpoint of someone who is either painfully intelligent or has forgotten
that their audience may be seeing the material for the first time. Secondly,
the common approach for most VHDL manuals is to introduce too many
topics and a lot of extraneous information too early. Most of this material
would best appear later in the presentation. Material presented in this
manner has a tendency to be confusing, is easily forgotten if misunderstood
or simply is never applied. The approach taken by this book is to provide
only what you need to know to quickly get up and running in VHDL.
As with all learning, once you have obtained and applied some useful
information, it is much easier to build on what you know as opposed
to continually adding information that is not directly applicable to the
2
subjects at hand.
The intent of this book is to present topics to someone familiar with digital
logic design and with some skills in algorithmic programming languages
such as Java or C. The information presented here is focused on giving a
solid knowledge of the approach and function of VHDL. With a logical and
intelligent introduction to basic VHDL concepts, you should be able to
quickly and efficiently create useful VHDL code. In this way, you will see
VHDL as a valuable design, simulation and test tool rather than another
batch of throw-away technical knowledge encountered in some forgotten
class or lab.
Lastly, VHDL is an extremely powerful tool. The more you understand
as you study and work with VHDL, the more it will enhance your learning
experience independently of your particular area of interest. It is well worth
noting that VHDL and other similar hardware design languages are used to
create most of the digital integrated circuits found in the various electronic
gizmos that overwhelm our modern lives. The concept of using software
to design hardware that is controlled by software will surely provide you
with endless hours of contemplation. VHDL is a very exciting language
and mastering it will allow you to implement systems capable of handling
and processing in parallel ns-level logic events in a comfortable software
environment.
This book was written with the intention of being freely available to
everybody. The formatted electronic version of this book is available from
the Internet. Any part of this book can be copied, distributed and modified
in accordance with the conditions of its license.
DISCLAIMER: This book quickly takes you down the path toward
understanding VHDL and writing solid VHDL code. The ideas presented
herein represent the core knowledge you will need to get up and running
with VHDL. This book in no way presents a complete description of the
VHDL language. In an effort to expedite the learning process, many of the
finer details of VHDL have been omitted from this book. Anyone who has
the time and inclination should feel free to further explore the true depth
of the VHDL language. There are many on-line VHDL reference books and
3
free tutorials. If you find yourself becoming curious about what this book
is not telling you about VHDL, take a look at some of these references.
Another aspect of this quick approach is that it must be agnostic to the
complete design flow for a specific hardware, which involves many crucial
steps which depend on various tools and target technologies.
Introduction To VHDL
1
VHDL has a rich and interesting history1 . But since knowing this history
is probably not going to help you write better VHDL code, it will only be
briefly mentioned here. Consulting other, lengthier texts or search engines
will provide more information for those who are interested. Regarding
the VHDL acronym, the V is short for yet another acronym: VHSIC
or Very High-Speed Integrated Circuit. The HDL stands for Hardware
Description Language. Clearly, the state of technical affairs these days has
done away with the need for nested acronyms. VHDL is a true computer
language with the accompanying set of syntax and usage rules. But, as
opposed to higher-level computer languages, VHDL is primarily used to
describe hardware. The tendency for most people familiar with a higher-
level computer language such as C, Python or Java is to view VHDL as
just another computer language. This is not altogether a bad approach if
such a view facilitates the understanding and memorization of the language
syntax and structure. The common mistake made by someone with this
approach is to attempt to program in VHDL as they would program
a higher-level computer language. Higher-level computer languages are
sequential in nature; VHDL is not.
VHDL was invented to describe hardware and in fact VHDL is a con-
current language. What this means is that, normally, VHDL instructions
are all executed at the same time (concurrently), regardless of the size of
1
VHDL-Wikipedia: http://en.wikipedia.org/wiki/VHDL
6 Chapter 1: Introduction To VHDL
2
Synthesis: the process of interpreting VHDL code and outputting a definition of
the physical circuit implementation to be programmed on a device such as an FPGA.
3
It is not really magic. There is actually a well-defined science behind it.
7
now is to learn how to properly use VHDL to describe what you want to
implement.
implementations. If you want to do VHDL coding for FPGAs you will have
to play within the rules that current major FPGA manufacturers have
drawn up to help you (rules which also ensure their continued existence in
the market).
The successful implementation of a VHDL-based system roughly calls
for the following steps: VHDL code writing, compiling, simulation and
synthesis. All major FPGA manufacturers have a set of software and
hardware tools that you can use to perform the mentioned steps. Most of
these software tools are free of charge (occasionally with limitations) but
are not open-source. Nevertheless, the same tools follow a license scheme,
whereby paying a certain amount of money allows you to take advantage
of sophisticated software features, access to high-end hardware targets or
get your hands on proprietary libraries with lots of components (e.g. a
32-bit processor) that you can easily include in your own project.
If your have no interest in the advanced features offered by the paid tools,
you can use open-source solutions (e.g. GHDL4 and NVC5 are quite actively
maintained) which will allow you to compile and simulate your VHDL
code using the open-source tool gcc6 . At the time of writing, no open-
source solution is available for the synthesis process with broad hardware
support (although it seems to be worth following the F4PGA Project7 ).
However synthesis can be accomplished using a free-license version of any
major FPGA manufacturer’s software tool (e.g. AMD/Xilinx Vivado or
Intel/Altera Quartus).
Thanks to the open-source community, you can write, compile and
simulate VHDL systems using excellent open-source solutions, even without
installing software locally8 . This book will show you how to get up and
running with the VHDL language. For further tasks such as synthesis and
upload of your code into an FPGA, the free of charge versions of the main
manufacturers tools can be employed.
4
VHDL simulator GHDL: http://ghdl.free.fr
5
VHDL software tool NVC: https://www.nickg.me.uk/nvc/
6
Multi-language open-source compiler GCC: http://gcc.gnu.org
7
F4PGA Project: https://f4pga.readthedocs.io
8
EDA Playground: https://www.edaplayground.com/
VHDL Invariants
2
There are several features of VHDL that you should know before moving
forward. Although it is rarely a good idea for people to memorize anything,
you should memorize the basic concepts presented in this section. This
should help eliminate some of the drudgery involved in learning a new
programming language and lay the foundation that will enable you to create
visually pleasing and good VHDL source code. Don’t worry about fully
understanding the statements in the Listings presented in this Chapter.
that proper indenting using spaces can make the code a lot clearer.
2.3 Comments
Comments in VHDL begin with the symbol “--” (two consecutive dashes).
The VHDL synthesizer ignores anything after the two dashes and up to
the end of the line in which the dashes appear. Listing 2.3 shows two types
of commenting styles. The block-style comments \* ... *\ (that span
multiple lines without requiring comment marks on every line) have been
introduced in the 2008 version of VHDL. Keep in mind that every newer
feature used increases the chance of problems when migrating the code
from one tool to another.
2.4 Parentheses
VHDL is relatively lax on its requirement for using parentheses. Like other
computer languages, there are a few precedence rules associated with the
various operators in the VHDL language. Though it is possible to learn
all these rules and write clever VHDL source code that will ensure the
readers of your code are left scratching their heads, a better idea is to
practice liberal use of parentheses to ensure the human reader of your
source code understands the purpose of the code. Once again, the two
statements appearing in Listing 2.4 have the same meaning. Note that
extra white space has been added along with the parentheses to make the
lower statement clearer.
2.7 Identifiers
An identifier refers to the name given to various items in VHDL. Examples
of identifiers in higher-level languages include variable names and function
names. Examples of identifiers in VHDL include variable names, signal
names and port names (all of which will be discussed soon). Listed below
are the hard and soft rules (i.e. you must follow them or you should follow
them), regarding VHDL identifiers.
Identifiers should be self-describing. The text you apply to identifiers
should inform the use and purpose of the item it represents.
Identifiers must not end with an underscore and must never have two
consecutive underscores.
The best identifier for a function that calculates the position of the Earth
is CalcEarthPosition or calc earth position. Try to be consistent.
Intelligent choices for identifiers make your VHDL code more readable,
understandable and more impressive to coworkers, superiors, friends etc.
Adopting a good coding style helps you write code without mistakes.
As with other compilers you have experience with, you will find that
the VHDL compiler does a great job of knowing a document has an
error but a marginal job at telling you where or what the error is.
Using a consistent coding style enables you to find errors both before
compilation and after the compiler has found an error.
architecture. Familiarity with the entity will hopefully aid in your learning
of the techniques to describe the architecture.
3.1 Entity
The VHDL entity construct provides a method to abstract the functionality
of a circuit description to a higher level. It provides a simple wrapper for the
lower-level circuitry. This wrapper effectively describes how the black box
interfaces with the outside world. Since VHDL describes digital circuits, the
entity simply lists the various inputs and outputs of the underlying circuitry.
In VHDL terms, the black box is described by an entity declaration. The
syntax of the entity declaration is shown in Listing 3.1.
1 entity my_entity is
2 port(
3 port_name_1 : in std_logic;
4 port_name_2 : out std_logic;
5 port_name_3 : inout std_logic); --don't forget the semicolon
6 end my_entity; -- do not forget this semicolon either
my entity defines the name of the entity. The next section is nothing
more than the list of signals from the underlying circuit that are available
to the outside world, which is why it is often referred to as an interface
specification. The port name x is an identifier used to differentiate the
various signals. The next keyword (the keyword in) specifies the direction
of the signal relative to the entity where signals can either enter, exit or do
both. These input and output signals are associated with the keywords in,
out and inout1 respectively. The next keyword (the keyword std logic)
refers to the type of data that the port will handle. There are several data
types available in VHDL but we will primarily deal with the std logic
type and derived versions. More information regarding the various VHDL
data types will be discussed later.
1
The inout data mode will be discussed later on in the book.
3.1 Entity 19
When you attempt to write fairly complicated VHDL code, you will need
to split your code into different files, functions and packages constructors
which will help you better deal with your code. In this scenario, the entity
body will not only host the port definition statements but, most likely,
other procedures as well. We will talk about this later in the book.
1 ------------------------------
2 -- interface description --
3 -- of killer_ckt --
4 ------------------------------
5 entity killer_ckt is life in1 kill a
6 port ( life in2 killer ckt kill b
7 life_in1 : in std_logic; ctrl a kill c
8 life_in2 : in std_logic; ctrl b
9 ctrl_a, ctrl_b : in std_logic;
10 kill_a : out std_logic;
11 kill_b, kill_c : out std_logic);
12 end killer_ckt;
Listing 3.2 shows an example of a black box and the VHDL code used
to describe it. Below are a few points about the code. Most of them deal
with the readability and understandability of the VHDL code.
Each port name is unique and has an associated mode (in/out) and
data type (std logic). This is a requirement.
Hopefully, you are not finding these entity specifications too challenging.
In fact, they are so straightforward, we will throw in one last twist before
we leave the realm of VHDL entities. Most of the more meaningful circuits
that you will be designing, analyzing and testing have many similar and
closely related inputs and outputs. These are commonly referred to as “bus
signals” in computer lingo. Bus lines are made of more than one signal
that differ in name by only a numeric character. In other words, each
separate signal in the bus name contains the bus name plus a number to
separate it from other signals in the bus. Individual bus signals are referred
to as elements of the bus. As you would imagine, buses are often used
in digital circuits. Unfortunately, the word bus also refers to established
data transfer protocols. To disambiguate the word bus, we will be using
the word “bundle” to refer to a set of similar signals and bus to refer to a
protocol.
Bundles are easily described in the VHDL entity. All that is needed is a
new data type and a special notation to indicate when a signal is a bundle
or not. A few examples are shown in Listing 3.3. In these examples note that
the mode remains the same but the type has changed. The std logic
data type has now been replaced by the word std logic vector to
indicate that each signal name contains more than one signal. There are
ways to reference individual members of each bundle, but we will get to
those details later.
As you can see by examining Listing 3.3, there are two possible methods
to describe the signals in a bundle. These two methods are shown in the
argument lists that follow the data type declaration. The signals in the
bundle can be listed in one of two orders which are specified by the to and
downto keywords. If you want the most significant bit of your bundle to
be the first bit on the left you use the downto keyword. Be sure not to
forget the orientation of signals when you are using this notation in your
VHDL model.
In the black box of Listing 3.3 you can see the formal notation for a bundle.
Note that the black box uses a slash-and-number notation. The slash across
the signal line indicates the signal is a bundle and the associated number
specifies the number of signals in the bundle. Worthy of mention regarding
3.1 Entity 21
the black box relative to Listing 3.3 is that the input lines sel1 and sel0
could have been made into one bundle containing the two signals.
8
a data /
8
b data /
8
c data /
8 8
d data / mux 4 / data out
sel0
sel1
1 -------------------------------------------------------------
2 -- Unlike the other examples, this is actually an interface
3 -- for a MUX that selects one of four bus lines for the output.
4 -------------------------------------------------------------
5 entity mux4 is
6 port ( a_data : in std_logic_vector(0 to 7);
7 b_data : in std_logic_vector(0 to 7);
8 c_data : in std_logic_vector(0 to 7);
9 d_data : in std_logic_vector(0 to 7);
10 sel1,sel0 : in std_logic;
11 data_out : out std_logic_vector(7 downto 0));
12 end mux4;
The data type std logic and the data type std logic vector is
what the IEEE has standardized for the representation of digital signals.
Normally, you should consider that these data types assume the logic value 1
or the logic value 0. However, as specified in the std logic 1164 package,
the implementation of the std logic type (and the std logic vector
type) is a little more generous and includes 9 different values, specifically:
0,1,U,X,Z,W,L,H,-.
The data type std logic becomes available to you soon after the de-
claration library IEEE; use IEEE.std logic 1164.all; at the
beginning of your code.
22 Chapter 3: VHDL Design Units
The reason for all these values is the desire for modeling three-state
drivers, pull-up and pull-down outputs, high impedance state and a few
others types of inputs/outputs. For more details refer to the IEEE 1164
Standard2 .
Alternatively to the std logic data type, VHDL programmers some-
times use the much simpler data type bit which has only the logic values
1 and 0.
Once these packages have been included, you will have access to a very
large set of goodies: several data types, overloaded operators, various
conversion functions, math functions and so on. For instance, the inclusion
of the package numeric std.all will give you the possibility of using the
unsigned data type and the function to unsigned shown in Listing 3.4.
For a detailed description of what these libraries include, refer to the
Language Templates of your favorite synthesis software tool.
3.3 Architecture
The VHDL entity declaration, introduced before, describes the interface
or the external representation of the circuit. The architecture describes
what the circuit actually does. In other words, the VHDL architecture
describes the internal implementation of the associated entity. As you can
probably imagine, describing the external interface to a circuit is generally
much easier than describing how the circuit is intended to operate. This
2
IEEE 1164 Standard: http://en.wikipedia.org/wiki/IEEE_1164
3.3 Architecture 23
1 -- library declaration
2 library IEEE;
3 use IEEE.std_logic_1164.all; -- basic IEEE library
4 use IEEE.numeric_std.all; -- IEEE library for the unsigned
5 -- type and various arithmetic operators
6
13 -- entity
14 entity my_ent is
15 port ( A, B, C : in std_logic;
16 F : out std_logic);
17 end my_ent;
18 -- architecture
19 architecture my_arch of my_ent is
20 signal v1, v2 : std_logic_vector (3 downto 0);
21 signal u1 : unsigned (3 downto 0);
22 signal i1 : integer;
23 begin
24 u1 <= "1101";
25 i1 <= 13; -- decimal equivalent to "1101"
26 v1 <= std_logic_vector(u1);
27 v2 <= std_logic_vector(to_unsigned(i1, v2'length));
28
statement becomes even more important as the circuits you are describing
become more complex.
There can be any number of equivalent architectures describing a single
24 Chapter 3: VHDL Design Units
entity. As you will eventually discover, the VHDL coding style used inside
the architecture body has a significant effect on the way the circuit is
synthesized (how the circuit will be implemented inside an actual hardware).
This gives the VHDL programmer the flexibility of designing systems with
specific features such as particular physical size (measuring the number of
needed basic digital elements) or operational speed.
For various reasons, such as facilitating code re-usability and connectibil-
ity, an architecture can be modeled in different ways. Understanding the
various modeling techniques and understanding how to use them represent
the first important steps in learning VHDL.
An architecture can be written by means of three modeling techniques
plus any combination of these three. There is the data-flow model, the
behavioral model, the structural model and the hybrid models.
These models will be described throughout the book. Listing 3.5 gives a
sneak preview of what a simple but complete VHDL code block looks like.
6 -- the ENTITY
7 entity circuit1 is
8 port (A, B, C : in std_logic;
9 F, G : out std_logic);
10 end circuit1;
11
12 -- the ARCHITECTURE
13 architecture circuit1_arc of circuit1 is
14 signal sig_1 : std_logic; -- signal definition
15 begin
16 process (a,b,c)
17 variable var_1 : integer; -- variable definition
18 begin
19 F <= not (A and B and C); -- signal assignment
20 sig_1 <= A; -- another signal assignment
21 var_1 := 34; -- variable assignment
22 end process;
23
consistent with the jargon used in the standards and related literature,
hence the use of the words assignment and execution. Despite this, keep in
mind that signals work as “named wires”, and the code will define how
are they connected or have their state defined.
It is important to understand the difference between variables and signals,
specifically when their value changes. A variable changes its value soon
after the variable assignment is executed. Instead, a signal changes its
value “some time” after the signal assignment expression is evaluated.
This has important consequences for the updated values of variables and
signals. This means that you should never assume that a signal assignment
26 Chapter 3: VHDL Design Units
can happen instantly and it also means that you can take advantage of
variables every time you need to implement a counter or to store values
when inside a process.
In order to be able to introduce the use of a variable we had to employ
the process construct, a construct that you are not yet familiar with. We
will see more in details later on in the book that any time we need a
non-concurrent execution environment where code lines are executed one
after the other (like in C or Java), we will be using the process construct.
Inside a process, all instructions are executed consecutively from top to
bottom. However the process itself will be executed concurrently with the
rest of the code (e.g. the instruction at line 24).
Always remember that the assignment of Listing 3.5 at line 24 and
the execution of the process, are not executed consecutively but instead
concurrently (all at the same time). Any hope that the execution of line
24 will happen before or after the execution of the process will only result
in great disappointment.
As a final note, let us to remind that the type std logic only exists if
you declare the library ieee.std logic 1164.all as done in line 4 of
Listing 3.5.
3.5 Summary
The entity declaration describes the inputs and outputs of your circuit.
This set of signals is often referred to as the interface to your circuit
since these signals are what the circuitry, external to the entity, uses
to interact with your circuit.
The word bundle is preferred over the word bus when dealing with
multiple signals that share a similar purpose. The word bus has other
connotations that are not consistent with the bundle definition.
The architecture describes what your circuit actually does and what
its behavior is. Several possible implementations (models) of the same
behavior are possible in VHDL. These are the data-flow model, the
behavioral model, the structural model as well as any combination
of them, generally called hybrid model.
3.6 Exercises
1. What is referred to by the word bundle?
a in1 input w
8 8
b in2 out b / dat 4
sys1 a data /
clk 8 sys2
3
ctrl int b data / / dat 5
clk
b)
entity ckt_e is
port (
RAM_CS, RAM_WE, RAM_OE : in std_logic;
SEL_OP1, SEL_OP2 : in std_logic_vector(3 downto 0);
RAM_DATA_IN : in std_logic_vector(7 downto 0);
RAM_ADDR_IN : in std_logic_vector(9 downto 0);
RAM_DATA_OUT : out std_logic_vector(7 downto 0));
end ckt_e;
6. The following two entity declarations contain two of the most common
syntax errors made in VHDL. What are they?
a)
entity ckt_a is
port (
J,K : in std_logic;
CLK : in std_logic
Q : out std_logic;)
end ckt_a;
b)
entity ckt_b is
port (
mr_fluffy : in std_logic_vector(15 downto 0);
mux_ctrl : in std_logic_vector(3 downto 0);
byte_out : out std_logic_vector(3 downto 0);
end ckt_b;
VHDL Concurrent Programming Paradigm
4
The previous chapter introduced the idea of the basic design units of VHDL:
the entity and the architecture. Most of the time was spent describing
the entity simply because there is so much less involved compared to the
architecture. Remember, the entity declaration is used to describe the
interface of a circuit to the outside world. The architecture is used to
describe how the circuit is intended to function.
Before we get into the details of architecture specification, we must step
back for a moment and remember what it is we are trying to do with VHDL.
We are, for one reason or another, describing a digital circuit. Realizing
this is very important. The tendency for young VHDL programmers with
computer programming backgrounds is to view VHDL as just another pro-
gramming language they want or have to learn. Although many university
students have used this approach to pass the basic digital classes, this is
not a good idea.
When viewed correctly, VHDL represents a completely different approach
to programming while still having many similarities to other programming
languages. The main similarity is that they both use a syntactical and rule-
based language to describe something abstract. But, the difference is that
they are describing two different things. Most programming languages are
used to implement functionalities in a sequential manner, one instruction
at a time. VHDL however describes hardware and so instructions may be
“executed” in a concurrent or in a sequential manner, meaning that several
30 Chapter 4: VHDL Concurrent Programming Paradigm
instructions may be “executed” at once. Realizing this fact will help you
to truly understand the VHDL programming paradigm and language.
my circuit
A1 A
A2
B1 B E E out
B2
D1 D C
Figure 4.1: Some common circuit that is well known to execute parallel oper-
ations.
Listing 4.1 shows the code that implements the circuit shown in Figure 4.1.
This code shows four concurrent signal assignment statements. As seen
before, the “<=” construct refers to the signal assignment operator. It is
true that we cannot write these four statements at the same time but we can
interpret these statements as actions that occur concurrently. Remember
to keep in mind that the concept of concurrency is a key concept in VHDL.
32 Chapter 4: VHDL Concurrent Programming Paradigm
If you feel that the algorithmic style of thought is creeping into your soul,
try to snap out of it quickly. If a different thought process is needed,
think that these concurrent assignments are instructing the synthesis tool
on how to connect components using “named wires” (see line 10). Once
connected, the components work inherently in parallel. The concurrent
signal assignment is discussed in greater detail in the next section.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_circuit is
5 port ( A_1,A_2,B_1,B_2,D_1 : in std_logic;
6 E_out : out std_logic);
7 end my_circuit;
8
Listing 4.2: Equivalent VHDL code for the circuit of Figure 4.1
Listing 4.3: Equivalent VHDL code for the circuit of Figure 4.1
Listing 4.4: Equivalent VHDL code for the circuit of Figure 4.1
code of Listing 4.1 and its relationship to its schematic shown in Figure 4.1.
The statement “G <= A AND B;” indicates that the value of the signal
named G represents an AND logic operation between the signals A and B.
There are four types of concurrent statements that are examined in
this chapter. We have already briefly discussed the concurrent signal
assignment statement which we will soon examine further and put it
into the context of an actual circuit. The three other types of concurrent
statements that are of immediate interest to us are: the process state-
ment, the conditional signal assignment and the selected signal
assignment.
In essence, the four types of statements represent the tools that you will
use to implement digital circuits in VHDL. You will soon be discovering
the versatility of these statements. Unfortunately, this versatility effectively
adds a fair amount of steepness to the learning curve. As you know from
your experience in other programming languages, there are always multiple
ways to do the same things. Stated differently, several seemingly different
pieces of code can actually produce the same result. The same is true
for VHDL code: several considerably different pieces of VHDL code can
actually generate the exact same hardware. Keep this in mind when you
look at any of the examples given in this tutorial. Any VHDL code used to
solve a problem is more than likely one of many possible solutions to that
problem. Some of the VHDL models in this tutorial are presented to show
that something can be done a certain way, but that does not necessarily
mean they can only be done in that way.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_nand3 is
5 port ( A,B,C : in std_logic;
A
6 F : out std_logic); B my nand3 F
7 end my_nand3; C
8
This example highlights the use of several logic operators. The logic
operators available in VHDL are AND, OR, NAND, NOR, XOR and
XNOR. The NOT operator is technically not a logic operator but is
also available. Moreover, these logic operators are considered to be
binary operators in that they operate on the two values appearing on
the left and right-hand side of the operator. The NOT operator is a
unary operator and for that, it only operates on the value appearing to
the right of the operator.
In this solution, the entity only has one associated architecture. This
is fairly common practice in most VHDL design. Conversely, different
designs with the same interface could be implemented with focus on
different optimizations: chip resource usage, throughput, latency etc.
Example 1 demonstrates the use of the concurrent signal assignment (CSA)
statement in a working VHDL program (refer to line 11 of Listing 4.6).
But since there is only one CSA statement, the concept of concurrency is
not readily apparent. The idea behind any concurrent statement in VHDL
is that the output is changed any time one of the input signals changes.
In other words, the output F is re-evaluated any time a signal on the
input expression changes. This is a key concept in truly understanding the
VHDL, so you may want to read the previous sentence a few more times.
The idea of concurrency is more clearly demonstrated in Example 2.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_ckt_f3 is
5 port ( L,M,N : in std_logic;
6 F3 : out std_logic);
7 end my_ckt_f3;
8
The intermediate signals must be declared within the body of the archi-
tecture because they have no link to the outside world and thus do not
38 Chapter 4: VHDL Concurrent Programming Paradigm
appear in the entity declaration. Note that the intermediate signals are
declared in the architecture body but appear before the begin statement.
Despite the fact that the architectures f3 2 and f3 1 of Listing 4.7 and
Listing 4.8 appear different, they are functionally equivalent. This is because
all the statements are concurrent signal assignment statements. Even
though the f3 1 architecture contains three CSAs, they are functionally
equivalent to the CSA in f3 2 because each of the three statements is
effectively executed concurrently.
Although the approach of using intermediate signals is not mandatory
for this example, their use brings up some good points. First, the use
of intermediate signals is the norm for most VHDL models. The use of
intermediate signals was optional in Listing 4.8 due to the fact that the
example was modeling a relatively simple circuit. As circuits become more
complex, there are many occasions in which intermediate signals must be
used. Secondly, intermediate signals are something of a tool that you will
often need to use in your VHDL models. The idea here is that you are
trying to describe a digital circuit using a textual description language: you
will often need to use intermediate signals in order to accomplish your goal
of modeling the circuit. The use of intermediate signals allows you to more
easily model digital circuits but does not make the generated hardware
more complicated. The tendency in using VHDL is to think that since
there is more text written on your page, the circuit you are describing
and/or the resulting hardware is larger or more complex. This is simply
not true. The main theme of VHDL is that you should use the VHDL
tools at your disposal in order to model your circuits in the simplest way
possible. Simple circuits have a higher probability of being understood and
synthesized efficiently. But most importantly, a simple VHDL model is not
related to the length of the actual VHDL code.
In Example 2, the conversion of the logic function to CSAs was relatively
straightforward. The ease with which these functions can be implemented
into VHDL code was almost trivial (the function was not complicated at
all). As functions become more complex (more inputs and outputs), an
equation entry approach becomes tedious and error prone. Luckily, there
are other types of concurrent construct that can ease its implementation.
4.4 Conditional Signal Assignment when 39
Listing 4.9: The syntax for the conditional signal assignment statement
If you look carefully at this code you will notice that there is in fact
one target and a bunch of expressions and conditions. The associated
expressions are the single digits surrounded by single quotes; the as-
sociated conditions follow the when keyword. In other words, there is
only one signal assignment operator used for each conditional signal
assignment statement.
The solution uses relational operators. There are actually six different
relational operators available in VHDL. Two of the more common
relational operators are the “=” and “/=” relational operators which
are the “is equal to” and the “is not equal to” operators, respectively.
Operators are discussed at greater length in further sections.
Note that this statement describes a fully combinational circuit with one
output and 3 inputs, which can be fully specified by a table with 8 lines
(one for each possible input combination). This means that this conditional
statement, due to the specified logic, allowed a very concise (but complete)
description of the table, with only 3 lines of code.
4.4 Conditional Signal Assignment when 41
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_4t1_mux is
5 port(D3, D2, D1, D0 : in std_logic;
6 SEL : in std_logic_vector(1 downto 0); my 4to1 mux
7 MX_OUT : out std_logic); D3
8 end my_4t1_mux; D2
9
MX OUT
D1
10 architecture mux4t1 of my_4t1_mux is D0
begin 2
11 SEL /
12 MX_OUT <= D3 when (SEL = "11") else
13 D2 when (SEL = "10") else
14 D1 when (SEL = "01") else
15 D0 when (SEL = "00") else
16 '0';
17 end mux4t1;
Here are some important notes about the VHDL code in Listing 4.11:
The solution looks somewhat efficient compared to the amount of
logic that would have been required if CSA statements were used. The
VHDL code looks good and is pleasing to the eye, qualities required
for readability.
42 Chapter 4: VHDL Concurrent Programming Paradigm
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_4t1_mux is
5 port (D3, D2, D1, D0 : in std_logic;
6 SEL : in std_logic_vector(1 downto 0);
7 MX_OUT : out std_logic);
8 end my_4t1_mux;
9
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_4t1_mux is
5 port (D3, D2, D1, D0 : in std_logic;
6 SEL : in std_logic_vector(1 downto 0);
7 MX_OUT : out std_logic);
8 end my_4t1_mux;
9
One thing to notice about the solution shown in Listing 4.14 is the
use of the when others clause as the final entry in the selected signal
assignment statement. In reality, the last clause D0 when "00" could be
removed from the solution without changing the meaning of the statement
if the last line would be changed to D0 when others;. Despite this, it is
considered good VHDL programming practice to include all the expected
cases in the selected signal assignment statement, without relying on the
when others clause.
Once again, there are a few things of interest in the solution for Example 5
which are listed below.
The VHDL code has several similarities to the solutions of Example 4,
but this solution is even more pleasing to the eye than the ones where
the MUX was modeled referring to the SEL inputs repeatedly.
The circuit used in this example was a 4:1 MUX. In this case, each of
the conditions of the chooser expression is accounted for in the body
of the selected signal assignment statement. However, this is not a
requirement. The only requirement here is that the line containing the
when others keywords appears in the final line of the statement.
range of D IN SZ OUT
0000 → 0011 100
0100 → 1001 010
1010 → 1111 001
unknown value 000
my sz ckt
4 3
D IN / / SZ OUT
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_sz_ckt is
5 port ( D_IN : in std_logic_vector(3 downto 0);
6 SZ_OUT : out std_logic_vector(2 downto 0));
7 end my_sz_ckt;
8
The only comment for the solution of Example 6 is that the vertical bar
is used as a selection character in the choices section of the selected signal
assignment statement. This increases the readability of the code as do the
similar constructs in algorithmic programming languages.
4.5 Selected Signal Assignment with select 47
L M N F3
0 0 0 0
0 0 1 1
X 0 1 0 0
F 3(L, M, N ) = (1, 6, 7) 0 1 1 0
1 0 0 0
1 0 1 0
1 1 0 1
1 1 1 1
48 Chapter 4: VHDL Concurrent Programming Paradigm
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_ckt_f3 is
5 port ( L, M, N : in std_logic;
6 F3 : out std_logic);
7 end my_ckt_f3;
8
4.6 Testbenches
Hardware description languages had their origin related to the need to test
and integrate modules, as important parts of the development process. For
this reason, it is expected that the languages included features to specify
test routines for the hardware being described. Starting here with the VHDL
features already presented, we will provide at the end of each chapter an
example of using the same language to describe test procedures which
are named testbenches.
Remember, there are a thousand ways to learn things. This is especially
true when learning programming languages, where there are usually many
different and varied solutions to the same problem. This is highlighted
by the many different approaches that appear in VHDL books and by
the many tutorials. This book approaches the learning process in a very
informal way, and many concepts are introduced briefly so they don’t get
in the way of the main topic being presented. This is exactly the case here,
where we need two topics which will be covered in detail in later chapters.
4.6 Testbenches 49
To understand the logic of our first testbench, we need only the following
short descriptions for two important VHDL features:
1 -- library omitted
2 entity or_gate is
3 port(a, b: in std_logic;
4 q: out std_logic);
5 end or_gate;
6
Figure 4.2: Waveform for the OR gate test generated by the EPWave tool.
50 Chapter 4: VHDL Concurrent Programming Paradigm
1 -- library omitted
2 entity testbench is -- the testbench has no interfaces
3 end testbench;
4
5 architecture tb of testbench is
6 component or_gate is -- the same interface of the entity
7 port(a, b: in std_logic;
8 q: out std_logic);
9 end component;
10 -- these are the local signals conected to the DUT
11 signal a_in, b_in, q_out: std_logic;
12 begin
13
A VHDL simulation tool analyzes the two codes (DUT and testbench)
and evaluates the outputs provided by the design for each input stimulus.
Additionally to the comments in the code, note that the keyword wait,
evaluated sequentially, provides a simulated time for the DUT to react to
the new signals at the inputs and update the correspondent output. The
test signals defined at line 11 are “connected” to the DUT at line 14.
As the designs become more complex the testbenches will be adapted to
cover more test procedures. The following chapters will gradually introduce
other features to this very useful development resource.
4.7 Summary
The entity/architecture pair is the interface description and behavior
description of how a digital circuit operates.
The architecture body can contain any or all of the mentioned concur-
rent statements.
4.8 Exercises
1. For the following function descriptions, write VHDL models that
implement these functions using concurrent signal assignment.
a) F (A, B) = AB + A + AB
b) F (A, B, C, D) = ACD + BC + BCD
c) F (A, B, C, D) = (A + B) · (B + C + D) · (A + D)
Y
d) F (A, B, C, D) = (3, 2)
Y
e) F (A, B, C) = (5, 1, 4, 3)
X
f) F (A, B, C, D) = (1, 2)
A later example with multiple lines will explain the sequential nature
of the process declaration. But, since this behavioral example has only
a single line with an assignment, let’s focus on the important difference
here: the sensitivity list (A, B). Despite being a feature relevant only for
pre-2008 VHDL and which affects only simulation (and not the hardware
synthesis), this book will use it for two main reasons: it helps understanding
how the process block is triggered and it is still quite possible that you
will encounter VHDL code written with older standards (for older tools).
For the behavioral architecture description, when simulating the design,
every time there is a change in signals in the process sensitivity list, all of
the sequential statements in the process are re-evaluated. Evaluation of
the process statement is controlled by the signals that are placed in the
process sensitivity list. The exact same result described for the data-flow
will be obtained in synthesis only: the tool evaluates the signals listed
on the right-hand side.
If the inconsistent way the sensitivity list is used in simulation and
synthesis looks strange, don’t feel bad. This has historical roots on why
HDLs were created decades ago and has been changed since to a better
and less error prone alternative. For this reason, when writing your own
code from scratch just use the “new” feature of automatic sensitivity list
process(all) and be relieved that no simulation/synthesis mismatch
will happen in your design due to errors in the sensitivity list.
be visible within the process body itself. Furthermore, notice that the
statement at line 23 is placed inside the architecture body but outside
the process body; therefore its execution happens concurrently with the
process statement.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_system is
5 port ( A, B, C : in std_logic;
6 F, Q : out std_logic);
7 end my_system;
8
5.4.2 if Statement
The if statement is used to create a branch in the execution flow of the
sequential statements. Depending on the conditions listed in the body of
the if statement, either the instructions associated with one or none of
the branches is executed when the if statement is processed. The general
form of the if statement is shown in Listing 5.4.
1 if (condition) then
2 <statements>
3 elsif (condition) then
4 <statements>
5 else
6 <statements>
7 end if;
The final else clause is optional. Not including the final else clause
presents the possibility that none of the sequence of statements associ-
ated with the if statement will be evaluated. This has deep ramifica-
tions that we will discuss later.
Let us see now some examples that will help us to better understand how
to use the if statement.
This is probably not the best way to implement a logic function but it
does show an if statement in action. An alternate architecture for the
solution of Example 8 is shown in Listing 5.6.
62 Chapter 5: Standard Models in VHDL Architectures
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity my_ex is
5 port (A, B, C : in std_logic;
6 F_OUT : out std_logic);
7 end my_ex;
8
The solution to Example 9 shown in Listing 5.7 uses some new syntax.
The entity uses bundle signals, but the associated architecture needs to
access individual elements of these bundles. The solution is to use the
bus index operator to access internal signals of the bus. This is done via
the use of a number representing an index placed inside parentheses (for
example Data in(7)). Bus index operators are used extensively in VHDL
and were previously mentioned. The solution to Example 9 shows a more
typical use of the operator than was previously mentioned.
One other thing to notice about the solution in Example 9 is that every
possible combination of the select variable is accounted for in the code.
It would be possible to remove the final elsif statement in the code
shown in Listing 5.7 and place the associated signal assignment in the
else clause. But this is not considered good VHDL practice and should
be avoided at all costs. The justification for this is that it will modify the
readability of the code but not alter the hardware generated by the code.
64 Chapter 5: Standard Models in VHDL Architectures
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity mux_8t1 is
5 port ( Data_in : in std_logic_vector (7 downto 0);
6 SEL : in std_logic_vector (2 downto 0);
7 F_CTRL : out std_logic);
8 end mux_8t1;
9
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity mux_8to1_ce is
5 port ( Data_in : in std_logic_vector (7 downto 0);
6 SEL : in std_logic_vector (2 downto 0);
7 CE : in std_logic;
8 F_CTRL : out std_logic);
9 end mux_8to1_ce;
10
1 case (expression) is
2 when choices =>
3 <sequential statements>
4 when choices =>
5 <sequential statements>
6 when others =>
7 <sequential statements>
8 end case;
Once again, the concept of the case statement should be familiar to you
in several regards. Firstly, it can generally be viewed as a compact form of
the if statement. It is not as functional, however, for the reason described
above. Secondly, the case statement is similar in both form and function
to the case statement or the switch statement in other algorithmic
programming languages. And finally, the VHDL case statement is the
sequential equivalent of the VHDL selected signal assignment statement.
These two statements essentially have the same capabilities but the case
statement is a sequential statement found in a process body while the
selected signal assignment statement is one form of concurrent signal
assignment. The when others line is not required but should be used as
good programming practice.
5.4 Sequential Statements 67
EXAMPLE 11. Write some VHDL code that implements the following
function using the case statement: F OU T (A, B, C) = AB C + BC
SOLUTION. This solution falls into the category of not being the best
way to implement a circuit using VHDL. It does, however, illustrate another
useful feature in the VHDL. The first part of this solution requires that
we list the function as a sum of minterms. This is done by multiplying the
non-minterm product term given in the example by 1. In this case, 1 is
equivalent to (A + A). This factoring operation is shown as:
F OU T (A, B, C) = AB C + BC
F OU T (A, B, C) = AB C + BC(A + A)
F OU T (A, B, C) = AB C + ABC + ABC
One very important point in the solution to Example 12 is the fact that a
case statement was embedded into an if statement. The technical term
for this style of coding is, as you would guess, nesting. Nesting sequential
statements is typical in behavioral models and is used often. This is actually
one of the features that make behavioral modeling so much more powerful
than data-flow modeling. The reality is that conditional and selective signal
assignment statements cannot be nested.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity mux_8to1_ce is
5 port ( Data_in : in std_logic_vector (7 downto 0);
6 SEL : in std_logic_vector (2 downto 0);
7 CE : in std_logic;
8 F_CTRL : out std_logic);
9 end mux_8to1_ce;
10
It is not uncommon to see many, not so good, pieces of VHDL code that
attempt to use a single process statement in order to implement a relatively
complex circuit. Although the code appears like it should work in terms
of the provided statements, this is an illusion based on the fact that your
mind is interpreting the statements in terms of a higher-level language.
5.6 Automatic Testbenches 71
The reality is that VHDL is somewhat mysterious in that you are trusting
the VHDL synthesizer to magically know what you are trying to describe.
If you do not understand the ins and outs of VHDL at a low level, your
circuit is not going to synthesize properly. Most likely you understand
simple VHDL behavioral models. But once the models become complex,
your understanding quickly fades away. The solution to this problem is
really simple: keep your VHDL models simple, particularly your process
statements.
In VHDL, the best approach is to keep your process statements centered
around a single function and have several process statements that com-
municate with each other. The bad approach is to have one big process
statement that does everything for you. The magic of VHDL is that if
you provide simple code to the synthesizer, it is more than likely going to
provide you with a circuit that works and with an implementation that is
simple and eloquent. If you provide the synthesizer with complicated VHDL
code, the final circuit may work and may even be efficient in both speed
and real estate, but probably not. As opposed to higher-level languages
where small amounts of code often translate directly to code of relatively
high efficiency, efficiency in VHDL code is obtained by compact and simple
partitioning of the VHDL code based on the underlying hardware con-
structs. In other words, simple VHDL models are better but the simplicity
is generally obtained by proper partitioning and description of the model.
So try to fight off the urge to impress your friends with the world’s shortest
VHDL model; your hardware friends will know better.
5.7 Summary
Let us now review some of the important concepts that have been intro-
duced in this chapter.
The three main flavors of VHDL modeling styles include data-flow,
behavioral and structural models.
The case statement has a direct analogy to the selected signal assign-
ment statement used in data-flow modeling.
Both the case statement and the if statement can be nested. Concur-
rent, conditional and selected signal assignment statements
cannot be nested.
X
e) F (A, B, C, D) = (1, 2)
2. For the circuit below, write the VHDL behavioral model that im-
plements it using both case statements and if statements (two
separate models).
A1 A
A2
B1 B E E out
B2
D1 D C
Operator type
logical and or nand nor xor xnor
relational = /= < <= > >=
shift sll srl sla sra rol ror
adding + − &
sign + −
multiplying * / mod rem
miscellaneous ** abs not
Reliance on obscure precedence rules tends to make the VHDL code cryptic
and hard to understand. A liberal use of parentheses is a better approach
to VHDL coding.
The first column of Table 6.1 lists the operators from lowest to highest
precedence, where logical operators have the lowest precedence. Although
there is a precedence order for the types of operators, there is no precedence
order within each type of operator. In other words, the operators appearing
in the rows are presented in no particular order. This means that the
operators are applied to the given operands in the order they appear in
the associated VHDL code.
Both logical shifts introduce zeros into one end of the operand that
is affected by the shift operation. In other words, zeros are fed into
one end of the operand while bits are essentially lost from the other
end. The difference between logical and arithmetic shifts is that in
arithmetic shift, the sign-bit is never changed and the bit that is fed
into one end can differ. Hence, for arithmetic shift lefts, the last right
bit is stuffed to the right end of the operand. For arithmetic shift rights,
the sign-bit (the left-most bit) is propagated right (the value of the
left-most bit is fed into the left end of the operand).
Rotate operators grab a bit from one end of the word and stuff it into
the other end. This operation is done independently of the value of the
individual bits in the operand.
78 Chapter 6: VHDL Operators
Table 6.4: All the other VHDL operators not listed so far.
Table 6.5: Definitions of rem and mod operators. (abs = absolute value)
rem mod
8 rem 5 = 3 8 mod 5 = 3
-8 rem 5 = -3 -8 mod 5 = 2
8 rem -5 = 3 8 mod -5 = -2
-8 rem -5 = -3 -8 mod -5 = -3
1 -- library omitted
2 entity add3 is
3 port(a, b : in std_ulogic_vector(2 downto 0);
4 s : out std_ulogic_vector(3 downto 0));
5 end add3;
6
1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.numeric_std.all; -- for the 'to_unsigned' operation
4
5 architecture tb of tb_add is
6 component add3 is
7 port(a, b : in std_ulogic_vector(2 downto 0);
8 s : out std_ulogic_vector(3 downto 0));
9 end component;
10 signal a, b : std_logic_vector(2 downto 0);
11 signal s : std_logic_vector(3 downto 0);
12 begin
13 DUT: add3 port map (a=>a, b=>b, s=>s);
14
The process has the label dff. This is not required by the VHDL
language but the addition of process labels promotes a self-describing
nature of the code and increases its readability and understandability.
7.1 Simple Storage Elements Using VHDL 85
5 entity d_ff is
6 port ( D, CLK : in std_logic;
7 Q : out std_logic);
8 end d_ff;
9
The D flip-flop is best known and loved for its ability to store (save,
remember) a single bit. The way that the VHDL code in Listing 7.1 is
able to store a bit is not however obvious. The bit-storage capability in
VHDL is implied by the way the VHDL code is interpreted. The implied
storage capability comes about as a result of not providing a condition
that indicates what should happen if the listed if condition is not met. In
other words, if the if condition is not met, the device does not change the
value of Q and therefore it must remember the current value. The memory
feature of the current value, or state, constitutes the famous bit storage
quality of a flip-flop. If you have not specified what the output should
be for every possible set of input conditions, the option taken by VHDL
is to not change the current output. By definition, if the input changes
to an unspecified state, the output remains unchanged. In this case, the
output associated with the previous set of input can be thought of as being
86 Chapter 7: Using VHDL for Sequential Circuits
1 --------------------------------------------------------------
2 -- D Flip-flop model with active-low synchronous set input. --
3 --------------------------------------------------------------
4 library IEEE;
5 use IEEE.std_logic_1164.all;
6
7 entity d_ff_ns is
8 port ( D, S, CLK : in std_logic;
9 Q : out std_logic);
10 end d_ff_ns;
11
On the rising edge of the clock, the S input takes precedence over the
D input because the state of the S input is checked prior to examining
the state of the D input. In an if-else statement, once one condition
evaluates as true, none of the other conditions is checked. In other
words, the D input is transferred to the output only on the rising edge
of the clock and only if the S input is not asserted.
88 Chapter 7: Using VHDL for Sequential Circuits
5 entity d_ff_r is
6 port (D, R, CLK : in std_logic;
7 Q : out std_logic);
8 end d_ff_r;
9
5 entity t_ff_s is
6 port ( T, S, CLK : in std_logic;
7 Q : out std_logic);
8 end t_ff_s;
9
The process manipulates the intermediate signal and its value is assigned
to the output Q in a concurrent statement. Note that the intermediate
7.1 Simple Storage Elements Using VHDL 91
It should be noted that a proper test for such hardware description must
activate different inputs at specific times, when compared to the clock
edges, allowing the verification of the desired synchronous and asynchronous
behavior. In summary: a synchronous input should not affect the outputs
immediately when it changes, but only at the correct clock edge. Conversely,
an asynchronous input must perform its function regardless of the moment
it changes.
In Listing 7.7 a testbench which generates the described test sequence is
presented. Note that the clock signal is concisely created in a single line of
code (16). Using the simulation keyword after, the CLK signal is inverted
at each half of the desired clock period (defined using a constant). These
inversions proceed as long as the CLK ENABLE signal remains high.
This scheme allows the clock to start its first period at the low level
(defined at line 11), so the changes within the stimulus process, as long as
separated by multiples of the clock period, always happen at the falling clock
edge. As mentioned before, by simply adding different input combinations
to this process with the proper delay, both synchronous and asynchronous
behavior can be verified.
After all the input combinations are specified, the stimulus process can
7.4 Testbenches with clock 95
simply be terminated by setting the CLK ENABLE signal to low. This forces
the clock signal to stop and no further events will be registered by the
simulator. It is clear how easy it is to create test signals independently,
while controlling their changes reliably in the simulated time.
18 DUT : d_ff port map(CLK => CLK, RST => RST, D => D, Q => Q);
19
20 stimulus: process
21 begin
22 D <= '0';
23 RST <= '1';
24 wait for PERIOD;
25 RST <= '0';
26 D <= '1';
27 wait for PERIOD;
28 D <= '0';
29 wait for PERIOD;
30 CLK_ENABLE <= '0';
31 wait;
32 end process stimulus;
S
EXERCISE 1. Provide a VHDL behavi-
D Q
oral model of the D flip-flop shown on the
right. The S and R inputs are an active low
CLK Q
asynchronous preset and clear. Assume both
R
the S and R inputs will never be asserted
simultaneously.
S
EXERCISE 2. Provide a VHDL behavi-
D Q
oral model of the D flip-flop shown on the
right. The S and R inputs are an active low
CLK Q
asynchronous preset and clear. Assume the S
R
input takes precedence over the R input in the
case where both are asserted simultaneously.
S
EXERCISE 3. Provide a VHDL behavioral D Q
S
EXERCISE 4. Provide a VHDL behavi-
D Q
oral model of the D flip-flop shown on the
right. The S and R inputs are an active low
CLK Q
asynchronous preset and clear. If both the
R
S and R inputs are asserted simultaneously,
the output of the flip-flop will toggle.
7.5 Exercises: Basic Memory Elements 97
S
EXERCISE 6. Provide a VHDL behavi-
T Q
oral model of the T flip-flop shown on the
right. The S and R inputs are an active low
CLK Q
asynchronous preset and clear. Assume both
R
the S and R inputs will never be asserted
simultaneously.
Finite State Machine Design Using VHDL
8
Finite state machines (FSMs) are mathematical abstractions that are used
to solve a large variety of problems, among which are electronic design
automation, communication protocol design, parsing and other engineering
applications. At this point in your digital design career, you have probably
designed several state machines on paper. You are now at the point where
you can implement and test them using actual hardware. The first step in
this process is to learn how to model FSMs using VHDL.
As you will see in this section, simple FSM designs are just a step
beyond the sequential circuit design described in the previous section.
The techniques presented here will allow you to quickly and easily design
relatively complex FSMs which can be useful in a number of ways.
A block diagram for a standard Moore-type FSM is shown in Fig. 8.1. This
diagram is fairly typical but different names are used for some of the blocks
in the design. The Next State Decoder is a block of combinatorial
logic that uses the current external inputs and the current state to decide
upon the next state of the FSM. In other words, the inputs to the Next
State Decoder block are decoded to produce an output that represents
the next state of the FSM. The circuitry in the Next State Decoder
is generally the excitation equations for the storage elements (flip-flops) in
the State Register block. The next state becomes the present state of
the FSM when the clock input to the state registers block becomes active.
The state registers block is a storage element that stores the present state
of the machine. The inputs to the Output Decoder are used to generate
the desired external outputs, which is accomplished via combinatorial logic.
Because the external outputs are only dependent upon the current state
of the machine, this FSM is classified as a Moore-type FSM.
The FSM model shown in Fig. 8.1 is the most common model for de-
scribing a Moore-type FSM. This is probably because students are often
asked to generate the combinatorial logic required to implement the Next
State Decoder and the Output Decoder; however here we want to
think about FSMs in the context of VHDL. The true power of VHDL
starts to emerge in dealing with FSMs. As you will see, the versatility of
VHDL behavioral modeling removes the need for large paper designs of
endless K-maps and endless combinatorial logic elements.
There are several different approaches used to model FSMs in VHDL.
The various approaches are a result of the general versatility of VHDL as
a language. What we will describe in this section is probably the clearest
approach for FSM implementation. A block diagram of the approach we
will use in the implementation of FSMs is shown in Fig. 8.2.
Although it does not look that much clearer, you will soon find the FSM
model shown in Fig. 8.2 to be a straightforward method for implementing
FSMs. The approach we will use divides the FSM into two VHDL processes.
One process, referred to as the Synchronous Process handles all the
matters regarding clocking and other controls associated with the storage
element. The other process, the Combinatorial Process, handles all
the matters associated with the Next State Decoder and the Output
Decoder of Fig. 8.1. Note that the two blocks in Fig. 8.1 are both made
solely of combinatorial logic.
Some new lingo is used to describe the signals in Fig. 8.2, as outlined
and described below:
The inputs labelled Parallel Inputs are used to signify inputs that
act in parallel on each of the storage elements. These inputs include
enables, presets, clears, etc.
TOG EN TOG EN
ST1
1
TOG EN
Even though this example is of the simplest FSM you could hope for,
the code looks somewhat complicated. But if you examine it closely,
you can see that everything is nicely compartmentalized in the solution.
There are two processes; the synchronous process handles the asyn-
chronous reset and the assignment of a new state upon the arrival of
the system clock. The combinatorial process handles the Moore output
(independent of inputs) and the state transition calculation.
You will see later that Mealy outputs, due their general nature, are
assigned inside the if statement.
Note that the VHDL code shown in Listing 8.2 differs in only two areas
from the code shown in Listing 8.1 (identical processes have been omitted).
The first area is the modification of the entity declaration to account
for the state variable output Y. The second area is the inclusion of the
selective signal assignment statement which assigns a value of state variable
106 Chapter 8: Finite State Machine Design Using VHDL
output Y based on the condition of the state variable. The selective signal
assignment statement is evaluated each time a change in signal PS is
detected. Once again, since we have declared an enumeration type for the
state variables, we have no way of knowing exactly how the synthesizer
will decide to represent the state variable. The selective signal assignment
statement in the code of Listing 8.2 only makes it appear like there is
one state variable and the states are represented with a ’1’ and a ’0’. In
reality, there are methods we can use to control how the state variables
are represented and we will deal with those soon.
Lastly, it is important to note that in Listing 8.2 both processes (omitted,
identical to Listing 8.1) and the selective signal assignment are all con-
current statements. Each of them react independently to their respective
input signals.
8.1 VHDL Behavioral Representation of FSMs 107
0/0
EXAMPLE 18. Write the
VHDL code that implements State
the FSM shown on the right. 00
Consider the state variables 0/0 1/0
as outputs of the FSM. input(X)/output(Z2)
0/0 10
1/0
SET 11
1/1
As usual, there are a couple of fun things to point out about the solution
for Example 18. Most importantly, you should note the similarities between
this solution and the previous one.
The FSM has one Mealy-type output but its value is the default one in
the first two states. In the final when clause, the Z2 output appears
with a different value within if statement. The fact that the Z2 output
is different in the context of state ST2 makes it a Mealy-type output.
outputs as a bundle which has the effect of slightly changing the form
of the selected signal assignment statement appearing at the end of the
architecture description ("00", "10" etc).
Note that the default values at line 18 considerably simplified the code,
as no else statements were required.
One-hot encoding uses one bit in the state register for each state of the
1
In this case, encoding refers to the act of assigning a unique pattern of 1’s and 0’s
to each of the states in order to make them unambiguous from other states.
2
The ceiling function y = dxe assigns y to the smallest integer that is greater or
equal to x.
112 Chapter 8: Finite State Machine Design Using VHDL
FSM. For a one-hot encoding FSM with 16 states, 16 flip flops are required.
However only four flip flops are required if the same FSM is implemented
using a binary encoding. One-hot encoding simplifies the logic and the
interconnections between overall logic. Despite looking quite wasteful in
terms of employed logic, one-hot encoding often results in smaller and
faster FSMs.
For one-hot encoded FSMs, only one flip-flop is asserted at any given
time. This requires that each distinct state be represented by one flip-flop.
In one-hot encoding, the number of flip-flops required to implement a FSM
is therefore equal to the number of states in the FSM. The closed form of
this relationship is shown in equation 8.2.
SOLUTION. The state diagram shows four states, one external input X,
two external outputs Z1 and Z2 with the Z2 output being a Mealy output.
This is a Mealy machine that indicates one-hot encoding should be used
to encode the state variables. We will approach the implementation of this
FSM one piece at the time.
Listing 8.5 shows the modifications to the entity declaration required to
convert the full encoding used in Example 19 to one-hot encoding. It is
clear that the only change is in the Y output bit-width.
Listing 8.6 shows the required modifications to the state variable in
order to move from enumeration types to a special form of assigned types.
Forcing the state variables to be truly encoded using one-hot encoding
requires these two extra lines of code as is shown in Listing 8.6. These two
lines of code essentially force the VHDL synthesizer to represent each state
of the FSM with its own storage element. In other words, each state is
represented by the ”string” modifier as listed. This forces four bits per state
to be remembered by the FSM implementation which essentially requires
four flip-flops. Note that this merges the two best features of the previous
examples: the state type enumeration allows the convenient use of the
symbolic names (ST0 etc) and the attribute specifies the encoding without
actually forcing the signal representation (like in Example 19).
114 Chapter 8: Finite State Machine Design Using VHDL
8 -- the new approach (merging the best of the two previous ones)
9 type state_type is (ST0, ST1, ST2, ST3);
10 attribute ENUM_ENCODING: STRING;
11 attribute ENUM_ENCODING of state_type: type is "1000 0100 0010 0001";
12 signal PS, NS : state_type;
Listing 8.7 presents the final architecture for Example 20. Note that the
default case is assigned an invalid one-hot state (all bits are zero). This
decision depends strongly on the actual problem being solved.
8.2 One-Hot Encoding for FSMs 115
14 comb_proc: process(PS,X)
15 begin
16 Z1 <= '1'; Z2 <= '0'; -- outputs: Z1 (Moore) Z2 (Mealy)
17 case PS is
18 when ST0 => if (X = '0') then NS <= ST1; Z2 <= '0';
19 else NS <= ST0; Z2 <= '1';
20 end if;
21 when ST1 => if (X = '0') then NS <= ST2; Z2 <= '0';
22 else NS <= ST1; Z2 <= '1';
23 end if;
24 when ST2 => Z1 <= '0'; -- Moore output
25 if (X = '0') then NS <= ST3; Z2 <= '0';
26 else NS <= ST2; Z2 <= '1';
27 end if;
28 when ST3 => if (X = '0') then NS <= ST0; Z2 <= '0';
29 else NS <= ST3; Z2 <= '1';
30 end if;
31 when others => NS <= ST0; -- Important for One Hot Enc.
32 end case;
33 end process comb_proc;
34
2. changing the inputs at the same clock edge which activates the FSM,
besides being a probable violation in the final hardware, may result
in erroneous simulated behavior, with delayed state transitions;
comb_proc: process(PS,X)
begin
case PS is
Z1 <= '0'; Z2 <= '0';
when A =>
Z1 <= '0';
if (X='0') then NS<=A; Z2<='1';
else NS <= B; Z2 <= '0';
end if;
when B =>
Z1 <= '1';
if (X='0') then NS<=A; Z2<='0';
else NS <= C; Z2 <= '1';
end if;
when C =>
Z1 <= '1';
if (X='0') then NS<=B; Z2<='1';
else NS <= A; Z2 <= '0';
end if;
when others =>
Z1 <= '1'; NS<=A; Z2<='0';
end case;
end process comb_proc;
end fsm;
8.4 Exercises: Behavioral Modeling of FSMs 119
EXERCISE 2. Write a
VHDL behavioral model that Y1 Y0
could be used to implement X1/Z
X2/Z
-- library declaration
library IEEE;
use IEEE.std_logic_1164.all;
-- entity
entity fsmx is
Port ( BUM1,BUM2 : in std_logic;
CLK : in std_logic;
TOUT,CTA : out std_logic);
end fsmx;
-- architecture
architecture my_fsmx of fsmx is
type state_type is (S1,S2,S3);
signal PS,NS : state_type;
begin
sync_p: process (CLK)
begin
if (rising_edge(CLK)) then
PS <= NS;
end if;
end process sync_p;
when S1 =>
CTA <= '0';
if (BUM1 = '0') then
TOUT <= '0';
NS <= S1;
elsif (BUM1 = '1') then
TOUT <= '1';
NS <= S2;
end if;
when S2 =>
CTA <= '0';
TOUT <= '0';
NS <= S3;
when S3 =>
CTA <= '1';
TOUT <= '0';
if (BUM2 = '1') then
NS <= S1;
elsif (BUM2 = '0') then
NS <= S2;
end if;
EXERCISE 4.
Write the VHDL State (a)
INIT
behavioral model Z1 0
-- library declaration
library IEEE;
use IEEE.std_logic_1164.all;
-- entity
entity fsm is
port ( X,CLK : in std_logic;
RESET : in std_logic;
Z1,Z2 : out std_logic;
Y : out std_logic_vector(2 downto 0));
end fsm;
-- architecture
architecture my_fsm of fsm is
type state_type is (A,B,C);
attribute ENUM_ENCODING: STRING;
attribute ENUM_ENCODING of state_type: type is "001 010 100";
signal PS,NS : state_type;
begin
with PS select
Y <= "001" when A,
"010" when B,
"100" when C,
"001" when others;
end my_fsm;
8.4 Exercises: Behavioral Modeling of FSMs 123
EXERCISE 6. Write a
1/0 1/0
VHDL behavioral model
code that can be used to
implement the state dia- State 00 01
Z1 1 0
gram shown on the right. 0/1
All state variables should input(X)/output(Z2)
be encoded as listed and 0/0 1/0 1/0 0/0
also provided as outputs
of the FSM. 0/0
10 11
1 0
-- library declaration
library IEEE;
use IEEE.std_logic_1164.all;
-- entity
entity fsm is
Port ( CLK,CLR,SET,X1,X2 : in std_logic;
Z1,Z2 : out std_logic);
end fsm;
-- architecture
architecture my_fsm of fsm is
type state_type is (sA,sB,sC,sD);
attribute ENUM_ENCODING: STRING;
attribute ENUM_ENCODING of state_type: type
is "1000 0100 0010 0001";
signal PS,NS : state_type;
begin
sync_p: process (CLK, CLR, SET) -- process
begin
if (CLR = '1' and SET = '0') then
PS <= sA;
elsif (CLR = '0' and SET = '1') then
PS <= sD;
elsif (rising_edge(CLK)) then
PS <= NS;
end if;
end process sync_p;
EXERCISE 8.
0 0
Write the VHDL
behavioral model code
that can be used to 000 101 State
00 11 Z1 Z2
implement the state
diagram shown on 0 0 input(X)
1 1 1 1
the right. The state
variables should be 001 110
encoded as listed 00 11
and also provided as
0 0
outputs of the FSM. 1 1
010 111
0
00 11
0
1
011
10
100
01
126 Chapter 8: Finite State Machine Design Using VHDL
EXERCISE 9. Write
0/0
the VHDL behavioral
model code that can be
used to implement the 00 State
0 Z1 Z2
state diagram shown on
the right. The state vari- 1/0 input(X)/output(Z2)
ables should be encoded 0/0
01
as listed and also provided
0
as outputs of the FSM.
1/0
0/0
10
0
0/0 1/0
11
1
1/1
X/Z
8.4 Exercises: Behavioral Modeling of FSMs 127
X1/Z
X1/Z X2/Z
EXERCISE 12. Write the VHDL behavioral model code that can be used
to implement the state diagram shown on the right. The state variables
should be encoded as listed and also provided as outputs of the FSM.
EXERCISE 13.
Write the VHDL
Y3 Y2 Y1
behavioral model code (a)
that can be used to
input/outputs(CS,RD) 001
implement the state
X1/CS,RD
diagram shown on X1/CS,RD
the right. The state X2/CS,RD
variables should be 100 010
X2/CS,RD
encoded as listed
(c) -/CS,RD (b)
and also provided as
outputs of the FSM.
128 Chapter 8: Finite State Machine Design Using VHDL
EXERCISE 14.
Write the VHDL
Y3 Y2 Y1
behavioral model code Z1 (a)
that can be used to
001
implement the state input/output(Z2)
0
diagram shown on
X1/Z2
the right. The state
X1/Z2
variables should be X2/Z2
X1/Z2
encoded as listed
100 X2/Z2 010
and also provided as
1 1
outputs of the FSM.
(c) X1/Z2 (b)
Structural Modeling In VHDL
9
There are generally three approaches to writing VHDL code: data-flow
modeling, behavioral modeling and structural modeling. Up to this point,
this book has only dealt with data-flow and behavioral models, since all
examples were implemented with a single module. This section presents
an introduction to structural modeling.
As projects become more complex, it becomes less likely that any useful
design can be implemented with a single module (using one or two of the
types of VHDL models we have seen so far). We have already seen this
property in dealings with FSMs where process statements (behavioral)
and selective signal assignment statements (data-flow) were mixed. The
result was a hybrid VHDL model. This will also be the case for structural
modeling because even if all modules follow the behavioral approach, their
connection intrinsically follows the data-flow approach.
The design of complex digital circuits using VHDL should closely resemble
the structure of complex computer programs. Many of the techniques and
practices used to construct large and well structured computer programs
written in higher-level languages should also be applied when using VHDL.
This common structure we are referring to is the ever so popular modular
approach to coding. The term structural modeling is the terminology
that VHDL uses for modular design. The VHDL modular design approach
directly supports hierarchical design which is essentially employed when
attempting to understand complex digital designs.
130 Chapter 9: Structural Modeling In VHDL
The benefits of modular design to VHDL are similar to the benefits that
modular design or object-oriented design provides for higher-level computer
languages. Modular designs promote understandability by packing low-level
functionality into modules. These modules can be easily reused in other
designs thus saving the designer time by removing the need to reinvent and
re-test the wheel. The hierarchical approach extends beyond code written
on file level. VHDL modules can be placed in appropriately named files
and libraries in the same way as higher-level languages. Moreover, there
are often libraries that contain useful modules that can only be accessed
using a structural-modeling approach. Having access to these libraries and
being fluent in their use will serve to increase your perception as a VHDL
guru.
After all the commentary regarding complex designs, we present a few
simple examples. Though the structural approach is most appropriately
used in complex digital designs, the examples presented in this section are
rather simplistic (for didactical purposes) in nature. These examples show
the essential details of VHDL structural modeling. It is up to the designer
to conjure up digital designs where a structural modeling approach would
be more appropriate. Keep in mind that your first exposure to structural
modeling may be somewhat rough. Although there is some new syntax
to become familiar with, once you complete a few structural designs, this
new syntax becomes ingrained in your brain and it becomes second nature
to apply where required.
The tendency at this juncture in your VHDL programming career is
to use some type of schematic capture software instead of learning the
structural modeling approach. The fact is that no one of consequence uses
the schematic capture software these days even though it is taught in
many university textbooks. The funny part about this entire process is
that the schematic capture software is a tool that allows you to visually
represent circuits but in the end generates VHDL code (the only thing the
synthesizer understands is a Hardware Description Language like VHDL).
9.1 VHDL Modularity with Components 131
The first part of the solution is to provide entity and architecture imple-
mentations for the individual gates shown in Fig. 9.1. We need to provide
one implementation of an XNOR gate and one of a 3-input AND gate. We
only need to provide one definition of the XNOR gate despite the fact that
actually three are shown in the diagram. The modular VHDL approach
allows us to reuse circuit definitions and we take advantage of this feature.
These definitions are shown in Listing 9.1.
1 ----------------------------------
2 -- Description of XNOR function --
3 ----------------------------------
4 entity big_xnor is
5 port ( A, B : in std_logic;
6 F : out std_logic);
7 end big_xnor;
8
Step 3. Declare required internal signals used to connect the design units.
1 -----------------------------------------------
2 -- Interface description of 3-bit comparator --
3 -----------------------------------------------
4 entity my_compare is
5 port ( A_IN : in std_logic_vector(2 downto 0);
6 B_IN : in std_logic_vector(2 downto 0);
7 EQ_OUT : out std_logic);
8 end my_compare;
Listing 9.3
in this context would be that these signals represent wires which connect
components inside a closed box, and are visible from the outside.
The internal signals effectively provide an interface between the various
design units that are instantiated in the final design. For this example,
three signals are required to connect the outputs of the XNOR gates to
the inputs to the AND gate. Internal signal declarations such as these
appear with the component declarations in the architecture declaration
after the architecture line and before the begin line. Note that the
declaration of intermediate signals is similar to the signal declaration
contained in the entity body. The only difference is that the intermediate
signal declaration does not contain the mode specifier. We have previously
dealt with intermediate signals in other sections of this book. Signal
declarations are included as part of the final solution shown in Listing 9.4.
20 begin
21 x1: big_xnor port map (A => A_IN(2), B => B_IN(2),
22 F => p1_out);
23
30 a1: big_and3 port map (A => p1_out, B => p2_out, C => p3_out,
31 F => EQ_OUT);
32 end comp1;
Listing 9.4: VHDL code for the design hierarchy for the 3-bit comparator
Note that the instantiation process includes labels for all instantiated
design units (x1, x2, x3 and a1). Labels should always be used as part
of design unit instantiation because they increase the understandability
138 Chapter 9: Structural Modeling In VHDL
Due to the fact that this design was relatively simple, it was possible
to bypass one of the interesting issues that arises when using structural
modeling. Often when dealing with structural designs, different levels
of the design will contain the same signal name. The question arises
as to whether the synthesizer is able to differentiate between the signal
names across the hierarchy. VHDL synthesizers, like compilers for higher-
level languages, are able to handle such instances. Signals with the same
names are mapped according to the mapping presented in the component
instantiation statement. Probably the most common occurrence of this is
with clock signals. In this case, a component instantiation such as the one
shown in Listing 9.6 is both valid and commonly seen in designs containing
a system clock. Name collision does not occur because the signal name on
the left-hand side of the => operator is understood to be internal to the
component while the signal on the right-hand side is understood to reside
in the next level up in the hierarchy.
Listing 9.6: Example of the same signal name crossing hierarchical boundaries
Listing 9.7: Parity calculation implementation with generic input array size
Listing 9.8 shows how the generic code can be declared and instantiated
in your own code via the already seen component method. Specifically,
in Listing 9.8 the above generic parity check module is used to create a
4-bit parity check module.
To achieve the mentioned modularity the keyword generic was used
inside the entity field in the code above and again inside the component
field during its declaration in the code below. The generic field is used
to allow you to control all generic parameters.
Notice how during instantiation (see Listing 9.8, lines 13 and 14) the
keyword generic map was used in conjunction with the keyword port
map to define the generic variables.
Once again, to successfully simulate and synthesize the design shown in
Listing 9.8, the code of Listing 9.7 needs to be included in your VHDL
project as well.
9.3 Structured Testbenches (Unit Testing) 141
Listing 9.8: Use of generic for the construct of a generic parity check code
1 -- library omitted
2 entity fulladd is
3 port(a, b, cin : in std_ulogic;
4 sum, cout : out std_ulogic);
5 end fulladd;
6
example used “for loops” to simplify the test code. The generate loop,
which is used outside de scope of a process, not only allows the code to
be more compact but also permits the use of the generic feature. Take
some time to understand how the “fulladd” instances are interconnected.
Start by unrolling the loop and pay attention to how the cout(i) and
cout(i+1) signals are used to connect the several carries of the operation.
1 -- library omitted
2 entity add is
3 generic (W: positive := 3);
4 port (a, b : in std_logic_vector(W-1 downto 0);
5 sum : out std_logic_vector(W downto 0));
6 end add;
7
The testbench for the generic adder generates all the possible values for
addition and also adapts the code to whatever bit witdth is defined as a
constant at line 8. It would also be trivial to add automatic verification to
this example but this will be postponed to Chapter 11 where data objects
the conversion are covered in detail.
144 Chapter 9: Structural Modeling In VHDL
The VHDL structural model supports the reuse of design units. This
9.4 Important Points 145
includes units you have previously designed as well as the ability to use
predefined module libraries.
If you use one FPGA software development tool from one of the
major FPGA players in the market, you will be able to use digital
blocks already developed once you declare them. In this case the entity
declaration is not the one of Listing 9.2 but instead a simple library
inclusion in your VHDL code that looks like:
library UNISIM;
use UNISIM.VComponents.all;
All digital blocks available from this library package are described in
the documentation of the FPGA software development tool (e.g. Xilinx
Vivado).
146 Chapter 9: Structural Modeling In VHDL
component T_FF
port ( T,CLK : in std_logic;
Q : out std_logic);
end component;
component bb1
port (D,E : in std_logic;
F,G,H : out std_logic);
end component;
component bb2
port ( L,M,N : in std_logic;
P : out std_logic);
end component;
b2: bb2
port map ( L => x1, M => x2, N => x3, P => C);
end my_ckt;
a) b)
c) d)
Registers and Register Transfer Level
10
The concept of a register in VHDL and its subsequent use in digital circuit
design is probably one of the more straightforward concepts in VHDL. A
register in VHDL is simply a vector version of a D flip-flop in which all
operations on the flip-flops occur simultaneously. The “register transfer
level”, or RTL, is a flavor of design that is primarily concerned with how
and when data is transferred between the various registers in a digital
system. RTL-level design in often associated with “data-path” designs
which require the careful control and timing of the data that is being
transferred between registers. The controls associated with even simple
registers are sufficient to ensure that some outside entity has adequate
control over the “sequencing” of data through the circuit associated with
the data-path. In these cases, the proper sequencing of data transfers is
controlled by an FSM.
SOLUTION. The solution for the 8-bit register looks amazingly similar
to a model of a D flip-flop. The full solution to Example 22 is shown in
Listing 10.1. There are a couple of things worth noting in this solution:
Note that there is an if statement that does not contain a corres-
ponding else which is what generates the memory element. For this
example, eight bit-sized memory elements (D-flip-flops) are inferred
from the in and out signals widths. The storage elements are asso-
ciated with the REG OUT bundle. The ease in using VHDL code to
generate D flip-flops in this manner is related to D flip-flops being the
most widely used type of flip-flop in digital design.
The code uses a bundle signal for both the input and output. The
assignment of the bundles to other bundles is straightforward in VHDL
as is shown in the code. In many cases, such as the one in this example,
there is no need to use a bundle access operator in the VHDL model.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3
4 entity reg8 is
5 port ( REG_IN : in std_logic_vector(7 downto 0);
6 LD, CLK : in std_logic;
7 REG_OUT : out std_logic_vector(7 downto 0));
8 end reg8;
9
All of the signals shown in the Example 23 have external linkage except
for the output of the MUX. The MUX output is connected to the inputs
of both registers. The final approach taken in this solution is typical
in VHDL: many processes that communicate with each other through
shared signals. In this example, there is only one shared signal but this
is a fairly simple project. The same inter-process communication model
is used in more complicated circuits.
The model for the 2:1 MUX uses the terminology (others => ’0’).
This is a short-hand terminology for assigning all of the outputs to ’0’.
The real nice part about this instruction is that you do not need to
know how many 0’s you need to write. This is a nice feature in that
if the width of the associated bundle were to change, this particular
line of code would not need to be modified. This syntax together with
concatenation also allows, for example, that a few specific bits are set
to ’1’ while all the “others” are set to ’0’ (regardless of how many)
10.1 RTL-level Design 153
The circuit in this example is slightly more complex than most of the
examples seen so far. Additionally, remember that there are many different
solutions to the same problem. This is a common occurrence in VHDL;
in fact, many times there is no best method for implementing a given
circuit but different approaches may provide better results in one aspect
or another (speed, resource usage, maintainability etc).
154 Chapter 10: Registers and Register Transfer Level
1 entity mux2t1 is
2 port ( A, B : in std_logic_vector(7 downto 0);
3 SEL : in std_logic;
4 M_OUT : out std_logic_vector(7 downto 0));
5 end mux2t1;
6
The very important thing to note about the top-level solution in List-
ing 10.4 is to not be intimidated by the fact the the code doesn’t have
any logic besides the simple interconnection of modules. The code is well
structured; if you are able to recognize this structure, you will be more
apt to understand the solution.
The VHDL source code shown in Listing 10.4 is nicely formatted. In
particular, the code is nicely indented. Properly indented code is highly
desirable in that it nicely presents information based on the indentation.
No surprise here but properly formatted code is easier to understand.
Better yet, good looking code leads people who may or may not know
otherwise into thinking your code is actually as good as it looks. In this
busy world of ours, a quick glance is just about all the time people (bosses
and teachers) have to dedicate to perusing your VHDL source code.
10.1 RTL-level Design 155
1 entity ckt_rtl is
2 port (D0_IN, D1_IN : in std_logic_vector(7 downto 0);
3 CLK, SEL, LDA, LDB : in std_logic;
4 REG_A, REG_B : out std_logic_vector(7 downto 0));
5 end ckt_rtl;
6 architecture rtl_structural of ckt_rtl is
7 component mux2t1
8 port ( A, B : in std_logic_vector(7 downto 0);
9 SEL : in std_logic;
10 M_OUT : out std_logic_vector(7 downto 0));
11 end component;
12 component reg8
13 port ( REG_IN : in std_logic_vector(7 downto 0);
14 LD, CLK : in std_logic;
15 REG_OUT : out std_logic_vector(7 downto 0));
16 end component;
17 signal s_mux_result : std_logic_vector(7 downto 0);
18 begin
19 ra: reg8 port map ( REG_IN => s_mux_result, LD => LDA,
20 CLK => CLK, REG_OUT => REG_A );
21 rb: reg8 port map ( REG_IN => s_mux_result, LD => LDB,
22 CLK => CLK, REG_OUT => REG_B );
23 m1: mux2t1 port map ( A => D0_IN, B => D1_IN,
24 SEL => SEL, M_OUT => s_mux_result);
25 end rtl_structural;
Listing 10.6 introduces the process for text processing, using some features
present only in the 2008 version of the VHDL standard.
At line 10, a single line from the input text file is read to a local variable.
Each following call to hread (hexadecimal format) and read (binary
format) parses a value from the line variable and stores in an appropriate
variable, which transfers the value to a signal. If any of the parsing fails, an
error is indicated in the ok variable, which will result in an error message.
After the clock cycle has passed (line 29) the outputs of the circuit being
tested are formatted in a text line, which is saved into an output file.
Listing 10.7 presents a possible text input file which applies alternating
enable signals and values to the inputs, testing both registers. The sequence
also verifies if input values are ignored when no enable is active. The first
line is discarded as a comment.
# D0_IN D1_IN SEL LDA LDB
00 00 0 1 1
01 01 0 0 0
00 02 1 1 0
03 00 0 0 1
04 04 0 0 0
Listing 10.8 is the resulting output for the specified test, which is con-
sistent with the waveform output, presented in Figure 10.1, confirming not
only the storage but also the ignored input values (without enable signals).
00 00
00 00
02 00
02 03
02 03
The read and write functions may seem to provide less features than the
ones in a good programming language library, but are more than enough
considering that the text files may be generated and verified automatically.
10.3 Important Points 159
Each of the data objects can optionally be assigned initial values. Signal
declarations do not usually include initial values as opposed to constants
which obviously require one. Initial values for signals are not implementable
on silicon by the synthesizing tools but are taken into consideration by
VHDL simulation tools. When simulating a project, the module that drives
the simulation (called “testbench”) may have initialized signals. Even if an
FPGA tool supports this configuration, the value would only be applied at
power-up. Since signal initial values are only relevant to storage elements,
proper initialization of should be performed explicitly by a reset signal.
11.3 Variables and Assignment Operator “:=” 165
Example declarations for the three flavors of data objects are provided
in Table 11.2. These examples include several new data types which will
be discussed in the next sections.
Data object Declaration form
Signal signal sig var1 : std logic := ’0’;
signal tmp bus : std logic vector(3 downto 0):="0011";
signal tmp int : integer range -128 to 127 := 0;
signal my int : integer;
Variable variable my var1, my var2 : std logic;
variable index a : integer range (0 to 255) := 0;
variable index b : integer := -34;
Constant constant sel val : std logic vector(2 downto 0):="001";
constant max cnt : integer := 12;
Table 11.2: Example declarations for signal, variable and constant data objects.
The std logic type is quite powerful and, besides the expected ’0’ and
’1’, can assume values like ’U’ (uninitialized), ’X’ (unknown), ’-’ (don’t
care), and many others related to the actual electrical implementation. It
is not uncommon to find older implementations which use the simpler type
bit (and its respective vector bit vector), which is restricted to the
values ’0’ and ’1’.
The range of the default integer type is (−2, 147, 483, 648 to 2, 147, 483, 647).
These numbers should seem familiar since they represent the standard
32-bit range for a signed number: from −(231 ) to +(231 − 1). Other types
similar to integers include natural and positive types. These types
are basically integers with shifted ranges. For example, the natural and
positive types range from 0 and 1 to the full 31-bit range, respectively.
Examples of integer declarations are shown in the following listing.
The answer to this question is in Listing 11.1. The std logic vector
type should not be used to define a numerically meaningful signal or variable.
These types are reserved for vectors that are interpreted and behave like
a number. The std logic vector type should be only employed for
defining “bags of bits”.
The use of signed/unsigned types is desirable any time your bags
of bits (signals, variables or constants) stop being “bags” and become
numbers of type signed, unsigned or even integers.
1 library IEEE;
2 use IEEE.std_logic_1164.all; -- defines std_logic_vector type
3 use IEEE.numeric_std.all; -- defines signed and unsigned types
4
5 entity double_sum is
6 port (
7 in1, in2 : in std_logic_vector (7 downto 0);
8 out1 : out std_logic_vector (7 downto 0));
9 unsig_in : in unsigned(7 downto 0);
10 unsig_out : out unsigned(7 downto 0));
11 end double_sum;
12
19 -- legal operations:
20 unsig_out <= unsig_in + 1;
21 unsig_out <= unsigned(in1) + 1;
22 out1 <= std_logic_vector(unsigned(in1) + 1);
23 end arch;
possible enumerations of course include ’1’ and ’0’. The actual definition
is shown in the Listing 11.2. The std logic type is a resolved version
of the std ulogic type. Resolved means that unlike for std ulogic
types, when you use std logic type signals, you can assign multiple
drivers to the same signal without having the compiler complain about it.
The std logic type uses the VHDL character type in its definition.
Although there are nine values in the definition shown in Listing 11.2, this
book only deals with ’0’, ’1’, ’Z’ and ’-’. The ’Z’ is generally used
when dealing with bus structures. This allows a signal or set of signals (a
bus) to have the possibility of being driven by multiple sources without
the need to generate resolution functions. When a signal is driven to its
high-impedance state, the signal is not driven from that source and is
effectively removed from the circuit. Finally, since the characters used in
the std logic type are part of the definition, they must be used as listed.
Mind the use of lower-case letters will generate an error.
11.9 std logic Types 173
SOLUTION. As usual for more complex concepts and circuits, there are
a seemingly infinite number of solutions. A solution that uses several of
the concepts discussed in this section is presented in Listing 11.3. Some of
the more important issues in this solution are listed below.
The type declaration for my count appears in the architecture body
before the begin statement.
A constant is used for the max count variable. This allows for quick
adjustments in the clock frequency. In this example, this concept is
somewhat trivial because the max count variable is used only once.
Variable declarations occur in the process body before the begin line.
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3 use IEEE.numeric_std.all;
4
5 entity clk_div is
6 Port (
7 clk : in std_logic;
8 div_en : in std_logic;
9 sclk : out std_logic);
10 end clk_div;
11
With all the conversions explained and the differences between variables
and signals detailed (Section 11.4), the automatic verification is added, as
detailed in Listing 11.4.
The delay in line 21 is crucial to allow the arithmetic at line 23 to reflect
the value sum = a + b. Since these three vectors are assigned to signals,
their values would be the ones from the previous loop, up until de wait.
After that, the conversion to isum already takes the result of the addition
in the current loop, as well as the sum performed directly with the two
implicit variables (ai and bi).
The assert directive will compare the values which are expected to
be the same and print an error message otherwise. Note that, by using
other conversions, a full report of the test values and the wrong addition
is informed. The keyword failure can be replaced by error at line 26,
if the desired behavior of the test is not to be interrupted at the first error
found. This may be used it is sometimes easier to spot a fault is a pattern
emerges in the errors.
The standard IEEE library numeric std is needed when you want
to use signed and/or unsigned types. The standard IEEE library
numeric std is almost always preferred over the non-standard
std logic arith library.
You cannot increment a std logic vector type signal, you need to
first convert it into an unsigned, a signed or an integer:
11.11 Important Points 177
1 library IEEE;
2 use IEEE.std_logic_1164.all;
3 use ieee.numeric_std.all
4
Figure 11.1: Direct casts and type conversions with explicit lengths.
Looping Constructs
12
As the circuits you are required to design become more and more complex,
you will find yourself searching for more functionality and versatility from
VHDL. You will probably find what you are looking for in various looping
constructs which are yet another form of VHDL statement. This chapter
provides descriptions of several types of looping constructs and some details
regarding their use.
There are two types of loops in VHDL: for loops and while loops.
The names of these loops should seem familiar from your experience with
higher-level computer programming languages. Generally speaking, you can
leverage your previous experience with these loop types when describing
the behavior of digital circuits. The comforting part is that since these
two types of loops are both sequential statements, they can only appear
inside processes. You will also be able to apply to the circuits you will be
describing using VHDL the algorithmic thinking and designing skills you
developed in coding with higher-level computer languages. The syntax is
slightly different but the basic structured programming concepts are the
same.
level language programming, the syntax of the language is such that you
can use either type of loop in any given situation by some modification of
the code. The same is true in VHDL. But although you can be clever in the
way you design your VHDL code, the best approach is to make the code
readable and understandable. Keeping this concept in mind lets us see the
functional differences between for and while loops. This basic difference
can be best highlighted by examining the code provided in Listing 12.1.
1 -- for loop
2 my_label: for index in a_range loop
3 -- sequential statements...
4 end loop my_label;
5
6 -- while loop
7 my_label: while (condition) loop
8 -- sequential statements...
9 end loop my_label;
Listing 12.1: The basic structure of the for and while loops.
The major difference between these two loops lies in the number of
iterations the loops will perform. This difference can be classified as under
what conditions the circuit will terminate its iterations. If you know the
number of iterations the loop requires, you should use a for loop. As you
will see in the examples that follow, the for loop allows you to explicitly
state the number of iterations that the loop performs.
The while loop should be used when you do not know the number of
iterations the loop needs to perform. In this case, the loop stops iterating
when the terms stated in the condition clause are not met. Using these
loops in this manner constitutes a good programming practice. The loop
labels are optional but should be always used to clarify the associated
VHDL code. Use of loop labels is an especially good idea when nested
loops are used and when loop control statements are applied.
12.1 for and while Loops 181
The index variable used in the for loop contains some strange qualities
which are listed below. Although your VHDL synthesizer should be able
to flag these errors, you should still keep these in mind when you use a
for loop and you will save yourself a bunch of debugging time. Also note
that the loop body has been indented to make the code more readable.
Enhanced readability of the code is always a good thing.
The index variable (cnt val in the examples) does not need to be
declared, it is in fact done implicitly.
The index variable can only step through the loop in increments of one.
The identifier used for the index variable can be the same as another
variable or signal; no name collisions will occur. The index variable will
effectively hide identifiers with the same name inside the body of the
loop. Using the same identifier for two different values constitutes bad
programming practice and should be avoided.
The specified range for the index (when specified outside of the loop
declaration) can be of any enumerated type.
next Statement
The next statement allows for the loop to bypass the remaining statements
within the body of the loop and start immediately at the next iteration. In
for loops, the index variable is incremented automatically before the start
of the upcoming iteration. In while loops, it is up to the programmer to
ensure that the loop operates properly when the next statement is used.
There are two forms of the next statement and both forms are shown
in the next listing. These are two examples that use the next statement
and do not necessarily represent a good programming practice nor really
contain meaningful code.
3 -- using 'if':
4 for cnt_val in 0 to 50 loop
5 if (my_sum = 20) then
6 next;
7 end if;
8 my_sum := my_sum + 1;
9 end loop;
10
11 -- using 'when':
12 variable my_sum : integer := 0;
13
exit Statement
The exit statement allows for the immediate termination of the loop and
can be used in both for loops and while loops. Once the exit statement
is encountered in the flow of the code, control is returned to the statement
following the end loop statement associated with the given loop. The
exit statement works in nested loops as well. The two forms of the exit
statement are similar to the two forms of the next statement. Examples
of these forms are provided in the next listing. Note that these codes are
merely presenting the syntax for the exit statement. It is obviously a bad
practice to prematurely abort loops with known lengths like these.
3 -- using 'if':
4 for cnt_val in 0 to 50 loop
5 if (my_sum = 20) then
6 exit;
7 end if;
8 my_sum := my_sum + 1;
9 end loop;
10
13 -- using 'when':
14 while (my_sum < 300) loop
15 exit when (my_sum = 20);
16 my_sum := my_sum + 1;
17 end loop;
Standard Digital Circuits in VHDL
13
As you know or as you will be finding out soon, even the most complex
digital circuit is composed of a relatively small set of standard digital
circuits plus some associated control signals. This list of standard digital
circuits is a mixed bag of combinatorial sequential devices such as MUXes,
decoders, counters, comparators, registers, etc. The art of digital design
using VHDL is centered around the proper selection and interfacing of these
devices. The actual creation and testing of these devices is de-emphasized.
The most efficient approach to utilizing standard digital circuits using
VHDL is to use existing code for these devices and modify them according
to the needs of your particular design. This approach allows you to utilize
your current knowledge of VHDL to quickly and efficiently design complex
digital circuits. The following listings show a set of standard digital devices
and the VHDL code used to describe them. The following circuits are
represented in various sizes and widths. Note that the following circuit
descriptions represent possible VHDL descriptions but are by no means
the only descriptions. They do however provide starting points for you to
modify for your own design needs.
186 Chapter 13: Standard Digital Circuits in VHDL
1 ---------------------------------------------------
2 -- D flip-flop: RET D flip-flop with single output
3 --
4 -- Required signals:
5 ---------------------------------------------------
6 -- CLK,D: in std_logic;
7 -- Q: out std_logic;
8 ---------------------------------------------------
9 process (CLK)
10 begin
11 if (rising_edge(CLK)) then
12 Q <= D;
13 end if;
14 end process;
1 ---------------------------------------------------------------
2 -- D flip-flop: FET D flip-flop with asynchronous preset. The
3 -- preset input takes precedence over the synchronous input.
4 --
5 -- Required signals:
6 ---------------------------------------------------------------
7 -- CLK,D,S: in std_logic;
8 -- Q: out std_logic;
9 ---------------------------------------------------------------
10 process (CLK,S)
11 begin
12 if (S = '0') then
13 Q <= '1';
14 elsif (falling_edge(CLK)) then
15 Q <= D;
16 end if;
17 end process;
13.3 8-Bit Register with Load Enable - Behavioral Model 187
1 -----------------------------------------------
2 -- Register: 8-bit Register with load enable.
3 --
4 -- Required signals:
5 -----------------------------------------------
6 -- CLK,LD: in std_logic;
7 -- D_IN: in std_logic_vector(7 downto 0);
8 -- D_OUT: out std_logic_vector(7 downto 0);
9 -----------------------------------------------
10 process (CLK)
11 begin
12 if (rising_edge(CLK)) then
13 if (LD = '1') then -- positive logic for LD
14 D_OUT <= D_IN;
15 end if;
16 end if;
17 end process;
1 ----------------------------------------------------------
2 -- Counter: synchronous up/down counter with asynchronous
3 -- reset and synchronous parallel load.
4 ----------------------------------------------------------
5 -- library declaration
6 library IEEE;
7 use IEEE.std_logic_1164.all;
8 use IEEE.numeric_std.all;
9
10 entity COUNT_8B is
11 port ( RESET,CLK,LD,UP : in std_logic;
12 DIN : in std_logic_vector (7 downto 0);
13 COUNT : out std_logic_vector (7 downto 0));
14 end COUNT_8B;
15 architecture my_count of COUNT_8B is
16 signal t_cnt : unsigned(7 downto 0); -- internal counter signal
17 begin
188 Chapter 13: Standard Digital Circuits in VHDL
1 ------------------------------------------------------------------
2 -- Shift Register: unidirectional shift register with synchronous
3 -- parallel load.
4 --
5 -- Required signals:
6 ------------------------------------------------------------------
7 -- CLK, D_IN: in std_logic;
8 -- P_LOAD: in std_logic;
9 -- P_LOAD_DATA: in std_logic_vector(7 downto 0);
10 -- D_OUT: out std_logic;
11 --
12 -- Required intermediate signals:
13 signal REG_TMP: std_logic_vector(7 downto 0);
14 ------------------------------------------------------------------
15 process (CLK)
16 begin
17 if (rising_edge(CLK)) then
18 if (P_LOAD = '1') then
19 REG_TMP <= P_LOAD_DATA;
20 else
21 REG_TMP <= REG_TMP(6 downto 0) & D_IN;
13.6 8-Bit Comparator - Behavioral Model 189
22 end if;
23 end if;
24 D_OUT <= REG_TMP(7);
25 end process;
1 ------------------------------------------------------------------
2 -- Comparator: Implemented as a behavioral model. The outputs
3 -- include equals, less than and greater than status.
4 --
5 -- Required signals:
6 ------------------------------------------------------------------
7 -- CLK: in std_logic;
8 -- A_IN, B_IN: in std_logic_vector(7 downto 0);
9 -- ALB, AGB, AEB: out std_logic
10 ------------------------------------------------------------------
11 process(CLK)
12 begin
13 if ( A_IN < B_IN ) then ALB <= '1';
14 else ALB <= '0';
15 end if;
16
1 --------------------------------------------------------------------
2 -- BCD to 7-Segment Decoder: Implemented as combinatorial circuit.
3 -- Outputs are active low; Hex outputs are included. The SSEG format
4 -- is ABCDEFG (segA, segB etc.)
5 --
190 Chapter 13: Standard Digital Circuits in VHDL
6 -- Required signals:
7 --------------------------------------------------------------------
8 -- BCD_IN: in std_logic_vector(3 downto 0);
9 -- SSEG: out std_logic_vector(6 downto 0);
10 --------------------------------------------------------------------
11 with BCD_IN select
12 SSEG <= "0000001" when "0000", -- 0
13 "1001111" when "0001", -- 1
14 "0010010" when "0010", -- 2
15 "0000110" when "0011", -- 3
16 "1001100" when "0100", -- 4
17 "0100100" when "0101", -- 5
18 "0100000" when "0110", -- 6
19 "0001111" when "0111", -- 7
20 "0000000" when "1000", -- 8
21 "0000100" when "1001", -- 9
22 "0001000" when "1010", -- A
23 "1100000" when "1011", -- b
24 "0110001" when "1100", -- C
25 "1000010" when "1101", -- d
26 "0110000" when "1110", -- E
27 "0111000" when "1111", -- F
28 "1111111" when others; -- turn off all LEDs
1 ----------------------------------------------------------------
2 -- A 4:1 multiplexer implemented as behavioral model using case
3 -- statement.
4 --
5 -- Required signals:
6 ----------------------------------------------------------------
7 -- SEL: in std_logic_vector(1 downto 0);
8 -- A, B, C, D: in std_logic;
9 -- MUX_OUT: out std_logic;
10 ----------------------------------------------------------------
11 process (SEL, A, B, C, D)
12 begin
13 case SEL is
14 when "00" => MUX_OUT <= A;
15 when "01" => MUX_OUT <= B;
13.9 4:1 Multiplexer - Data-Flow Model 191
1 ------------------------------------------------------------
2 -- A 4:1 multiplexer implemented as data-flow model using a
3 -- selective signal assignment statement.
4 --
5 -- Required signals:
6 ------------------------------------------------------------
7 -- SEL: in std_logic_vector(1 downto 0);
8 -- A, B, C, D: in std_logic;
9 -- MUX_OUT: out std_logic;
10 ------------------------------------------------------------
11 with SEL select
12 MUX_OUT <= A when "00",
13 B when "01",
14 C when "10",
15 D when "11",
16 (others => '0') when others;
13.10 Decoder
1 -------------------------------------------------------------------
2 -- Decoder: 3:8 decoder with active high outputs implemented as
3 -- combinatorial circuit with selective signal assignment statement
4 --
5 -- Required signals:
6 -------------------------------------------------------------------
7 -- D_IN: in std_logic_vector(2 downto 0);
8 -- FOUT: out std_logic_vector(7 downto 0);
9 -------------------------------------------------------------------
10 with D_IN select
11 F_OUT <= "00000001" when "000",
12 "00000010" when "001",
192 Chapter 13: Standard Digital Circuits in VHDL