Xilinx ModelSim Simulation Tutorial

CIS 371 (Spring 2012): Digital Systems Organization and Design Lab

ISE Simulator is an application that integrates with Xilinx ISE to provide simulation and testing tools. Two kinds of simulation are used for testing a design: functional simulation and timing simulation. Functional simulation is used to make sure that the logic of a design is correct. Timing simulation also takes into account the timing properties of the logic and the FPGA, so you can see how long signals take to propagate and make sure that your design will behave as expected when it is downloaded onto the FPGA.

Functional Simulation

  1. Open the ISE Project Navigator and create a new project. After entering a project name and location, you'll be prompted for the project properties. Set the properties as shown below, making sure to select ISE Simulator as your Simulator.


    Click Next, and create a new Verilog Module source named full_adder. The inputs and outputs for the module are shown below. Once you've entered them, click Next and Finish until your module is generated.



    If you accidentally select a simulator other than ISE Simulator for your project, or if you open a previous project that had a different simulator selected, you can change the simulator by right-clicking on xc2vp30-7ff896 in the Sources window in ISE and selecting Properties.

  2. This tutorial will use a full adder that is the same as the one you created in Lab 0. You can use your own code, or copy the solution below:
    module full_adder(s, cout, a, b, cin);
      output s, cout;
      input a, b, cin;
      wire t1, t2, t3;
      xor (t1, a, b);
      xor (s, t1, cin);
      and (t2, t1, cin);
      and (t3, a, b);
      or  (cout, t2, t3);

    Run the Check Syntax process (under Synthesize) to make sure your code is entered correctly, and save your design.

  3. Right-click on full_adder.v in the Sources window and choose New Source. Select Verilog Test Fixture (not Verilog Module) and give your file a name such as "test_full_adder". Click Next, and you'll be prompted to associate the file with a module; choose full_adder, click Next, then click Finish. The file will be added to your project.

  4. ISE creates a skeleton test fixture (a.k.a. testbench) for you. The full_adder module is instantiated as the "unit under test" (uut). The inputs to the module are registers ("reg") because they are assigned in a procedural block (the section between "begin" and "end"). The outputs from the module are wires. The test simulation begins after the "initial begin" line. The values are initialized, and your code goes under the "Add stimulus here" line.

    The module is tested by assigning different values to the inputs in the test fixture, then running ISE Simulator to observe the outputs. Between assignments, delays are inserted using #n, where n is the number of nanoseconds of delay. For example, the following code tests when each of the inputs are high separately, changing the inputs every 25 ns:

    // Add stimulus here
    //125 ns
    #25;    a = 1'b1;
    //150 ns
    #25;    a = 1'b0;   b = 1'b1;
    //175 ns
    #25;    b = 1'b0;   cin = 1'b1;


    For the value 1'b0, the 1 represents the number of bits, the b stands for binary and the 0 represents the 1-bit binary value. Values can also be represented in decimal and hexadecimal; for example, 8'd27 represents 00011011 and 4'hA represents 1010.

    A good test fixture will test all or most of the possible inputs to the module, and especially any corner/boundary cases. Since the full adder only has three one-bit inputs, there are only eight possible input combinations, and your test fixture should simulate them all. Write the rest of the simulation code yourself; if you need help, you can view a completed test fixture here.

  5. After saving your test fixture, it will not immediately appear in the Sources window. To find the test fixture, click on the "Sources for:" drop-down box, and select Behavioral Simulation. The test fixture (test_full_adder.v) will now appear in the Sources hierarchy.


    To run the simulation in ISE Simulator, click on the test fixture in the Sources window to highlight it, expand the Xilinx ISE Simulator option in the Processes window, and double-click Simulate Behavioral Model. ModelSim will open and run the test code in your test fixture file.


    Make sure your test fixture file, not the file containing your actual Verilog module, is selected in the Sources window before running ISim. Otherwise, the simulation will not run correctly.

  6. Once ISim starts, check the Transcript window carefully for warnings and errors.


    Warnings that say "Module 'module' does not have a `timescale directive in effect" can be ignored; other warnings and errors should be resolved before examining the simulation results.

    The black and green section of ISim is the waveform area. To scale the waveform correctly, move the horizontal slider all the way to the beginning (the left), then click the Zoom-Out 2x button until a proper scale is reached. For this design, a suitable scale is from 0 ps to about 200000 ps (200 ns) or 400000 ps (400 ns).


    The leftmost column of the wave area lists the input and output signals for the test module, test_full_adder_v. These signals can be rearranged and deleted. The column to the right lists the values of the signals at the cursor. To set the default cursor, Cursor 1, click anywhere in the waveform. To move the cursor to the exact location where a signal changes, click on the signal so it is highlighted in blue, then click the Find Previous Transition and Find Next Transition buttons (transition.jpg) on the toolbar.


    Your output signals may have "false" transitions at times when more than one input signal changes. This is normal.


    "False" transitions in two output signals.

    Markers can be inserted by clicking on the "Single Marker" button in the toolbar, or by right-clicking in the waveform and selecting "New Marker". Markers can be renamed and deleted by right-clicking on them as well.

  7. Check that your module functions correctly by observing which of the input signals are high and checking that the outputs are correct. It may be helpful to drag the cursor across the waveform, so you can look at the numerical values of the signals instead of looking at the waves themselves. For modules with vector signals (rather than single-bit signals), you can right-click on the signal values and change the radix to display the values in binary, decimal, hexadecimal, etc. The output values have an "St" in front of them because they are set to the Symbolic radix by default; change this to Binary or another format.


    Use the Unsigned radix, rather than the Decimal radix, to view signals as decimal values. Signed decimal values can cause confusion.


    Drag the cursor across the waveform to see these values change, and check that the outputs are correct for the given inputs.

    Close the Simulator tab when you are finished. If you make changes to your module in ISE, the easiest way to restart the simulation is to close ISim, then run the Simulate Behavioral Model process again.


    ISim only allows one instance of to run on a computer at any time. You will need to stop any previous simulations before starting a new one.

