You are on page 1of 12

VHDL tutorial: Simulation and Synthesis

1 Simulation with ModelSim ................................................................................................ 2


1.1 Analyse/Compile ........................................................................................................ 3
1.1.1 Simulate.............................................................................................................. 4
1.1.2 Script file with the stimuli.................................................................................. 4
1.1.3 Stimuli generation with VHDL .......................................................................... 5
1.1.3.1 Connect the test set with the design under test............................................... 7
1.2 Simulation model ....................................................................................................... 9
2 Synthesis with LeonardoSpectrum................................................................................... 10
2.1 Synthesis in action.................................................................................................... 10
2.1.1 Synthesize count............................................................................................... 11
3 Alternative descriptions.................................................................................................... 12

1
1 Simulation with ModelSim

figure 1

ModelSim is a handy VHDL/Verilog simulator. In this document you can find a short
introduction how to use ModelSim.
ModelSim starts with the window shown in figure 1. The left windows shows the libraries and
the right window is used for entering commands and for reporting information to the user.
An analysed VHDL file is stored in a library. Library work is used to store your analysed
VHDL designs.
The first step is to create a library work:
FileΔChange directory” and browse to the directory that contains your design files. Next
you enter the command:
vlib work <return>
Note: Due to a bug in the software the library “work” is not always shown in workspace
after this command is entered. Workaround: quit ModelSim, start ModelSim and
browse to the directory.
The library is created but is still empty. A correctly analysed design unit (entity, architecture,
package, package body or configuration) is placed in library work.
As an example a circuit that counts the number of ones in the input pattern is used in this
tutorial (N.B. the line numbers are not part of VHDL).
1. LIBRARY ieee;
2. USE ieee.std_logic_1164.ALL;
3. ENTITY count IS
4. GENERIC (w : positive := 8);
5. PORT (a : IN std_logic_vector(w-1 DOWNTO 0);
6. q : OUT integer RANGE 0 TO w);
7. END count;
8.
9. ARCHITECTURE behaviour OF count IS
10. FUNCTION cnt (a:std_logic_vector) RETURN integer IS
11. VARIABLE nmb : INTEGER RANGE 0 TO a'LENGTH;
12. BEGIN
13. nmb := 0;
14. FOR i IN a'RANGE LOOP
15. IF a(i)='1' THEN nmb:=nmb+1; END IF;
16. END LOOP;
17. RETURN nmb;
18. END cnt;
19. BEGIN
20. q <= cnt(a);
21. END behaviour;
Figure 2: behavioural description of count

2
The generic w, on line 4, is a global constant with value 8.
The input a of this design is w bits wide. The output q is an integer value. The width of the
input is w therefore the number of ones must be between 0 and w (inclusive). A range
constraint is added to the integer type. The range constraint is not necessary but it can be used
for documentation and will help synthesis.

There are many ways to count the number of ones in an array. In the architecture (figure 2) a
function is declared that takes care of this. This function has as input of type std_logic_vector.
A std_logic_vector is an unconstrained array; the length of this type is not (yet) known! The
reason to use an unconstrained array as input is to make the design generic with respect to the
width of the input. At the location of the function call, line 20, it is clear what the range of the
input is.
The algorithm used in the function is straightforward. With a loop all elements of the inputs
are examined. The only problem is: how do I know what the vector indices are? The attribute
‘range is used for this. If the function is called with an object that is declared as
std_logic_vector(5 to 36) then within the function the attribute ‘range is replaced with “5 to
36”.

1.1 Analyse/Compile
Place (a copy) of the file count_loop.vhd in the design directory. Via the menu compile you
can compile this description. Compile the design via compileÎcompile.

Figure 3: the result after compilation

If there are no errors then your ModelSim environment should look like shown in figure 3.
In library work the entity and architecture of the design is located. In case of an error you can
double click the error message and an editor is opened with your design on the line where the
error was found (the error is often just before this line).

3
1.1.1 Simulate
Click with the right mouse button on the architecture name ‘behaviour’ and you can load your
design in the simulator (or you can use menu button “simulate”).
During simulation you probably like to see some waveforms therefore enter:
add wave * <return>
(In stead of * you may enter a list with the signal names separated with a comma).

With the run command you perform a simulation with length 100 ns (default) or you can
explicitly add a time length:
run 200ns <enter>
The inputs are all ‘U’ why?

