Ece 551 Modelsim Tutorial: Brian Hickmann, Michael Morrow Dept of Ece, Uw-Madison
Ece 551 Modelsim Tutorial: Brian Hickmann, Michael Morrow Dept of Ece, Uw-Madison
In this tutorial, you will learn how to setup a ModelSim project, compile your Verilog files, correct compilation errors, and perform design debugging using ModelSim. The example design used within this tutorial is simple Synchronous Serial Port (SSP) that contains both a send and receive module. It has a simple 3-wire interface: Port Name SerData Recv_nTran StartOp Function Bi-directional data line used for both sending and receiving Input that indicates if we are receiving (1) or transmitting (0) Input that indicates that an operation should be performed
The design of this unit is broken into a number of separate modules: Module Name ssp receive transmit busint Function Top-level module which instantiates all of the below sub-modules Contains the shift register and state machine for the receiver Contains the shift register and state machine for the transmitter Contains the logic to control the three wire serial interface
The tutorial also contains testbenches for the receive, transmit, and ssp modules. The ModelSim Tutorial must be run on a Linux workstation using your CAE account in order to use the latest release of ModelSim (version 6.3).
IMPORTANT NOTE
It is critical to remember that Verilog is NOT a software language. Verilog is used to describe hardware. While ModelSim may provide the ability to step through the code or insert breakpoints, this is NOT what actually happens when the hardware is operating. In reality, hardware is inherently parallel, with each transistor or gate continuously providing an output signal based upon its input signals. For example, in software, if there is an if (flag) a = b&c else a = b|c statement, only one branch of the statement is actually executed. However, if HDL code has an if-else statement, hardware must be created for both branches if the value of flag isnt constant. When we synthesize to hardware, both an AND gate and an OR gate are created with b and c as inputs, and a multiplexer is created to choose the result based on the value of flag, and the output of the multiplexer is sent to a. Both gates are always present and operating continuously, regardless of the value of flag. Stepping through the code, however, will make it appear as if only one of the branches is executed. To make the simulation more
efficient, the simulator will only perform calculations and event scheduling when necessary. Since it knows the value of flag, it will only schedule the event relating to the input of the multiplexer that is active.
1 Tutorial Setup
Directory and File Setup
In your root directory, create an ece551 directory: %> mkdir ece551 Change directory to the ece551 directory: %> cd ece551 Copy all the tutorial files to your current directory: %> cp r ~hickmann/public_html/tutorials/modelsim . Change directory to your tutorial directory: %> cd modelsim/tutorial
Start ModelSim
%> newver vsim When you start ModelSim for the first time, a pop-up box will appear (possibly after a short delay) as in Figure 1-1. You should check the Dont Show box and then close the window. You need to use newver vsim because CAE (Computer Aided Engineering) also supports an older version of ModelSim that some research students use. The version that we will use for the course offers more complete support for Verilog 2001 and an improved user interface.
Figure 1-2: ModelSim default window. Now that ModelSim is open, you should see a default window similar to that in Figure 1-2. There are a few things to note about the window. In the lower left hand corner, it says <No Design Loaded> which indicates that there is no current Verilog or VHDL simulation. There is a Workspace on the left hand side of the window that currently contains only the Library tab. On the bottom of the window is a command line area that can be used either to issue commands, or view the outputs of commands run through the GUI. Many (although not all) operations performed through the menus will also echo into the command line or transcript area, so you can learn the command-line operation as you go. Knowing the command-line commands is important, for example, if you want to write a script to perform a set of simulations. Help for each command-line command is available by entering help <command name> on the command line.
2 Creating Projects
Create a New Project
File->New->Project
Figure 2-1: Create Project dialog The dialog of Figure 2-1 will appear. If necessary, use the Browse button to make sure the Project Location is set to your tutorial directory. Name the project tutorial as in Figure 2-1 and click OK.
Figure 2-3: Add File dialog box Click Browse and select all of the files as in Figure 2-4. Click Open and then OK. The files will be added to the project.
Figure 2-4: Select Files dialog box Existing files can be added to a project at any time by right clicking within the workspace window and selecting Add to Project -> Existing File.
Figure 2-5: Create File dialog box Change Add file as type to Verilog and type in the name of the new file as shown in Figure 2-5. Click OK then Close to return to the main window. The new file will now appear in the workspace window of the project 2.) Click on File->New->Source->Verilog. This will open a blank file within the main ModelSim window for you to edit and save as needed. 3.) Create a new *.v file using an external editor and add it by right clicking on the workspace and selecting Add to Project -> Existing File.
Figure 2-6: ModelSim main window, with project open All of the tutorial files are now in the project, and a Project tab is now part of the workspace. The status of all of the files is ?, which indicates that there has not yet been an attempt to compile them. In other words, at this point it is not known whether the file can successfully compile or not.
Figure 3-1: The Unsuccessful Compile window provides details on why the compilation failed
This error indicates that a semicolon is missing from one of the lines and is one of the most common errors in code entry. There are only two errors listed, even though there are more than two errors in the code. When there is a missing semicolon, the compiler will not attempt to finish compiling and thus will not report any other errors that exist. Note that the semicolon is missing from line 2, but line 3 is listed instead because the compiler is only sure that the semicolon is missing once it finds the output keyword on the next line. Make sure to check nearby lines when fixing missing semicolons.
Figure 3-2: Edit window with errors.v code (with code errors)
Figure 3-3: The Unsuccessful Compile window showing a different error The first error is due to the fact that the output o is not listed within the modules port list despite being declared as output on line 3. The second error indicates that a variable (o in this case) is being used in an always block but was not declared as a reg variable. You may also receive this error if you attempt to use a continuous assign statement to assign a value to a reg variable. Fix the code by adding , o on the first line, and reg on the third line. The full corrected code appears in Figure 3-4. The errors.v file should now compile without errors after these last changes.
Alternative ways to load a testbench: - From Library tab in main window, double click on t_receive underneath the work library - In the command line area type: vsim work.t_receive
Figure 4-2: Main ModelSim window after loading the testbench If loading of the testbench is successful, the main window should now appear similar to Figure 4-2, though you may have to expand DUT and its sub-modules. Note that there is now a sim tab in the workspace. In the figure, all of the design instances are expanded so that everything is visible. In the sim tab, there are three primary columns. The first column, Instance, shows the instance name of each structure that is currently under simulation. The second column, Design unit, shows the module name for each of the instances. The third column, Design unit type, classifies the instances into different types according to their function. In this example we see there are Module types and Process types. To the right of the sim tab in a separate embedded window is the Objects window. This window shows all of the signals for the currently selected instance in the sim tab and their values at the current timestep or the timestep indicated by the waveform cursor. This window is discussed next
Method 1
Typing 100 ns in the Run Length box in toolbar and click the Run button (the button directly to the right of the Run Length box)
Method 2
Type %> run 100ns in the transcript window and press enter.
Once the simulation is run for 100 ns, you will notice that the signals within the Objects window have changed from xs to actual binary values. Also, in the bottom bar now reads Now: 100 ns to indicate that the simulation is currently at 100 ns.
Method 1
Click the Run all button on the toolbar.
Method 2
Type %> run -all in the transcript window and press enter. Your simulation time should now be 7,905 ns. When a $stop or $finish statement is reached, a window appears showing you where this statement is in the code.
Method 1
Click the Restart button in either the main window or the Source window.
The dialog shown in Figure 6-1 will appear. Leave all boxes checked, and click Restart.
Method 2
%> restart f Note that the f option is used to force restart of the simulation, which will not show up when using method 1 since you have manually checked restart options. Your simulation time should now once again be at 0 ns.
Method 1
Select Simulation -> End Simulation from the menu bar.
Method 2
Type %> quit -sim in the transcript window and press enter.
Figure 7-1: Wave window You may need to expand the size of the window and certain columns, but your wave window should now look like Figure 7-1. The names of the signals are currently several levels deep. In larger designs this problem becomes even worse. We can change the number of levels that are seen. Detach the wave window from the ModelSim main window by clicking the button to the left of the X in the window. The button looks like a box with an arrow pointing to the upper right. Doing this will enable additional options for the waveform window. MAKE SURE TO DO THIS OTHERWISE THE TUTORIAL WONT MATCH.
Figure 7-2: Wave Window Preferences dialog We can change the Display Signal Path to 2 so that we can see the name of the signal and the module it is contained in. Some other useful values are 0 which will show the full path and 1 which will show only the signal name. Another option that is useful to disable is the double-click to show the Drivers option. You may test the other options for your personal preferences.
Figure 7-3: Wave window after running the simulation with the given commands
Zoom
You will find at times that you need to Zoom In on a signal value in a particular time span, or that you need to Zoom Out to get a better overall picture of the operation of the hardware. Also, the Zoom Full option is very useful to allow you to see the entire operation of the circuit, and from there you can Zoom In to the area you need to examine. There are three graphical buttons that can be used. Zoom In 2x Zoom Out 2x Zoom Full You can also use a menu option
View->Zoom->Zoom Full Yet another technique is to center click, and draw a box from lower right to upper left. The box defines the new area visible in the window.
Method 1
Select ExpectedDataOut. View->Properties Change Radix from Default to Hexadecimal. Note that in Properties window you may also change the Wave Color and Name Color, discussed later.
Method 2
Select DataOut (all instances in the wave window). Right click, and from the context menu, select Radix -> Hexadecimal. Note that using this method, you may select several signals at once and change all of the radices at the same time. Now make these signals displayed in hex: DataOut, ExpectedDataOut, RBufShiftReg Now make these signals displayed as unsigned: State, NextState
Add a Divider
Another feature to help organize many signals is dividers. You may insert a divider to separate logical groups of signals and make the wave window easier to read. You can right-click on any divider you have created to open a context menu, where you can select Delete if you no longer need the divider.
Method 1
Select StartOp. Add->Divider
Change the Divider Name to DUT signals and change Divider Height to 30. You now have a divider between the signals. You may change the name of the divider or the divider height at any time by right clicking and choosing Divider Properties.
Method 2
Select the signal that you want to create a divider above. Use the right-click contest menu to Insert Divider. You get the same dialog as Figure 7-5.
Figure 7-6: Wave Color dialog The signals in the top pane are now orange, as shown in Figure 7-7. Note that the signal coloring here overrides the default coloring, so do not change the color of signals if you wish to use the default coloring to tell the difference between xs, zs and binary values.
Figure 7-7: Wave window showing orange signals in the top pane
Figure 7-8: Combine Signals dialog The dialog of Figure 7-8 will appear. Fill in RBufShiftReg_ls4 as the Result Name, and click OK. A new signal is now created and appears above RBufShiftReg in the wave window. This signal is simply a different way to view existing signals, not an entirely new set of wires introduced in the design.
Cursors
Cursors are used within ModelSim to specify the time at which you want to view a signals value and also measure the time between two events. By default, the wave window begins with one cursor. Wherever you click in the wave window, the cursor will be placed at that time. There are a few cursor buttons: Add Cursor: Delete Cursor: Previous Transition: Next Transition:
- Right click on the time next to Cursor 1 (in the signal column, not the value under the cursor), and then enter 700 ns. - Right click on the cursor (at the bottom of the screen) and choose Cursor Properties. Change the time to 700 ns.
Lock a Cursor
Right-click on the cursor and select Cursor Properties. Check the Lock Cursor to specified time box. Alternative ways to lock the cursor: - Right click on the cursor, and choose Lock StartOp The cursor is now locked in place (as is denoted by the color changing to red). This is useful if you are working with several cursors, and one time is to be used as the base point, or you dont want to accidentally move a cursor.
Add a Cursor
You can either use the graphical Add Cursor button, right click on the bottom part of the wave window and select New Cursor, or Add->Cursor A new cursor has been inserted, and a measurement of the amount of time between the two cursors is shown at the bottom of the screen. You will also see that there is a row for each cursor in the wave area. Right clicking in one of these rows will open the context menu for the corresponding cursor.
Save Settings
Now that we have our wave window set up to appear as we want it to, lets save the format so that we dont need to reproduce all of the changes we made every time we want to run this simulation. File->Save... Click Save. Now, when we wish to run this simulation, we can choose File->Load so that all of the settings we have specified will be there (note that the data itself is not saved, just settings such as what order the signals are in, their radix and color etc.).
Save Datasets
If we have a known good dataset, or we simply want to save the data to analyze later, the data set is automatically saved in a file called vsim.wlf. We can open this data any time by doing File->Open...
When we re-open the dataset, we must either have a saved format as well, or we must manually create a wave format as we did earlier.
8 Simulation Debugging
Now that we have learned how to setup our waveform window, we will look at how to use it to test the receiver module that we are simulating. Within t_receive, we have a number of tests which input data to the receiver as a serial signal on the SerData signal. We want to make sure that the parallel byte of data output by the receiver on its DataOut signal matches the serial data put into the module.
Figure 8-1: Wave Window Using the ExpectedDataOut Signal for Debugging
Self-Checking Testbenches
A more advanced debugging technique is to add code to your testbench to check the values for you. This simplifies the process of checking your simulation output for correctness. A self-checking testbench is illustrated in the t_ssp testbench. Please end the current receiver simulation by typing quit sim in the transcript window and close the current wave window. Start a new simulation using the t_ssp module as described above. Add all of the testbench signals by right clicking on t_ssp on the sim tab and selecting Add -> Add to Wave. Type run all in the typescript window to run the simulation of the entire project until completion. The simulation should end at 1,280,505 ns. Click the Zoom Full button to view the entire waveform. It should look like the window pictured in Figure 8-2. The t_ssp testbench is self-checking and generate a signal called error that will go high only if an error is detected during the simulation. A message will also be printed to the transcript window by a $display statement if an error is found. Find the error signal on the wave and ensure that it never goes high during the simulation. While this testbench is slightly more complicated to write, it allows for easier checking of your design, especially as design get more complex or you run a large number of test cases. This testbench runs 256 test cases to exhaustively test sending every possible byte of information.
Creating a Breakpoint
All breakpoints are created by using the Source window, the same window used to edit the source files. While a simulation is running, some of the line numbers are highlighted in red. These are the executable lines of code. On these lines of code breakpoints can be created. These appear as red dots which denote a currently enabled breakpoint, while a black dot indicates a breakpoint that has been created but is not currently enabled. However, at this point in the tutorial you have not yet created the breakpoints or started to step through the code, so they will not appear on your window. For this tutorial, we would like to create a breakpoint at line 88 of the t_ssp testbench. This will stop the simulation after each test of the receiver. To do this, open the Source window for t_ssp.v by doubleclicking on this file in the Project tab. There are three way to create a breakpoint at this point in the code
Method 1
Right click over the desired line of executable code in the BP column and select Set Breakpoint to set and enable a breakpoint on that line
Method 2
Left-click once on the desired line number in the BP column within the Source window to create and enable a break at that line number (it must be an executable line of code). Click once more to disable the breakpoint. The breakpoint can be removed by right-clicking on it and choosing Remove Breakpoint.
Method 3
Select Tools->Breakpoints from the menu. The dialog box in Figure 9-1 will appear. Note that this dialog may be accessed from the Source, Signals or Wave window in the same way. Click Add to create a new breakpoint. The dialog in Figure 9-2 appears.
Figure 9-2: Add Breakpoints dialog Using this method, you may create breakpoints based upon a Signal or a Signal Value (discussed next) or a Line Number (like those in the first method). You can also use the Modify Breakpoints dialog to modify line number breakpoints made using the first method. If Signal or Signal Value is chosen, the Signal Breakpoint dialog of Figure 9-3 will appear. You may choose a name for the breakpoint, specify a condition (such as equal to 0) and commands (such as printing a message).
Figure 9-3: Signal Breakpoint dialog If File and Line Number is chosen, the File Breakpoint dialog of Figure 9-4 will appear. You may choose the File (source file), line number, instance name (if the same module is used many times), as well as a breakpoint condition and commands like when creating a breakpoint on Signal or Signal Value..
Now that we have our breakpoint created, first restart the simulation by typing restart f in the typescript window. Next, type Run 10 us into the typescript command-line. Notice that although we specified run for 10uS, we have reached the breakpoint and the simulation has stopped at 2,905 ns. The run button tells ModelSim to run for a specific amount of time, whereas the continue button tells ModelSim to run until the next breakpoint is reached. In that way, the continue button has a similar effect as run all. Click the Continue button You will run again and stop at the same breakpoint. Note that the time listed below the sim tab has now changed to 7,905ns. Disable the breakpoint.
Figure A-1: Simulation Error during Startup If you look in the code in t_errors.v, it is easy to see that the instantiation of the mux using an input wire which is 2-bits while the port is only 1-bit wide. Errors such as this must be fixed before simulations are run. Below is a list of common errors encountered when starting a simulation.
Explanation
This error appears when a module instantiation has too many or too few connections. While this may not be an error in all case (i.e. you wanted to leave an output unconnected) it usually indicates an incorrect instantiation and should be checked. This is an actual error which occurs when a connection to an instantiated module does not match the ports size. This should be fixed before moving on with simulation. This means you forgot to add a module to your project. Make sure and double check that all your Verilog files have been added to the project and are compiled as well as double-check the offending module name for misspellings. In most situations this is nothing to be worried about. Only in situations where multiple modules are using delay statements should this be looked into further.
Appendix B: Printing
The wave window can be printed either directly to a printer or to a Postscript file. To print directly to the nearest printer, choose File->Print Postscript, then change the Print command in the Write Postscript dialog to lp. If you wish to annotate waveforms in software or paste images of the waveforms into a document, it is recommended that you print to postscript first so that the image is converted to black and white (unless you plan to print in color later). You may then open the files in gv (GhostView) or convert them to PDF files (www.ps2pdf.com) to copy the images into the program or document you wish to use.