Notes-Day3
Notes-Day3
Digital Design
Suppose we want a piece of hardware that adds two 16-bit numbers together
16
a 16
16 ADD sum
b
How would we approach this using traditional methods? We would have to exercise
some ingenuity to partition this large problem into a series of smaller problems that
are easier to solve. For example, we could partition into a sequence of sixteen 1-bit
adders
a0 + sum0
b0
a1 sum1
+
b1
a2 + sum2
b2
a3 + sum3
b3
and so on …
1
We turn this truth table into a Karnaugh map
We extract Booleans equation from the Karnaugh map
We turn these Boolean equations into AND, OR and NOT gates
Finally, we assemble the 16 units to make our complete circuit:
That’s the traditional design methodology. Let’s do some evaluation of this method.
Design effort
It’s quite a lot of work to get from the design brief (add 16-bit numbers) into the
design using logic gates. It would take about 10 minutes to complete and document
the process. But the chips used in mobile phones or computer games consoles contain
millions of logic gates. If we have to put this much effort into getting out a design that
conatins only a few dozen gates, then we are in trouble. The designer productivity will
be too low; it will take too much time, and cost too much money to undertake a big
project. We need a method that gives higher productivity.
Maintainability
When you are in the midst of designing a piece of hardware, you probably have a
pretty good understanding of the design you produce. However, it’s difficult to work
with gate level designs produced by someone else, or even with designs that were
produced by you a few weeks previously. Just by glancing at the logic schematics
above, can you tell what it actually does? We need a method that gives greater clarity
as to what the designs do.
2
to be implemented on an FPGA. (FPGA design tools have translation utilities that
translate the logic gates into something that the FPGA can use, but the fact still
remains that the effort expended in getting out the gate-level design was largely
wasted, since the FPGA didn’t actually use it.)
12.2 VHDL
VHDL is the VHSIC Hardware Description Language. (VHSIC stands for Very High
Speed Integrated Circuit). The main motivation for its creation was to provide
rigorous and unambiguous specification of modules. Gradually it developed to be
used for other purposes such as synthesis. The other important HDL is Verilog. This
is older than VHDL, and its original form was not very powerful. Over the years, it
has been enhanced and extended with extra features, so that it is now as powerful as
VHDL.
VHDL is based around the notion of being able to view the modules in a design at
different levels of abstraction. Crudely speaking, a high level of abstraction contains
little detail, and a low level of abstraction contains a lot of detail. Low-level design
requires a lot of effort, and we want to avoid this effort until we are sure that it won’t
be wasted. We need to resolve all high level issues before we commit to any low level
design.
There may also be a 4th level, the physical level. This might refer, for example, to the
processing of a piece of silicon that would be necessary to manufacture the required
configuration of logic gates as an application-specific integrated circuit (ASIC).
Alternatively, it might be the generation of the configuration bit stream for a field
programmable gate array (FPGA).
1 Or whatever is the most fundamental design primitive for the hardware implementation we are
intending to use. This would Configurable Logic Blocks (CLBs) for an FPGA or a fuse map for a
CPLD.
3
12.3 Synthesis
A synthesis CAD tool is one that automatically maps a description from one level of
the hierarchy to a lower level on the hierarchy. The main types are as follows:-
Physical synthesis
This performs the mapping from gate-level to physical level. For an ASIC, a
gate level representation is automatically translated to a mask level design of
an integrated circuit. For a PLD or an FPGA, a gate-level design is mapped to
a configuration file that controls how the fuses in the device should be blown,
or how the CLBs should be configured. CAD tools do this task extremely
well.
Logic synthesis
This maps an RTL description to a gate level description. CAD tools also
perform this stage extremely well.
Behavioural synthesis
This maps an algorithmic description to a register transfer or to a gate level
description. At present, automated synthesis tools cannot do this well. It is a
very active area of research, but for now, and the foreseeable future, this is a
task that must be done by humans.
Automatic synthesis leads to great productivity gains, because human designers can
confine their activities to high level design. It has often said that the output of a
designer is limited to about 10-50 items per day, fully debugged and properly
documented. This rate is true whether an item corresponds to a logic gate, a functional
unit of an RTL design, or an equation representing the behaviour of an entire digital
filter for a signal processing system. 10-50 logic gates will only form a very small part
of a system whereas 10-50 RTL functional units may be enough to describe an entire
system. The higher the level at which the designer is working, the more the designer
can produce.
Synthesis also helps us avoid the risk of putting a lot of effort into targeting one
particular manufacturing technology, only to find that we need to re-target the design
to a new technology. If all the low-level synthesis was done by humans, the re-
targeting could take thousands of man hours. If it was done by a CAD tool, then we
simply re-synthesise for the new target hardware, which means leaving a computer
running for a few days.
12.4 Summary
Traditional design methods have many problems. Designer productivity is too low.
Decisions about the implementation have to be made early in the design process. If
the design is re-targeted from one technology to another (say a design originally
implemented on an ASIC is moved to an FPGA) the whole design process needs to be
repeated.
4
HDLs also allow a specification for a module to be simulated. So we can try out
different combinations of input and see what the outputs would do. This can be done
before any detailed gate level design is attempted.
5
13 Introduction to VHDL
In this session we will look at how to do simple designs in VHDL.
b nandgate
c
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC );
END;
Now that we have described the inputs and outputs, we need to say what the device
does, i.e. how its outputs respond to its inputs. This is done in an architecture:
c <= a NAND b;
6
The symbol <= (which is meant to look like a left-pointing arrow) is pronounced
"gets". It means that the signal c gets the value of a NANDed together with the value
of b. Whenever a or b change their value, this statement causes the value of c to be
updated.
If we want to check that our description is functioning correctly, we can feed it into a
simulator, a program that predicts how the outputs would change in response to
changes in the input. Here is the sort of thing we get if we run this code through a
simulator
The horizontal axis is time, ranging from 0 to 100 ns. Traces are shown for the signals
a, b and c. Whenever a or b changes its value, c receives a new value. In order to carry
out the simulation, we need to tell the simulator what we want each of the inputs a and
b to do (in this case we have toggled each from 0 to 1 and then back to 0). The
simulator then works out what the output c would do in response. You can see that c
is carrying out the logic function a NAND b, so the design is correct.
VHDL uses the following logical operators: NOT, AND, OR, NAND, NOR, XOR
This achieves exactly the same function as the first description, but does it in a
different way.
7
The braces show that the two statements should be considered collectively as a block
that makes up the body of the loop.
VHDL uses the keywords BEGIN and END. So in VHDL the loop would look like
this
FOR i IN ( 1 TO N ) LOOP
BEGIN
a(i) = i;
b(i) = a(i) * a(i);
END LOOP;
Note that indentation of the block is used to make it clearer where the block starts and
ends.
13.4 Semicolons
Like C or Java, VHDL uses the semicolon to indicate the end of a statement.
Statements that "open up" a block don't take semicolons. So in C these would be
wrong:
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC );
END;
The keyword IS is "opening up" a block of statements, and therefore does not need a
semicolon. However, note that VHDL is a little inconsistent as to whether IS needs to
be followed by a BEGIN. In an ENTITY, the BEGIN is implied, and the END
statement is answering the IS. By contrast, in an ARCHITECTURE the word BEGIN
must also be there, and the END is answering the BEGIN.
8
13.5 Stylistic issues
13.5.1 Case
VHDL is not case sensitive. All three of these are identical in meaning, and you’ll see
all three styles in textbooks and design magazines:
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC);
END;
entity NANDGATE is
port ( A, B: in std_logic; C: out std_logic);
end;
entity nandgate is
port ( a, b: in std_logic; c: out std_logic);
end;
It used to be considered good style to write all the keywords of VHDL in one case,
and all the names that we have chosen for our design in the other case. This makes it
easier to figure out what is going on in the design.
In lectures we will show all keywords in uppercase to make it clearer to you what is
part of the VHDL language, and what is just a name that I have chosen.
ENTITY nandgate IS
PORT (a,b: IN STD_LOGIC; c: OUT STD_LOGIC);
END;
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC);
END;
13.5.3 Returns
Putting in a carriage return makes no difference to the function of your code. So the
following two are identical in function
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC);
END;
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC;
c: OUT STD_LOGIC);
END;
9
You can use whichever you feel is clearest.
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC);
END ENTITY nandgate;
When you run the compiler, the code will be checked, and if there is a mismatch
between what you say you are ENDing and what VHDL thinks you are ending, then
this will be flagged as an error.
13.5.5 Comments
Comments are introduced by two dashes:
-- This is a comment
LIBRARY IEEE;
The IEEE library contains many sub-libraries, which in turn contain many features.
The VHDL name of a sub-library is a package. In order to say which features of
which packages we wish to access, we use a statement that looks like this:
USE IEEE.XXXX.YYYY
Where XXXX is the name of the required package, and YYYY is the name of the
specific feature that is to be used. Rather than listing each specific feature that we
10
want to use (which can be very tedious), often we will simply make all features within
a package visible by using the VHDL keyword ALL:
USE IEEE.XXXX.ALL
This opens up all features in the XXXX package of the IEEE library so that they can
be used by our design.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY nandgate IS
PORT ( a, b: IN STD_LOGIC; c: OUT STD_LOGIC );
END ENTITY nandgate;
13.7 Summary
We’ve looked at the basic features of VHDL, and seen some simple examples. The
two key parts of a description are the ENTITY, i.e. a list of inputs and outputs, and an
ARCHITECTURE, i.e. a description of the logical relationship between the inputs and
outputs. We’ve also looked at the STD_LOGIC data type, which represents a wire
carrying values of 1 and 0. The definition of the STD_LOGIC data type is held in the
library IEEE and must be imported at the start of each entity in our code.
2 1164 is simply the number of the IEEE standards document that defined the Standard Logic type.
11
14 Handling signals that are more than 1 bit wide
Most interesting design have inputs that are more than just a single bit. For example,
lets consider a device that has two 4-bit inputs a and b, and a 4-bit output c.
b c0
0
a
0
4 b1 c1
a 4
a
1
b3 c3
a
3
14.1 STD_LOGIC_VECTORs
In VHDL, quantities such as a, b and c are called STD_LOGIC_VECTORs. If you are
familiar with arrays in computer programming, you can think of a
STD_LOGIC_VECTOR as being an array of STD_LOGIC signals. So the input a
would be declared as being
STD_LOGIC_VECTOR(0 TO 3)
Now a contains four members a(0), a(1), a(2) and a(3). Each of these four members is
of type STD_LOGIC.
14.2 An example
Imagine that we wanted to represent a device like this:
b c0
0
a
0
b1 c1
a
1
b c2
2
a
2
b3 c3
a
3
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY orgate IS
PORT ( a, b: IN STD_LOGIC_VECTOR(0 TO 3);
c: OUT STD_LOGIC_VECTOR(0 TO 3));
END ENTITY orgate;
There are several ways that we could write the architecture. One way to describe it
would be like this, explicitly listing what happens for each bits:
12
ARCHITECTURE number1 OF orgate IS
BEGIN
c(0) <= a(0) OR b(0);
c(1) <= a(1) OR b(1);
c(2) <= a(2) OR b(2);
c(3) <= a(3) OR b(3);
END ARCHITECTURE number1;
Alternatively, we could just write this, which would be simpler and would mean
exactly the same thing
VHDL knows that a, b and c are four bits wide, and will do the appropriate operation
for each of the bit positions.
a <= '1';
a <= "1110";
By default, VHDL expects the values to be binary, but sometimes it can be useful to
use Hex numbers. This can be done by placing the letter X before the
STD_LOGIC_VECTOR value:
a <= X"E";
a: STD_LOGIC_VECTOR(0 TO 3);
a <= “1110”;
Element 0
Element 1
Element 2
Element 3
13
This feels normal and intuitive (indeed in the programming languages that you may
know, e.g. C or Java, this is the only way that you are allowed to do it). However, in
VHDL you also have the option to have arrays where the index counts downwards:
a <= “1110”;
Element 3
Element 2
Element 1
Element 0
In both cases, the number would be interpreted as 14 signed or –2 unsigned: the left-
most bit is always interpreted as the msb and the right most is always interpreted as
the lsb.
In digital logic design, the normal numbering convention is that bit 0 is the least
significant bit (lsb). This is accomplished by having the index run downwards. So
unlike most programming languages, in VHDL it is normal for arrays to be numbered
downwards. You can use upward-numbering if you want, but this often leads to
confusion that creates awkward bugs in your code.
14.3.2 Aggregates
Aggregates are a group values, separated by commas, that will be used for an array.
Here is an example:
ARCHITECTURE example OF aggregate IS
SIGNAL nibble1, nibble2: STD_LOGIC_VECTOR ( 0 TO 3 );
BEGIN
nibble1 <= ( '0','1','0','0');
nibble2 <= ( '0','0','1','0');
END ARCHITECTURE example;
The assignment for nibble1 sets its 0th value to ‘0’, its 1st value to ‘1’, the 2nd to ‘0’
and so on. This way of doing things is called positional assignment: the 0th value
listed goes in the 0th position, the first goes in the first position and so on. We could
instead use named association. So these statements have the same effect:
nibble1 <= ( '0','1','0','0');
nibble1 <= ( 1 => '1', 0 => '0', 3 => '0', 2 => '0');
With named association, we can just specify the values of some of the bit positions,
and use an OTHERS value to provide a value for everything not explicitly mentioned.
So these are all the same:
The OTHERS notation can be used as a convenient trick when we want to set all the
values of an array to a particular value:
nibble1 <= ( OTHERS => '1');
14
This would set all of the elements of nibble1 to '1'.
14.3.3 Concatenation
Concatenation merges two vectors to produce a longer vector. For example
ARCHITECTURE example OF aggregate IS
SIGNAL byte: STD_LOGIC_VECTOR ( 0 TO 7 );
SIGNAL nibble1, nibble2: STD_LOGIC_VECTOR ( 0 TO 3 );
BEGIN
nibble1 <= ( '0','1','0','0');
nibble2 <= ( '0','0','1','0');
byte <= nibble1 & nibble2;
END ARCHITECTURE example;
14.3.4 Literals
STD_LOGIC is a data type that has values '0', '1', ‘X’, ‘U’ etc. It is sub-type of
CHARACTER. An array of characters is a string, and is denoted by double quotes.
This is similar to the convention used in the C programming language: '1' is a
character; "1010" is an array of 4 characters. We can use this notation for
STD_LOGIC_VECTORS. So for example,
nibble1 <= ( '0','1','0','0');
could be written as
nibble1 <= "0100";
A value that is directly specified (as opposed to being calculated from other signals),
like “0100” in the code above is called a literal. Standard logic vector literals may be
specified in binary, octal or hexadecimal. By default, a string is interpreted as binary.
To make it explicit that we wish the string to be interpreted as a binary number, we
can place the letter B in front. For an octal string, we place the letter O in front, and
for hexadecimal, we place X in front. So if a is 12-bit std_logic_vector, then these are
all equivalent:
a <= "010011001010";
a <= B"010011001010";
a <= O"2312";
a <= X"4CA”
Long strings of ‘1’s and ‘0’s can be confusing, so in order to improve legibility, we
can introduce underscores:
a <= B"0100_1100_1010";
The underscores are ignored by VHDL; their only function is to space the digits out to
make it easier for a human to read. Note that if you do use underscores in your values,
you must put the B in front to make it clear that this should be interpreted as a binary
value. This (without the B) would be an error:
a <= "0100_1100_1010"; -- Wrong!
15
14.4 Summary
We’ve looked at the STD_LOGIC_VECTOR datatype, which represents multi-bit signals.
It is effectively an array of STD_LOGIC values. We have also seen the two main
notations for assigning values to STD_LOGIC_VECTORs and how to indicate the number
base for a STD_LOGIC_VECTOR value.
16
15 Number Representation and Arithmetic
Most forms of data that are handled by digital systems (e.g. samples of audio data,
pixel values for image and video data, ASCII data for representing text) are some
form of number. In this section we will look at the background to two of the most
important VHDL numerical data types (SIGNED and UNSIGNED). Before we do that, we
will briefly recap binary data representation formats for numbers.
15.1 Denary
In everyday life, we use the denary (base 10) number system whose digits can take the
values 0,1,2,3,4,5,6,7,8,9. An n-digit denary number with digits di is interpreted as
having the value
d 10
i 0 , n 1
i
i
So, for example, the number 365 has three digits: d2=3, d1=6 and d0=5. Its value is
d 2
i 0 , n 1
i
i
So, for example, the denary number 5 equates to the 3-bit binary number 101, which
has digits d2=1, d1=0 and d0=1. Its value is
1 4 + 0 2 + 1 1, which is
1 22 + 0 21 + 5 20
The bit with the highest weighting is called the most significant bit (msb) and the bit
with the lowest weighting is the least significant bit (lsb). The msb is always the
leftmost bit and the lsb is the rightmost. For this 3-bit example, bit number 2 is the
msb and bit number 0 is the lsb.
The largest number that we can represent depends on the number of bits that we use.
For example, if we use 4 bits to represent a number, then there are 16 different values
that can be represented:
3 Remember that anything raised to the power of zero is 1, i.e. x 0 1 for all x
17
Number Unsigned binary representation
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
Similarly, if we have a 4-bit binary up-counter and it reads 1111, then the next state in
its count sequence will be 0000. This provides us with an alternative method for the
representation of negative numbers. –1 is the number that is 1 less than zero. In other
words, it is a number which when added to 1 gives zero. But we have just seen that
1111 when added to 1 gives 0000. Similarly, -2 is the number that gives zero when we
add 2 to it. This is 1110. The number system that this generates is shown below
18
5 0101
6 0110
7 0111
d n 1 2 n 1 d 2
i 0 ,n 2
i
i
This is exactly the same as an unsigned binary number, except that the msb is
negatively weighted. So, for example, the interpretation of the number 1010 is
Note that all negative numbers have an msb of 1 and all positive numbers have an
msb of 0. The msb of a 2s complement is therefore often referred to as the sign bit.
But this is correct without any modification. An adder for unsigned binary works
without modification for 2s complement. This is why 2s complement is the normal
representation format for integer and fixed point numbers in digital systems4.
4In fact there is one small difference between an unsigned binary adder and a 2s complement adder,
and this is related to the treatment of overflow conditions.
19
15.5 Arithmetic on STD_LOGIC_VECTORs
Now let’s look at a simple example to illustrate arithmetic on STD_LOGIC_VECTORs, a
comparator:
4
a
4 g
b
It has two inputs, a and b, both of which represent four-bit binary numbers. There is a
single one-bit output g, which represents the “greater than” condition. When a>b then
g=’1’; otherwise g=’0’.
This can’t be interpreted unless we know whether a and b are signed (2’s
complement) or unsigned numbers. Suppose a=1111 and b=0001. If the numbers are
unsigned then a is fifteen and b is one. So a>b and g=1. But if the numbers are signed
then a is minus one and b is plus one; a<b and g=’0’.
The way that VHDL handles this is by having two different versions of the arithmetic
operators +,-,>,< etc. one for unsigned numbers and one for signed. The signed
package is called STD_LOGIC_SIGNED. The unsigned is STD_LOGIC_UNSIGNED. We
have to import one or the other library to tell VHDL how we want the standard logic
vectors to be interpreted. So if we want the signed version it looks like this
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_signed.ALL;
ENTITY comp IS
PORT ( a, b: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
g: OUT STD_LOGIC);
END ENTITY comp;
USE ieee.std_logic_unsigned.ALL;
A more powerful package is STD_LOGIC_ARITH, which introduces two new data types,
SIGNED and UNSIGNED. These are declared and used in the same way that
STD_LOGIC_VECTORs would be. So for the following example
20
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_arith.ALL;
ENTITY comp2 IS
PORT ( a: IN SIGNED(3 DOWNTO 0);
b: IN UNSIGNED(3 DOWNTO 0);
g: OUT STD_LOGIC);
END ENTITY comp2;
21
16 Dataflow and Structural VHDL
There are two fundamentally different ways that we can describe a design:
Behavioural descriptions tell us what the design should do but not how we would
make it
Structural descriptions tell us how we would make it but not what it would do
In this unit, we will look how a behavioural design (a 4-bit adder) might be
transformed by a synthesis tool into a structural netlist of logic gates.
As we do this, we will learn more about how to write behavioural and structural
VHDL. There are several different approaches to writing behavioural VHDL. The
easiest to understand is dataflow, a type of description where we build up the required
behaviour as a set of arithmetic and logical operations on data items.
carry in
x
0 + sum
y 0
0
x
1 + sum
y 1
1
x
2 + sum
y 2
2
x
3 +
y sum
3 3
carry out
A structural description tells us how we would connect together several simpler units
to make a more complicated unit. In this case our simple units are full adders, and the
complicated unit is the 4-bit adder.
If we are going to build or circuits out of basic logic gates, then the above description
isn't quite finished. Although we have broken down our complicated (4-bit) unit into
simpler (1-bit) units, we still haven't shown how to make the full adders out of gates.
By contrast, in the description below everything is resolved into the most basic
building blocks we have (in this case logic gates).
22
This design is a special case. It is a netlist, a description that consists solely of the
interconnection of basic building blocks that are available to implement the design. A
netlist contains sufficient detail that it is immediately obvious how to build the device.
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_signed.ALL;
ENTITY adder IS
PORT ( x, y: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
sum: OUT STD_LOGIC_VECTOR(3 DOWNTO 0) );
END ENTITY adder;
The above code has all the advantages of a behavioural description. The code is
concise; you can easily see at a glance what function it performs; it contains no
detailed decisions about logic gates. This code would then be given to a synthesis
tool, a computer program whose purpose is to design a circuit that fulfils the required
behaviour.
23
16.2.1 Implementing the adder function
Here is a circuit that the synthesis tool might create in order to fulfil our 4-bit adder
requirement:
carry in
x
0 + sum
y 0
0
x
1 + sum
y 1
1
x
2 + sum
y 2
2
x
3 +
y sum
3 3
carry out
The four-bit adder is built up from four 1-bit full adders, which have the following
behaviour:
There are many ways to implement this. One possible way is shown below.
x
sum
y
carry in
carry out
In the remainder of this lecture, we will look in detail at the full adder circuit, and use
it to illustrate the features of dataflow VHDL. In the next lecture we will look at how
to connect together the full adders and create a structural 4-bit adder. We will also
look at how to feed the 4-bit adder circuit into a simulator.
24
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY fulladd IS
PORT ( x, y, cin: IN STD_LOGIC;
sum, cout: OUT STD_LOGIC);
END ENTITY fulladd;
This is easy and obvious, but also tedious. There are many neater ways to describe the
behaviour. We could take inspiration from the gate level design of the full adder, and
write this
This is much neater and nicer, but requires us to think a bit harder about how the
outputs relate to the inputs.
It’s important to realise that as far as a synthesis tool is concerned, both descriptions
are the same thing. They simply say how the outputs relate to the inputs. The second
architecture is not ordering the synthesis tool to use two XOR gates, 3 AND gates and
an OR gate. It’s simply a shorthand for saying how the output relates to the inputs.
The synthesis tool is free to do whatever it wants to find a circuit that has the same
input-output relation.
25
16.3.1 Local signals
Now let’s look at a slight modification of our description.
n1
x
sum
y
n2
cin
cout
n3
n4
We have given names to the internal nodes of the circuit (n1, n2, n3, n4). Once we
have given them names, we are free to use them in our description. So here is a
slightly different description
This is basically the same as the simple architecture of fulladd, but this time we have
used the local signals n1, n2, n3 and n4 as part of the description. In order to use the
names, we have to declare that they exist, that they are signals, and that they carry
logic values (e.g. ‘1’, ‘0’, ‘X’ and ‘U’) which means that they are of type
STD_LOGIC. The declaration of local signals takes place between the
ARCHITECTURE statement and the first BEGIN.
work.fulladd(dataflow)
The name is constructed from the library name followed by a point, then the entity
name, then the architecture name.
26
16.4.1 Placing library components into a design
We can now build up a 4-bit adder structurally as follows:
cin
x
0 + sum
y 0
0 carry
x 1
1 + sum
y 1
1 carry
2
x
2 + sum
y 2
2 carry
3
x
3 +
y sum
3 3
cout
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY adder IS
PORT ( x, y: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
cin: IN STD_LOGIC;
sum: OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
cout: OUT STD_LOGIC);
END ENTITY adder;
ARCHITECTURE structural OF adder IS
SIGNAL carry: STD_LOGIC_VECTOR(4 DOWNTO 0);
BEGIN
g0: ENTITY work.fulladd(dataflow)
PORT MAP (x(0),y(0),cin,sum(0),carry(1));
g1: ENTITY work.fulladd(dataflow)
PORT MAP (x(1),y(1),carry(1),sum(1),carry(2));
g2: ENTITY work.fulladd(dataflow)
PORT MAP (x(2),y(2),carry(2),sum(2),carry(3));
g3: ENTITY work.fulladd(dataflow)
PORT MAP (x(3),y(3),carry(3),sum(3),cout);
END ARCHITECTURE structural;
The names appearing in the port maps are the names of the wires. Wherever the same
name occurs in the output list of one component and in the input list of another, that
5 Strictly speaking, g1 is a statement label, but you can think of it as just providing a name for the gate.
27
means that there is a wire connecting these two components. So, for example,
carry(2) is a wire connecting the output of c1 to the input of c2.
ENTITY fulladd IS
PORT ( x, y, cin: IN STD_LOGIC; sum, cout: OUT STD_LOGIC);
END ENTITY fulladd;
We see that the first three signals in the port map are inputs and the last two are
outputs. So the first three signals in the instantiation x(0), y(0) and cin will be attached
to the inputs x, y and cin. Similarly, sum(0) will be connected to sum and carry(1) will
be connected to cout. This is called positional association.
This is called named association. With named association, the order doesn’t matter,
so you could jumble up the order of the signals and write the instantiation like this
16.5 Summary
A behavioural description says what a design should do. Dataflow is a type of
behavioural description that relates the outputs to inputs using logical or arithmetic
assignments.
A structural description says how we construct a design from the composition of
simpler units. A structural description can be
Hierarchic: made up from simpler units, which themselves then need to be
designed
Netlist: made up from fundamental building blocks (e.g. logic gates)
28
17 VHDL Simulation
A simulator is a software tool that takes a proposed design (which could be
behavioural or structural or a mixture of both) and predicts what outputs would result
from a given set of input transitions. Simulation one of the main methods of verifying
that a proposed design has the required behaviour.
Let’s look again at the full adder circuit, and for the sake of clarity, we will now give
names (g1…g6) to the gates.
g1 n1
x
g2 sum
y
n2
g3
cin
g4 g6 cout
n3
g5
n4
Imagine that the signals x, y and cin are initially at zero. Looking through the circuit,
we can see that n1, n2, n3, n4, sum and cout will all be at zero.
Now imagine that x changes its value from 0 to 1. Let’s think through what happens
next:
x is the input to three gates: g1, g3 and g4. These gates are potentially affected by
the change, so we need to re-compute their outputs n1, n2, n3.
We also know that gates g2, g5 and g6, which don’t have x as an input, can’t be
affected by this change, so there is no point to re-computing their outputs.
The new value of n1 is 1 (i.e. it changed)
The new value of n2 is 0 (i.e. it is unchanged)
The new value of n3 is 0 (i.e. it is unchanged)
n1 just changed, which means that any gate that has n1 as an input (i.e. g2) needs
to have its output (sum) re-computed.
n2 and n3 didn’t change, so we don’t need to bother to examine any consequences
in gate g6, which has n2 and n3 as inputs.
The new value of sum is 1.
There are no more gates whose inputs have changed, so we can stop analysing the
circuit now.
29
ARCHITECTURE number3 OF fulladd IS
SIGNAL n1, n2, n3, n4: STD_LOGIC;
BEGIN
1 n1 <= x XOR y;
2 sum <= cin XOR n1;
3 n2 <= x AND y;
4 n3 <= cin AND x;
5 n4 <= y AND cin;
6 cout <= n2 OR n3 OR n4;
END ARCHITECTURE number3;
All statements 1-6 are scanned simultaneously, waiting for a signal on the right hand
side (RHS) to change. In the jargon of VHDL, a change to a signal is called an event.
A VHDL simulation proceeds by manipulating an event queue. If we assume that all
signals are initially at 0, then the event queue initially looks like this:
Time = 0
Signal Name: x y cin n1 n2 n3 n4 sum cout
Present value: 0 0 0 0 0 0 0 0 0
Next value: 1
Event time: 10
It has a list of the present value for each signal, any new value that has been scheduled
to take place in future, and the time at which the signal must assume this new value.
In this case then next event is that x will transition from ‘0’ to ‘1’ at time 10 ns.
Once the event queue is set up, the simulator proceeds by looking down the event
queue to find the time of the next pending event. It then jumps forward to the time of
the next event (10 ns), giving x its new value. An event has just occurred on signal x.
This triggers execution of the all of the statements that have x on the RHS:
If the new value is different from the old value, it is placed on the event queue. The
queue now looks like this.
Time = 10
Signal Name: x y Cin n1 n2 n3 n4 sum cout
Present value: 1 0 0 0 0 0 0 0 0
Next value: 1
Event time: 10+
The simulator now looks down the event queue to find the next scheduled event.
There is only one item on the queue, the 0 to 1 transition on n1 at time 10+. The time
pointer is incremented to 10+and n1 takes its new value. Because an event has
occurred on n1, any statement with n1 on the RHS is triggered:
30
2 sum <= cin XOR n1; Gives new value of 1
If the new value is different from the old value, it is placed on the event queue. The
queue now looks like this.
Time = 10+
Signal Name: x y Cin n1 n2 n3 n4 sum cout
Present value: 1 0 0 1 0 0 0 0 0
Next value: 1
Event time: 10+
The simulator now looks down the event queue to find the time of the next scheduled
event, i.e. 10+2. The time pointer is incremented to 10+2and sum takes its new
value.
Time = 10+2
Signal Name: x y Cin n1 n2 n3 n4 sum cout
Present value: 1 0 0 1 0 0 0 1 0
Next value:
Event time:
There are no statements with sum on the RHS, so no further statements are triggered.
The simulator now looks down the queue to find the next scheduled event. There are
none, so simulation terminates.
Although they are written in a different order, they do exactly the same thing. Unlike
programming languages such as C, which process lines in the order that they are
written, VHDL normally monitors all statements at the same time, and executes a
31
statement when one of its RHS values changes. This is called concurrent execution.
The style of VHDL that uses concurrent assignments through arithmetic or Boolean
operators is called dataflow VHDL.
Now, when a or b change, the value of c will be re-computed, but c will not get its
new value until 10 ns after the change in the input. VHDL knows about the following
units of time:
Imagine that the signals x, y and cin are initially at zero, so n1, n2, n3, n4, sum and
cout are also initially at zero. At time 10 ns x will go to one. The event queue initially
looks like this:
32
Time = 0
Signal Name: x y cin n1 n2 N3 n4 sum cout
Present value: 0 0 0 0 0 0 0 0 0
Next value: 1
Event time: 10
Once the event queue is set up, the simulator looks down the event queue to find the
time of the next scheduled event. It then jumps forward to the time of the next event
(10 ns), giving x its new value. An event has just occurred on signal x. This triggers
execution of the all of the statements that have x on the RHS:
If the new value is different from the old value, it is placed on the event queue. The
queue now looks like this.
Time = 10
Signal Name: X y Cin n1 n2 n3 n4 sum cout
Present value: 1 0 0 0 0 0 0 0 0
Next value: 1
Event time: 20
The simulator now looks down the event queue to find the next scheduled event.
There is only one item on the queue, the 0 to 1 transition on n1 at time 20. The time is
incremented to 20and n1 takes its new value. Because an event has occurred on n1,
any statement with n1 on the RHS is triggered:
If the new value is different from the old value, it is placed on the event queue. The
queue now looks like this.
Time = 20
Signal Name: x y Cin n1 n2 n3 n4 sum cout
Present value: 1 0 0 1 0 0 0 0 0
Next value: 1
Event time: 30
The simulator now looks down the event queue to find the time of the next scheduled
event, i.e. 30. The time pointer is incremented to 30and sum takes its new value.
Time = 30
Signal Name: x y Cin n1 n2 n3 n4 sum cout
Present value: 1 0 0 1 0 0 0 1 0
Next value:
Event time:
There are no statements with sum on the RHS, so no further statements are triggered.
The queue is now empty, so simulation terminates.
33
17.5 Simulation of structural VHDL
For the sake of clarity, let’s look again at the structural description, and highlight
which of the signals are inputs:
cin
x
0 + sum
y 0
0 carry
x 1
1 + sum
y 1
1 carry
2
x
2 + sum
y 2
2 carry
3
x
3 +
y sum
3 3
cout
17.6 Summary
Simulation advances through time updating signal values according to assignments.
Dataflow VHDL consists of a series of Boolean/arithmetic assignment statements.
These statements are concurrent: all are active at the same time; a statement is
triggered to re-evaluate its left hand side value when any a right-hand side value
changes. Structural VHDL consists of instantiations of library elements, which
operate concurrently. When an input to an instance changes, the new outputs are
evaluated.
34
Index
13 Introduction to VHDL 6
13.1 Entity and Architecture 6
13.2 Specifying another architecture 7
13.3 BEGIN and END statements 7
13.4 Semicolons 8
13.5 Stylistic issues 9
13.5.1 Case 9
13.5.2 Spaces and indents 9
13.5.3 Returns 9
13.5.4 Annotating END statements 10
13.5.5 Comments 10
13.6 The IEEE library 10
13.6.1 Opening libraries 10
13.6.2 Using STD_LOGIC 11
2.7 Summary 11
35
16.4.1 Placing library components into a design 27
16.4.2 Positional association 28
16.4.3 Named association 28
116.5 Summary 28
17 VHDL Simulation 29
17.1 How are statements processed? 29
17.2 Simulation of dataflow VHDL 29
17.3 Concurrent processing 31
17.4 Components with delays 32
17.4.1 The AFTER keyword 32
17.4.2 The full adder example with component delays 32
17.5 Simulation of structural VHDL 34
17.6 Summary 34
36