With the force command you can apply an input pattern to a:


force a 01100011 <enter>
run <enter>
Try it with some other values for a.

You can assign multiple values to the input with:


force a 11111111, 00111111 10ns, 11110101 20ns
Try it.

1.1.2 Script file with the stimuli


A tool dependent solution to apply stimuli is to use a script file. A simple example is given
beneath:
e.g. file demo.do
force a 00011111
run 100ns
force a 10100000
run 100ns

In ModelSim these commands are executed with the command:


do demo.do <return>

Note 1: In a synchronous design a clock signal is needed. Assume signal clk is the clock line.
A repetitive pattern is generated with the command:
force clk 0, 1 50 ns –repeat 100ns

Note 2: The ModelSim command “run –all” performs a simulation and will stop simulation
when nothing ‘happens’ anymore. Do not use this command when a clock signal is
generated with the method of Note 1.

4
1.1.3 Stimuli generation with VHDL
Applying stimuli as presented in the previous section is tool dependent. You can also use
VHDL to generate stimuli. Finding test data for a design is not an easy task. In this chapter we
only want to illustrate that stimuli can be generated.
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY testset IS
GENERIC (w : positive := 8);
PORT (data : OUT std_logic_vector(w-1 DOWNTO 0));
END testset;

ARCHITECTURE set1 OF testset IS


BEGIN
PROCESS
BEGIN
data <= (others => '0'); -- all zero
WAIT FOR 10 ns;

data <= (others => '1'); -- all one


WAIT FOR 10 ns;

FOR i IN 0 to 2**w-1 LOOP


data <= std_logic_vector(to_unsigned(i,w));
EXIT WHEN i=20; -- if w is large not an exhaustive test is performed
WAIT FOR 10 ns;
END LOOP;
WAIT; -- forever
END PROCESS;
END set1;
Figure 4: simple test set.

Figure 4 gives a simple test set. It contains one process statement. It first generates all zeros,
waits for 10 ns, then generates all ones and it waits again,… Of course an exhaustive test is
possible. In the for-statement the loop variable i (which is implicitly declared!) goes from 0 to
2w-1. This integer value should then be converted to a bit pattern (using a binary coding; also
called unsigned). For this the function to_unsigned is used. This function converts the integer
value i to a binary vector with length w. This function is located in a package numeric_std (in
library ieee).

Background information: numeric_std


The package numeric_std declares two types:
- signed (twos complement representation) and
- unsigned (binary representation).
Both types are similar to type std_logic_vector; arrays with element type is std_logic

variable sa,sb,sc : signed(2 downto 0);


variable ua,ub,uc : unsigned(2 downto 0);
variable a,b,c : std_logic_vector(2 downto 0);

If sa is “111” then it is interpreted as decimal -1 (twos complement).


If us is “111” then it is interpreted as decimal 7.
Is a is “111” then no decimal value is associated with it!

What is the result of the statement: sa := sb + “11” ?


The operands do not have the same length. Since sb is a signed the shortest vector is sign
extended before the addition takes places.
In case the operands are of type unsigned the shortest vector is extended with zeros.

5
In case the operands are of type std_logic_vector you cannot perform an addition because no
decimal interpretation is associated with this type. (There are packages that can handle this,
but these packages are not IEEE standard and some of can be frustrating in use.)

VHDL is a strongly typed language therefore you can not write:


a := sa;
However the types are similar (“closely related”). In that case a type conversion function can
be used:
a := std_logic_vector(sa);

If you want the integer value of a vector you simply write:


integer_value := to_integer(sa);
If you want to convert an integer to a vector you must add the length of the vector:
sa := to_signed(integer_value,3) or
us := to_unsigned(integer_value,3)

However in case the generic (~ constant) w is large this is a time consuming task. Therefore in
this example the loop is ended in case i is equal to 20. The process ends with wait. This means
the process will not resume execution.

If the simulator is still active end the current simulation via the simulation menu.

Compile the file testset.vhd and simulate the design entity.


Figure 5 shows the simulation result. The test pattern generation will end at the wait
statement. If you enter
run –all <enter>
the simulator will simulate until no signal changes are planned for the future. Be careful with
this command. If you use the following concurrent statement to generate a clock
clk <= not clk after 10 ns;
it will never end ..

Figure 5: simulation result of the test set