Advanced Functional Simulation

  1. Instead of visually checking your simulation outputs every time you run the simulation, you can write test code that will change your inputs and check the outputs for you. Test code that does this is called a "task." Below is an example of a task for the full_adder module that takes five parameters: the three inputs for the module, and the two expected outputs. The task changes the inputs to the module, waits for the changes to propagate, then checks that the simulated outputs match the expected outputs. The task prints an error to the simulation console if the outputs do not match.
    task check_fa;
    input i_a;
    input i_b;
    input i_cin;
    input exp_s;
    input exp_cout;
        #25;    a = i_a;    b = i_b;    cin = i_cin;
        if (s !== exp_s) begin
            $display("Error at time=%dns s=%b, expected=%b", $time, s, exp_s);
            errors = errors + 1;
        if (cout !== exp_cout) begin
            $display("Error at time=%dns cout=%b, expected=%b", $time, cout, exp_cout);
            errors = errors + 1;

    The code for this task goes in your test fixture file, after the simulation block (from "initial begin" to "end"), but before the "endmodule." The task is "called" in the simulation block with the parameters you want to test your module with. For example, we can test all the possible inputs to the full_adder module with the following code:

    // Wait 100 ns for global reset to finish
    check_fa(0, 0, 0, 0, 0);
    check_fa(1, 0, 0, 1, 0);
    check_fa(0, 1, 0, 1, 0);
    check_fa(0, 0, 1, 1, 0);
    check_fa(1, 1, 0, 0, 1);
    check_fa(1, 0, 1, 0, 1);
    check_fa(0, 1, 1, 0, 1);
    check_fa(1, 1, 1, 1, 1);

    In addition to writing the task code and calling the task, we must also initialize the various variables. For a complete test fixture file that demonstrates the use of this task, click this link. If the simulation of your module produces any errors, the time of the error, the current output and the expected output will be displayed in the ISE console, along with a count of the total number of errors. If your simulation has no errors, a message will be displayed telling you so.


    Simulation errors displayed in the console.


    Don't just take the absence of error messages to mean that your module is working perfectly. An error in your simulation code may have prevented your tests from even being executed, or if there is an error in your module design and the value of a wire in your simulation is unknown (indicated by the value "x" and a red line in your waveforms), Isim may assume that any comparisons against this wire are true. Therefore, you should always check your waveforms in addition to the ISim console.


    A wire with an unknown value, most likely due to a module design error.


    If your simulation has errors, you can go back, fix your module and re-use the same test fixture to test the module again. It is a good idea to write your test fixture before you design your module (as long as you know the interface), because this will force you to think carefully about the expected outputs of the module and corner cases that should be tested. Once you have written a complete and accurate test fixture, it can continually be used to test your module.

  2. In some designs, you will want to observe internal signals that are not inputs to or outputs from the module. In the full adder, we can observe the intermediate internal wires t1, t2 and t3. On the left side of the workspace window, click on the "Sim Instances" tab, then select your module under the testbench instance. In this case, select test_full_adder_v > full_adder. All of the wires in the full_adder module appear in the "Sim Objects" tab of the "Processes" window just underneath the "Sim Instances" Tab. Drag any of the wires which you would like to observe to the waveform to add them.


    Immediately after adding the signals, they will not have any waveforms, and will have a value of "No Data" if you move the cursor. In order to read these signals, ISim must run the simulation again. To do this, first click on the Restart button on the toolbar, leave all of the boxes checked on the subsequent prompt and click Restart. ModelSim will reload the simulation, but will not re-run it; click the Run -All button on the toolbar to start the simulation again. Waveforms will now appear for t1, t2 and t3.



    Restarting and running the simulation again will not incorporate any changes you have made to your module or test fixture. To see the effects of these changes, close ISim and run the Simulate Behavioral Model process again in ISE.

    There are other simulation controls that may be useful. The Break button stops a running simulation. The ContinueRun button continues a stopped simulation. The Run button runs the simulation for the amount of time entered in the box to the left of the button.


Designing and Testing an Adder

Verilog HDL On-Line Quick Reference

Authors: Peter Hornyack, Milo Martin

Last Edited By: Mishal Awadah

Date: 2012-02-02 Thu

HTML generated by org-mode 6.33x in emacs 23