Post on 01-Mar-2021
EECS 470 Tutorial (and tools reference for Winter 2013)
Getting Ready
1) Log onto a CAEN machine running Linux with your login and password. (You may have to reboot a windows
machine)
2) You now want to load up an xterm so that you can issue commands from the command-line. You can do this by
left clicking on the screen. A small menu should appear with “Open Terminal” listed near the top, click on that.
You should now have an xterm on the screen ready for commands.
3) In this class it is very easy to use vast quantities of disk space. When you run out of disk space many bad things
happen. Though this tutorial will be unlikely to exhaust your disk space unless it is almost full already, please check
your disk quota via this command:
fs quota
We will be happy to help you find files to delete if you find you have a problem.
4) Now we need to retrieve the files that will be used during the tutorial. We have put these files in the class web
space. They can be retrieved via a web browser, or via the wget utility which downloads files without using a
browser. The wget command to execute is:
wget www.eecs.umich.edu/courses/eecs470/tools/470tut.tar.gz
To start a web browser, execute:
firefox&
The & causes mozilla to be run in the background so that you can have mozilla up and execute more commands in
this shell at the same time. If you ever forget to run something in the background you can hit <Ctrl Z> (when the
shell is the focus) and then execute:
bg
6) Now that you have the files in tar format you need to untar them using this command:
tar -xzvf 470tut.tar.gz
7) Now change into the 470tut directory by typing:
cd 470tut
Among others, this should create the files Makefile, tut_mod.v, tut_test.v, and tut_synth.tcl in the current directory.
Please verify that the files were created by executing:
ls
Verilog from the command line
1) To simplify dealing with the command line, we have created a Makefile for this tutorial. In this class we use the
Makefile to create shortcuts for complicated commands and to standardize these commands. You will be required to
supply a Makefile with a standard user interface that will handle whatever files you use to accomplish your projects.
The Makefile that we are providing implements these standard interfaces and most of it can be reused without
modification for each of your projects. Let's have a look at the contents of the Makefile so you're comfortable with
modifying it when the time comes. Use your favorite text editor to load the file called "Makefile" or if you're not
familiar with unix text editors, you can just issue the following command for a gui text editor with syntax
highlighting for Makefiles, verilog, and c files that is commonly used here, however it will be beneficial if you learn
a more powerful editor in the future (vim or emacs come to mind):
nedit Makefile&
2) Looking at the Makefile there are several things you'll want to pay attention to. First of all, the comments at the
top list the commands and what they do. These may not make much sense yet, but if you forget what does what,
have a glance there. Secondly, and this is important for when you modify this Makefile for later projects, notice the
lines that begin with “TESTBENCH =”, “SIMFILES =” and “SYNFILES=” these lines specify the filenames of
your verilog files.
The “SIMFILES =” line should contain the filenames of all of the synthesizable verilog files you are
working with.
The “TESTBENCH =” specifies all of the files that contribute to your testbench and are not synthesized.
The “SYNFILES=” specifies the verilog files that are a result of the synthesis and any other files needed to
compile them. These files are named <module>.vg and correspond to each module, not file.
If you scroll down further you will see the actual commands executed. Feel free to add commands if you think they
will make things easier on you.
3) Now that we've looked at the Makefile, let's try to actually compile and run the provided verilog code. There are
some problems in the code provided. The reasoning behind this is that you wouldn't need very many verilog tools if
your code always worked perfectly. Most of the tools are there to help you debug broken code or to verify that your
code works. It's a useful way to demonstrate the tools and to show some examples of what you will see when you
run into these common bugs. Let's start out now by trying to compile and run the code on the command line. This
can be done by typing the following command:
make
You should notice that the code failed to compile. Looking at the error, you should notice there is a syntax error on
line 7 of tut_mod.v. There is a typo, "regg" should be "reg". You need to load up this file in your favorite text
editor to make the change. Fix the error, save your changes, and let's go ahead and run "make" again. You should
now see a successful compile and the values printed from the testbench (tut_test.v). The Makefile also puts the
output into a file called program.out. This file may be useful to compare results, to refer to during editing of the
verilog code, or for seeing the entire output when the output is very long.
Debugging
Because verilog is a hardware description language, the normal concept of a debugger (which allows one to step
through the execution of a program from line to line) does not make sense. Instead, we use a waveform viewer
which has more in common with electronics tools. This tool allows you to graphically view how the signals change
over time
The debugger: DVE
The Makefile command for compiling your code and running this tool is the following, go ahead and run it :
make dve
The TopLevel.1 window should appear as shown in the figure
to the left. In the TopLevel.1 window various panes will be
already present. You can also open different panes yourself
using the Windows → Panes → …option.
1)Module Hierarchy Pane
The pane on the left hand side is the Module Hierarchy
window. This window is used for finding the signals and
modules you are interested in viewing in other windows.
This pane contains a hierarchical view of modules that
instantiate other modules. In this case, our testbench instantiates
one other module.If you click the "+" next to testbench, all of
the modules instantiated by testbench will become visible. In
this case, there's only one such module called tbp. If there were
more they would be listed below it.
tbp? What is tbp? There is no module named tbp in the code
we gave you. Actually, tbp is the name of the instantiation of
module two_bit_pred in testbench. This is why using
meaningful identifiers for these otherwise meaningless names
can be important.
2) Data Pane
The data pane lists the signals contained in the currently
selected module in the module hierarchy pane. You can select
the particular signals for which you want to view the waveform
in this window. Use Shift or Ctrl key to select multiple signals.
Once you have selected the signals that you want, right click on
any one of the selected signals and choose Add to Waves →
Recent. A new TopLevel.2 window will open with the
waveform viewer pane.
You can go back to the original window and add more signals
to the waveform viewer using the same method as described
above. For this tutorial, you should add all the signals present in
the testbench to the waveform viewer.
3) The waveform viewer
The waveform viewer is the primary part of the debugger. Once you have added some signals into the waveform
viewer, click on Simulator→ Start. This will run the program until the end. You must choose the signals you
wish to view before you start the simulation. That is because the only signals that will be stored during the
execution are those signals that are in the waveform viewer. If during your debugging you discover that you need
some other signals, add them to the waveform viewer (they will show up as gray which indicates that the values
were not logged), and choose Simulator→ Terminate and then Simulator→ Start.
As said before, the waveform window is supposed to help facilitate your debugging by allowing you to see how
signals are changing in relation to each other as time progresses. Underneath the actual waveforms is a scrollbar
that will allow you to move to different time values. Go ahead and scroll right until you get to the end. This is
where the simulation ended. This is not a very large simulation, so it might make sense to want to zoom out so you
can see more of what's going on in a single window. On the toolbar at the top of the waveform window, you will
find different zooming options. Alternatively, you can find zooming options in View→ Zoom. Practice zooming in
and out for a few seconds. Try zooming out so the whole simulation is one 1 screen.
There are many different types of things you can see appear in the waveform window as a signal. The most obvious
and probably most common would be a normal 1 bit signal, which is represented by lines and edges moving up and
down as time progresses (where up is a value of 1 and down is a value of 0), "transition" is this kind of signal.
Another possibility is that the value of the signal is unknown, that its value could not be determined, and is usually
the sign of a bug in your program. An example of this is "prediction" from time 25 on. These can be seen quite
clearly by the amount of red that it shows and the value is represented as "x". Seeing a lot of these can be a good
indication of a problem (though sometimes it is not a problem). Another possibility is a high Z value. These are
shown as a gold line and indicate that nothing is driving the signal, that nothing ever sets its value. "taekn" is an
example of this. These could occur as a result of a typo or forgetting to instantiate a module properly. The last kind
of signal is one that consists of more than 1 bit. In this case, "bigsignal" is 16 bits wide. Its value is not shown with
rising and falling lines, but with separations between textual values.
For signals greater than 1 bit, the value is displayed in the wave. Sometimes it's convenient to have this value
displayed as hex, binary, 2s complement decimal, whatever. Notice the signal named "bigsignal" is a 16 bit value
and should currently be displaying its hex value of 0xdead. You can change how it is displayed by right clicking its
current value which is in the list to the right of the name. There you should see a menu with the Radix option. Try
changing it to binary or 2s compliment.
Left clicking on the waveform itself will set the current position and display a vertical line at that position. The
number on the top is the time and the values on the left will reflect that time. Try selecting time 50. Notice that
values displayed have changed from the initial values to different values. To go to the next time clock changes, left
click to highlight clock and right click on the waveform and select “next edge”.
It is also generally useful to rearrange the values so that the signals you are most interested in are grouped together.
You can do this by dragging the signal names to different positions. Signals you are simply not interested in can be
removed via the edit menu or by pressing the delete key when they are selected. Try placing transition just below
clock and removing bigsignal.
4) The source pane
The source pane allows you to quickly and easily see the source
code of any of your modules. This is useful when you see some
suspicious behavior in the waveform viewer and you would like
to see what is affecting the value of a variable. It can also be
useful for making sure the file you've been editing to fix a bug is
actually the file being compiled. You can load the source file by
dragging a module from the hierarchy window to the source
window. Try dragging the "tbp" module over to see the source
code.
You can also drag specific signals from the hierarchy pane to the
source pane or between the waveform window and the source
pane. Try dragging the state variable from the source window to
the waveform window and then drag the prediction signal from
the waveform window to the source window. Note that the
source window highlights the definition of the signal.
To view the values of variables in the source window, you should
right click on the source window and then click on Annotate
Values option. Now all values shown by the source window will
be from the time selected in the waveform window.
5) Finding the bug
Now that we've gone through much of the tools, we should try to determine if there's a bug in our code. The output
of this branch prediction module is the signal named "prediction." As we mentioned before, from time 25 on the
value is unknown despite the fact that the input signals are known. This means there is a bug, but where is it? For
the signal to be unknown, a signal that it relies on to determine its state must be either unknown or unconnected. So,
we need to start by looking at how "prediction" gets set. The source for "tbp" should already be open, so let's use
that to trace back to where the problem is. Let's also add the "tbp" module signals to the waveform viewer so we
can see the signals in that module. Looking at the source, we can see that "prediction" is simply set equal to a bit of
signal "state." Looking at the waveform viewer, we see that, as expected, "state" is unknown at the same times.
Now we want to check the signals that define "state." We can see that "state" either takes on a known value of 01,
or it takes on the value of "next_state." So again, now we need to trace back to which values set "next_state."
Looking at this, we can see that "next_state" is determined by "state" and "taken." We already know state is broken,
so we need to look at "taken." On the waveform viewer, we see that "taken" is always in high Z state, that it has
never been set. Because "taken" is an input into this module, we know the problem is in how the testbench is
connecting to this module. Now let's drag the "testbench" module into the source viewer so we can look for the
problem. Looking at where tbp is instantiated, we can see that there was a typo. ".taken(taekn)" should have been
".taken(taken)" on line 9. The compiler assumed "taekn" was just a wire not being driven and although it gave a
warning, it did not give a compiler error on this. These little bugs can often be very annoying to deal with.
Now that we've found the bug, we want to save the waveform viewer configuration so we can just easily reload
them when we run the waveform viewer again. It may not seem to be a big deal right now, but eventually when
you're working with hundreds of signals, only a few of which are related to a problem you're trying to debug, it can
be very annoying to try to find and place them on the waveform viewer every time. To do this, on the waveform
viewer go to File → Save Session. Type in a filename, like View1.tcl or, in the future, any name you want. It will
be useful to keep the names descriptive as there may be different sets of signals to look at for different parts of a
module you're testing. Now exit DVE.
Use your favorite text editor to fix that typo in "tut_test.v". When you're finished, type make clean and then
make dve to recompile and reload the waveforms so we can double check that it's working properly now. Now on
the TopLevel.1 window, go to File → Load Session. Select your View1.tcl file and press OK. The waveform
viewer, complete with the original signals, should appear. Run the simulation again. Here we can see, that
everything is working properly now.
6) Back to thewaveform viewer
So far we’ve been treating the simulation as a post-
processing problem. We can also debug a running
simulation, much like one debugs a C program, allowing
you to debug even when you get an infinite loop and to
recompile within the gui. Only some basic useful features
will be explained here. Feel free to explore more advanced
features on your own.
To begin, open up the Makefile with your favorite editor
and change the SIMFILES field from tut_mod.v to
tut_mod2.v. This module has an infinite loop in it
(circular combinational logic). In a small file like this, it
would be fairly easy to just open up the .v file and find the
source of the infinite loop, but in larger systems
pinpointing the source of the problem will be a lot more
difficult.
Try running the simulator by running make.
You’ll notice that it hangs while making the simv
file. Press ctrl-C to break the process.
Do a make clean to remove any output files
that may have been created. Now we will use the
interactive compiler to find where our problem is.
Start it with the commandmake dve.
The TopLevel.1 window should appear. Now let’s find that infinite loop. Add the signals to the waveform viewer.
To find out where it is, click Simulator→ Start. The waveform viewer should hang and a red dot in the toolbox
above should get enabled. Now click on Simulator→ Terminate. You can now see that the simulation was hung at
time 25. The problem must be around here somewhere. We are now at time 30. Now let’s find out where we’re
looping. Click Simulator→ Step/Next → Next a few times and look at the tbp source code. We seem to be going
between tut_mod2.v:11 and tut_mod2.v:12. That means that lines 11 and 12 of tut_mod2.v are causing the problem.
We now know that the problem is in our module (not our testbench). In the tbp source, right click and
selectAnnotate Values to monitor the values of each wire. We want to see how we got to this stateIf we look at the
source window, we can see that lines 11 and 12 are assignments to loop1 and loop2. If we click next a few times
and watch the values, we can see that both loop1 and loop2 keep changing even though no time is actually passing.
This is a sure sign of circular logic. Since loop1 is dependent on loop2, and loop2 is dependent on loop1, the
circular path is fairly obvious in this case.
But if we weren’t sure, a visual depiction of our module might help. Right click on the tbp module in the hierarchy
pane and click on Show Schematic to open up the Schematic pane. We can see a diagram of our Verilog module. If
you make your window wide enough, you should be able to see a circular loop of wires in the general area of the
loop1 and loop2 assign statements
If this were an actual design, we would now have enough information to fix the bug. In this example, there’s
nothing to really fix, since the loop1 and loop2 variables are not used for anything, so we can just leave the design
alone and finish up.
There are a number of other useful features in the waveform viewer. Feel free to explore and try other things on your
own.
The Virsim debugger presented in the next section was traditionally used with EECS 470. However, Synopsys (the
company that makes VCS and VirSim) stopped supporting VirSim in 2006. A better and newer debugger is the
DVE debugger, which comes with newer versions of Synopsys VCS.
Cleaning up
Sometimes some of the tools may fail in strange fashions. If this happens or if you are in need of disk space, we
have provided a Makefile command that will hopefully help. This command removes all of the intermediate files
generated during compilation and simulation. The command is:
make clean
This should not interfere with any of the files generated during our next topic, synthesis, so you may want to use
some of the other cleaning commands provided as well. Those commands will be described in the next section.
Synthesis (the short version)
In this class we require that your hardware designs really represent hardware and we judge your final project on the
overall speed of your design. To accomplish this you will need to synthesize your verilog code. The synthesis tool
attempts to create an actual circuit level design of your verilog code. This circuit level design is actually a verilog
file itself, but it is structural verilog and uses a library of standard cells that another tool can lay out on a real chip.
One benefit of the output being a verilog file is that we can simulate the circuit level design in the same way we
simulate your behavioral design. Thus, we can test your synthesized design and if it does not behave properly your
code is not considered to be synthesizable and is therefore incorrect (though we do give partial credit).The clock
speed of the circuit that it generates will be the clock speed we use for your design. We won’t go into great detail
about synthesis in this tutorial but we will show you the basics.
1) The synthesis script
tut_synth.tcl is a script of commands for the synthesis tool. Please open it using your editor of choice. For the time
being you should only have to modify the four lines near the top under the comment, “The following four lines must
be updated for every new design”. We will attempt to explain the rest of the magic in the script to you later. The
first line tells the tool to read your specific synthesizable verilog files. The second line tells the tool the name of
your top level module. In this case we only have one synthesizable module so it is the verilog name of that module.
The next line must also properly reflect the name of the clock signal in your design. If you do not have a clock in
your design just leave these as they are. The final line tells the tool the clock period you are aiming for. A higher
period will generally yield a slower synthesized design but will take less time to synthesize. Since speed does not
matter yet, leave this at a nice high number.
2) Synthesizing
In order for the Makefile to operate properly you need to add a rule for the .vg file you will be creating. Open
Makefile in your favorite editor. After the SYNFILES = two_bit_pred… line insert the following code:
two_bit_pred.vg: tut_mod.v tut_synth.tcl
dc_shell–t –f tut_synth.tcl | tee synth.out
Note: Do not copy and paste these two lines into your Makefile. Type it instead. The special characters are not
copied correctly when you do copy and paste.
Be careful to put a single tab after the : and a single tab before dc_shell. Makefiles are very particular about white
space. For each module you will create you must add it to the SYNFILES line as well as add a make option. Save
the file and exit the editor.
Now you are ready to run the synthesis script and generate the synthesized verilog please execute this command:
make syn
The synthesis process can take a long time. Since this design is so small it shouldn’t be too bad but be prepared for
some long waits. The synthesis script generates a number of files. the “db” files are binary files that are not
interesting to look at. Please edit the following files to see what they look like.
two_bit_pred.rep This file tells you about the properties of the synthesized circuit. The important things to
look for at this point are any violations (search for “VIOLATED” or “no violated”) and
the minimum value for slack (search for “slack”).
two_bit_pred.chk This file should be empty. If it is not empty it will contain warnings and/or error
messages that can help you determine what is going wrong.
two_bit_pred.vg This is the verilog code for the synthesized design. Do not feel any need to understand
this horrible code. There is a reason we do not write this code directly.
3) Running the synthesized code
All of the Makefile shortcuts defined for your original code have corollaries for your synthesized code.
4) Cleaning up from synthesis
To remove absolutely all of the files that result from synthesis, compilation, and simulation (including the
synthesized verilog files, the synthesis reports), which is a very scary thing to do, use this command:
make nuke
Trying it yourself
At this point we would like you to try writing and debugging a design from scratch (well, you should feel free use
any of the code we have provided for templates).
1) Design the hardware
As discussed in discussion, design the hardware and identify what the registers, inputs, outputs, and combinational
logic blocks are at a high level. You should implement a Moore state machine for an arbiter (which conceptually
will be connected to two requestors A and B at a higher level and should provide signals to each indicating whether
control has been granted to it. The state machine is described by this state transition diagram:
2) Write the verilog code
Write verilog code for the module (arbiter.v) and for a testbench (arbiter_test.v) for the module. Ideally, you should
try to write a testbench that produces the correct/incorrect output that will be required for projects, but for this
tutorial that is not vital.
3) Make sure it works
Compile, run, and debug your verilog using the tools described in this tutorial. Note that you may need to modify
the Makefile.
4) Synthesize your design
You will probably need to modify the tut_synth.tcl script (possibly making a copy of it) to do this.
5) Make sure the synthesis worked
This means both looking at the report file and testing the synthesized code.
6) Have one of the GSI’s check off your lab by next Thursday’s office hours.
Congratulations, you have finished the tutorial.
Grant
to
neither
Grant
to A
Grant
to B
A requesting
A requesting
B not
requesting
B requesting
A not
requesting
B requesting
but A not
neither
requesting
APPENDIX-the OLD debugger.
the debugger (interactive waveform viewer)
Because verilog is a hardware description language, the normal concept of a debugger (which allows one to step
through the execution of a program from line to line) does not make sense. Instead, we use a waveform viewer
which has more in common with electronics tools. This tool allows you to graphically view how the signals change
over time. The Makefile command for compiling your code and running this tool is the following, go ahead and run
it:
make int
1) The interactive window
The interactive window should appear. From here you can control
the simulation interactively, much like a debugger. To recompile
your code after making changes go to Sim->Rebuild and Re-exec.
The interactive window has a number of useful features, but we
need a few other tools before we can make effective use of it.
Now select “Hierarchy” from the Window menu.
2) The hierarchy window
The hierarchy window should appear. This window is used for
finding the signals and modules you are interested in viewing in
other windows.
The left pane contains a hierarchical view of modules that
instantiate other modules. In this case, our testbench instantiates
one other module. If you click the "+" next to testbench, all of the
modules instantiated by testbench will become visible. In this case,
there's only one such module called tbp. If there were more they
would be listed below it. tbp? What is tbp? There is no module
named tbp in the code we gave you. Actually, tbp is the name of the instantiation of module two_bit_pred in
testbench. This is why using meaningful identifiers for these
otherwise meaningless names can be important.
The right pane lists the signals contained in the currently selected
module.
The window menu is used to bring up the other windows. Select
“Waveform” from the window menu now.
3) The waveform window
The waveform window should appear. This window is where
most of the action is. The signals under investigation are
displayed in this window.
The easiest way to display signals in the waveform window is to
drag them from the hierarchy window (using the middle mouse
button) to the waveform window. First, left click on the
"testbench" module on the left side of the hierarchy window to
select it. Now middle click on it, holding down the middle
mouse button, and move your cursor over to the waveform
window and release when the cursor is green. You should now
see all of the signals within the testbench module displayed.
Individual signals can also be selected and dragged in the same way.
Once you have dragged some signals into the Waveform window, press the “Continue” button on the interactive
window. This will run the program until the end. You must choose the signals you wish to view before you press
continue. That is because the only signals that will be stored during the execution are those signals that are in the
waveform viewer. If during your debugging you discover that you need some other signals, add them to the
waveform viewer (they will show up as gray which indicates that the values were not logged), and choose Sim->Re-
exec.
As said before, the waveform window is supposed to help facilitate your debugging by allowing you to see how
signals are changing in relation to each other as time progresses. Underneath the actual waveforms is a scrollbar
that will allow you to move to different time values. Go ahead and scroll right until you get to the end. This is
where the simulation ended. This is not a very large simulation, so it might make sense to want to zoom out so you
can see more of what's going on in a single window. On the toolbar at the top of the waveform window, you will
find a big Z, and smaller z, and a z%. The big Z zooms you in, the little z zooms you out, and the z% let's you pick
how much of the simulation you want to fit on a single screen. Practice zooming in and out for a few seconds. Try
zooming out so the whole simulation is one 1 screen.
There are many different types of things you can see appear in the waveform window as a signal. The most obvious
and probably most common would be a normal 1 bit signal, which is represented by lines and edges moving up and
down as time progresses (where up is a value of 1 and down is a value of 0), "transition" is this kind of signal.
Another possibility is that the value of the signal is unknown, that its value could not be determined, and is usually
the sign of a bug in your program. An example of this is "prediction" from time 25 on. These can be seen quite
clearly by the amount of red that it shows and the value is represented as "x". Seeing a lot of these can be a good
indication of a problem (though sometimes it is not a problem). Another possibility is a high Z value. These are
shown as a gold line and indicate that nothing is driving the signal, that nothing ever sets its value. "taekn" is an
example of this. These could occur as a result of a typo or forgetting to instantiate a module properly. The last kind
of signal is one that consists of more than 1 bit. In this case, "bigsignal" is 16 bits wide. Its value is not shown with
rising and falling lines, but with separations between textual values.
For signals greater than 1 bit, the value is displayed in the wave. Sometimes it's convenient to have this value
displayed as hex, binary, 2s complement decimal, whatever. Notice the signal named "bigsignal" is a 16 bit value
and should currently be displaying its hex value of 0xdead. You can change how it is displayed by right clicking its
current value which is in the list to the right of the name. There you should see a menu with lots of choices. Try
changing it to binary or 2s compliment.
Left clicking on the waveform itself will set the current position and display a vertical line at that position. The
number on the top is the time and the values on the left will reflect that time. Try selecting time 50. Notice that
values displayed have changed from the initial values to different values. To go to the next time clock changes, left
click to highlight clock and right click on the waveform and select “next edge”
It is also generally useful to rearrange the values so that the signals you are most interested in are grouped together.
You can do this by middle-dragging the signal names to different positions. Signals you are simply not interested in
can be removed via the edit menu or by pressing the delete key when they are selected. Try placing transition just
below clock and removing bigsignal.
4) The source window
The source viewer allows you to
quickly and easily see the source code
of any of your modules. This is
useful when you see some suspicious
behavior in the waveform viewer and
you would like to see what is
affecting the value of a variable. It
can also be useful for making sure the
file you've been editing to fix a bug is
actually the file being compiled. You
can load the source file by dragging
with the middle mouse button a
module from the hierarchy window to
the source window. Try dragging the
"tbp" module over to see the source
code.
You can also middle drag specific
signals from the hierarchy window to
the source window or between the
waveform window and the source
window. Try dragging the state
variable from the source window to
the waveform window and then drag
the prediction signal from the
waveform window to the source
window. Note that the source
window highlights the definition of
the signal and puts the signal name in
the “Find” dialog at the bottom right of the source window. Pressing the arrow buttons next to that dialog will
search the source for that text.
To view the values of variables in the source window, you should first “link” the source window and the waveform
window to be in lock-step. Left click on the chains in the upper right corner of the source window and select the
same letter that is shown next to those chains for the waveform window. Now all values shown by the source
window will be from the time selected in the waveform window. To actually display the values, select “Display
Values” from the display menu of the source window. Specific times can also be entered into the “Time” dialog at
the bottom left of the source window.
5) Finding the bug
Now that we've gone through much of the tools, we should try to determine if there's a bug in our code. The output
of this branch prediction module is the signal named "prediction." As we mentioned before, from time 25 on the
value is unknown despite the fact that the input signals are known. This means there is a bug, but where is it? For
the signal to be unknown, a signal that it relies on to determine its state must be either unknown or unconnected. So
we need to start by looking at how "prediction" gets set. The source for "tbp" should already be open, so let's use
that to trace back to where the problem is. Let's also drag the "tbp" module signals to the waveform viewer so we
can see the signals in that module. Looking at the source, we can see that "prediction" is simply set equal to a bit of
signal "state." Looking at the waveform viewer, we see that, as expected, "state" is unknown at the same times.
Now we want to check the signals that define "state." We can see that "state" either takes on a known value of 01,
or it takes on the value of "next_state." So again, now we need to trace back to which values set "next_state."
Looking at this, we can see that "next_state" is determined by "state" and "taken." We already know state is broken,
so we need to look at "taken." On the waveform viewer, we see that "taken" is always in high Z state, that it has
never been set. Because "taken" is an input into this module, we know the problem is in how the testbench is
connecting to this module. Now let's drag the "testbench" module into the source viewer so we can look for the
problem. Looking at where tbp is instantiated, we can see that there was a typo. ".taken(taekn)" should have been
".taken(taken)" on line 9. The compiler assumed "taekn" was just a wire not being driven and although it gave a
warning, it did not give a compiler error on this. These little bugs can often be very annoying to deal with.
Now that we've found the bug, we want to save the waveform viewer configuration so we can just easily reload
them when we run the waveform viewer again. It may not seem to be a big deal right now, but eventually when
you're working with hundreds of signals, only a few of which are related to a problem you're trying to debug, it can
be very annoying to try to find and place them on the waveform viewer every time. To do this, on the waveform
viewer go to file->save configuration. Type in a filename, replacing default.cfg with 470tut.cfg or, in the future, any
name you want. It will be useful to keep the names descriptive as there may be different sets of signals to look at for
different parts of a module you're testing. Now exit the waveform viewer by choosing file->exit
Use your favorite text editor to fix that typo in "tut_test.v". When you're finished, type make int to recompile
and reload the waveforms so we can double check that it's working properly now. Now on the hierarchy window,
go to file->load configuration. Select your 470tut.cfg and press OK. Your waveform window, complete with
signals, should appear. Here we can see everything is working properly now.
6) Back to the interactive waveform viewer
So far we’ve been treating the simulation as a post-processing problem. We can also debug a running simulation,
much like one debugs a C program, allowing you to debug even when you get an infinite loop and to recompile
within the gui. Only some basic useful features will be explained here. Feel free to explore more advanced features
on your own.
To begin, open up the Makefile with your favorite editor and change the SIMFILES field from tut_mod.v to
tut_mod2.v. This module has an infinite loop in it (circular combinational logic). In a small file like this, it would
be fairly easy to just open up the .v file and find the source of the infinite loop, but in larger systems pinpointing the
source of the problem will be a lot more difficult.
Try running the simulator by running make. You’ll notice that it hangs while making the simv file. Press
ctrl-C to break the process.
Do a make clean to remove any output files that may have been created. Now we will use the
interactive compiler to find where our problem is.
Start it with the commandmake int
The interactive window should appear. Now let’s find that infinite loop. To find out where it is, click “continue”.
The history should reach time 25 and then hang. The problem must be around here somewhere. Press ctrl-C to
interrupt the simulation where it stopped. We are now at time 30. Now let’s find out where we’re looping. Click
next a few times and look at the history window. We seem to be going between tut_mod2.v:11 and tut_mod2.v:12.
That means that lines 11 and 12 of tut_mod2.v are causing the problem.
Click on Window and open up a Hierarchy window, a Waveform window, and a Source window. We know the
problem is in our module (not our testbench), so use the middle mouse button to drag tbp to the source window and
waveform window. In the source window, click Display->Show Values to monitor the values of each wire. We
want to see how we got to this state, so click on Sim->Re-exec to start over. Now, click Continue again and then
ctrl-C to interrupt the simulation. Let’s see what we have.
If we look at the source window, we can see that lines 11 and 12 are assignments to loop1 and loop2. If we click
next a few times and watch the values, we can see that both loop1 and loop2 keep changing even though no time is
actually passing. This is a sure sign of circular logic. Since loop1 is dependent on loop2, and loop2 is dependent on
loop1, the circular path is fairly obvious in this case.
But if we weren’t sure, a visual depiction of our module might help. Click on Window -> Logic to open up the
Logic window. Now drag the tbp module into this window, and we can see a diagram of our Verilog module. If you
make your window wide enough, you should be
able to see a circular loop of wires in the general
area of the loop1 and loop2 assign statements. If
you want to see how the values are changing,
click the left and right pink arrows to track
individual changes. You can also select a
specific wire and click the green arrows to track
changes on that signal.
If this were an actual design, we would now
have enough information to fix the bug. In this
example, there’s nothing to really fix, since the
loop1 and loop2 variables are not used for
anything, so we can just leave the design alone
and finish up.
There are a number of other useful features in
the interactive waveform viewer. Feel free to
explore and try other things on your own.