6
1.1.3.1 Connect the test set with the design under test
Figure 6 shows the structural VHDL description that connects the test set with count. Compile
file testbench.vhd and simulate entity testbench.
Check that the length of the pattern is changed to 10 in the design!
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY testbench IS
GENERIC (width : positive := 10);
END testbench;

ARCHITECTURE structure OF testbench IS


COMPONENT testset
GENERIC (w : positive := 8);
PORT (data : OUT std_logic_vector(w-1 DOWNTO 0));
END COMPONENT;
COMPONENT count
GENERIC (w : positive := 8);
PORT (a : IN std_logic_vector(w-1 DOWNTO 0);
q : OUT integer RANGE 0 TO w);
END COMPONENT;

-- local connections
SIGNAL stimuli : std_logic_vector(width-1 DOWNTO 0);
SIGNAL output : integer;
BEGIN
ts : testset
GENERIC MAP (w => width)
PORT MAP ( data => stimuli);
dut : count
GENERIC MAP (w => width)
PORT MAP ( a => stimuli,
q => output);
END structure;
Figure 6: test bench

Figure 7:The design hierarchy is shown in the left window

7
Via the menu SimulateÎRunÎ------- A new window is opened:

With this window you can step through your design (maybe to locate errors). The ‘step’
button is often used. Then only one statement (concurrent or sequential) is executed. Also a
source window is opened so you can see (the arrow) what the next statement to execute will
be. “Step -Over” is similar to the execution of a function/procedure in one step.
Often during debugging you like to run your program to a certain point and perform a low
level debug from that point. Double click on the left of the line number of an executable line
and a breakpoint appears.

Via the menu ‘View’ it is possible to open other windows:


- Signals: not everywhere in the design all signals are visible. This window shows all
visible signals with the current values. You can also drag and drop a signal in the wave
window (figure 8).
- Variables: variables within a process, function and procedure are also shown. During
debugging of a sequential part this can be handy. Also a variable can be placed in the
wave window using drag and drop.
- Structure: shows the hierarchical structure of the design loaded. If you click in this
structure you will notice that the source window will show the corresponding source file.
This can be used to set breakpoints before simulation is started, add signals/variables to
the wave windows that are visible at the selected location.

Figure 8:Simulation result

8
1.2 Simulation model
VHDL has concurrent statements. In a VHDL model there is an order in which the statements
are written however the simulation is order independent!
Processes can only communicate with each other using signals (I forget here the ‘shared
variable’; don’t use ‘shared variables’). If you assign a value to a signal that signal value is
not updated immediately. This means that all processes will use the same signal value;
consequently the simulation is order independent. If you assign a value to a variable that
variable is updated immediately.
Sometimes you are surprised by the update mechanism of signals.
If you write:
y <= a after 10 ns;
The output y follows the input a with a delay of 10 ns. (More precise; the input should be
stable for 10 ns too.)
y <= a;
The output is updated after a delta delay. Delta delays are not shown in the wave window.
There can be infinite delta delays before simulation time advances. You will experience this if
you don’t see any progress during simulation but your simulation is still going on (for hours
…). ModelSim will report a warning when it performs 1000 delta steps.

ModelSim also makes is possible to show the simulation results after every delta steps.

Repeat the previous simulation but (also) use the following command:
add list * <return>
Check that you really understand what is going on.

9
2 Synthesis with LeonardoSpectrum
Part of VHDL is synthesizable. There is a separate IEEE standard that describes a
synthesizable subset of VHDL (IEEE std. 1076.6-2004).
It is almost clear that a synthesis tool has problems with timing aspects and dynamic types
(access types). Also the initial values of signals, and variables in a process, are synthesizable.
Therefore almost all designs have an explicit reset input. Furthermore recursion is supported
when the recursion depth can be determined statically.
For this tutorial the synthesis details are not important:
- LeonardoSpectrum is a high level synthesis tool. It is technology (Actel, Xilinx,..)
independent. It supports a quite large subset of VHDL and will generate an
intermediate format (mostly EDIF; electronic design interchange format; also an IEEE
standard).
- The generated intermediate file is used as input by the FPGA specific synthesis tool
(Quartus, ISE,..) to map it on the specific device.
We will not focus on the second step. In this course we will only perform the high-level
synthesis step. The RTL (register transfer level) output gives us enough information about the
quality of the design (including a nice schematic). If we had installed the specific synthesis
tool the second step is rather easy; however it often is a time consuming task.

2.1 Synthesis in action


Start LeonardoSpectrum.

Figure 9: LeonardoSpectrum environment

Create working directory.


First you have to set a project (this can best be done locally on the disk to reduce network
traffic and thus reducing waiting time). The project directory does not have to be the same
directory you used with ModelSim. It is even better, in my opinion, to use another directory
due to the immense number intermediate files that are generated by the tool.

10
FileΔChange working directory” and choose a directory.

Set target device.


Although we will not really program a device we have to select a technology and a device.
LeonardoSpectrum needs this information for the generation of the EDIF file. You may
choose in the Technology box “FPGA/CPLD=>Actel=>A500K”.
(But any Technology/device can be used; sometimes a design is too large to fit in the selected
device.)

Add design files.


In the window with label “input” the VHDL source files are placed. Place the mouse in this
window and click on the right mouse button. A window will pop up and you can select your
design.
You can add multiple design files in the input window (the compile order should be correct!).
Place the “testset.vhd” in this window.
Now you can push the RUN button. Press it? Did you expect this result?
How to remove a design file? Select it with the left mouse button and press the delete key.
Don’t check the “Integrate Place and Route”. When the technology specific tooling is
installed (ISE, Quartus,..) the Synthesis tool will also perform the place and route.

2.1.1 Synthesize count


Synthesize the design count (file count_loop.vhd).
Study the report window: use of components and the delay information. Change the
technology and perform and synthesize your design again. Compare the synthesis report.
If it was a synchronous design the tool will also report a maximum clock frequency (although
the real value is determined in the second synthesis phase which we will not perform).
A schematic is shown if you press the red button (on the left of figure 10) with the schematic
symbol (“View RTL schematic”).

Figure 10: toolbar LeonardoSpectrum

Change the generic setting of w to 16 and perform again a synthesis step.


Study the RTL schematic. Would you also realize it this way? Can you improve the speed?

Note: if the size of the schematic is large the tool will automatically use more sheets.

You can enforce the tool drawing it on one sheet with:


Schematic viewer Î and not select “Multipage Schematic”

11
3 Alternative descriptions
The previous solution was a straightforward and readable description. This description can be
handed over to someone and probably (s)he will recognize the intention quite fast. A synthesis
tool supports more and more these kinds of descriptions, and sometimes finds smart
implementations. (It is expected that the tools will be better in the future with respect to this;
similar to the software compilers that are in use nowadays).

You can speed up the design by dividing the problem into two smaller problems that both take
the half of the input vectorÎrecursion!
Figure 11 shows a VHDL description with a recursive solution.

Do you understand all the details in this description? Maybe the two constant declarations in
the function are not quite clear.
CONSTANT n: natural := vec'LENGTH;
Vec’LENGTH is the length of the vector Vec (‘Length is an attribute).
CONSTANT v: std_logic_vector(1 TO n) := vec;
This looks funny. Why not use vec in stead of v? Remember the function has as input type
std_logic_vector. This is an unconstrained array. Not only the length but also the left and right
index is not known when the function is written. Only when the function is called it is known.
The function should operate properly with s1 and s2 if they are declared as:
signal s1 : std_logic_vector (4 to 20) ;
signal s2 : std_logic_vector (30 downto 3);

Synthesize this design and have a look at the RTL schematic.


LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY count IS
GENERIC (w : positive := 8);
PORT (a : IN std_logic_vector(w-1 DOWNTO 0);
q : OUT integer);
END count;

ARCHITECTURE recursive OF count IS


-- count bit with a balanced tree approach
FUNCTION count_bits(vec: std_logic_vector) RETURN integer IS
CONSTANT n: natural := vec'LENGTH;
CONSTANT v: std_logic_vector(1 TO n) := vec;
BEGIN
CASE n IS
WHEN 0 => RETURN 0;
WHEN 1 => IF v(1) = '1' THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
WHEN OTHERS => RETURN count_bits(v(1 to n/2)) -- 1
+ count_bits(v(n/2+1 to n));
END CASE;
END count_bits;

BEGIN
q <= count_bits(a);
END recursive;
Figure 11: recursion (file count_recursive_funct2.vhd)

1
The division operator, based on integer, will always give an integer result. Hence 8/3 is 2.66666 and the
integer result is 2.

12

You might also like