Cycle-Accurate Co-Simulation with Mentor Graphics ModelSim

Publish Date: Oct 05, 2018 | 5 Ratings | 5.00 out of 5 | Print | Submit your review

Overview

Simulation is an important step in the FPGA design flow. There are entire companies that create sophisticated tools for FPGA simulation. So that you can take advantage of these standard simulation packages, the NI LabVIEW FPGA Module interfaces with two third-party simulators: Mentor Graphics ModelSim and Xilinx ISim. You can use the design verification and debugging capabilities of these simulators to debug functional behavior and timing-based errors. To use ISim with the LabVIEW FPGA Module, you should be familiar with HDL simulators and VHDL, which is required for writing a testbench. You now have the option to write testbenches for ModelSim using either VHDL or LabVIEW. This tutorial steps through the process of using cycle-accurate co-simulation with a LabVIEW generated testbench in Mentor Graphics ModelSim. To learn more about the process of using cycle-accurate simulation with a VHDL testbench with either ModelSim or Xilinx ISim, reference the tutorial: Cycle-Accurate Simulation with Xilinx ISim.

Table of Contents

  1. Create an FPGA VI and Project
  2. Configure the LabVIEW FPGA Module for Simulation
  3. Create and Build Simulation Exports
  4. Option #1: Develop a Test Bench and Simulate the VI with Co-Simulation
  5. Modify the VI and Re-simulate
  6. Option #2: Develop a Test Bench and Simulate the VI
  7. Modify the VI and Resimulate
  8. Additional Resources

1. Create an FPGA VI and Project

First, create a new LabVIEW FPGA project and an FPGA VI. Later, you can test and debug this VI using LabVIEW FPGA simulation.

1. Create a LabVIEW project and save the project as incrementer.lvproj.

 

 

Figure 1. Create a LabVIEW Project

2. Add an FPGA target to the project. The target should support LabVIEW FPGA simulation. For this example, use an NI PXI-7854R multifunction RIO module.

 

Figure 2. Add an FPGA Target

3.   Add a new VI under the PXI-7854R module. Name the VI incrementer.vi.

 

Figure 3. Add a New VI

4.       Create the following block diagram. This VI increments the control value in a loop and writes the value to an indicator.

 

Figure 4. Create Value In and Value Out Controls

 

    —Add a single-cycle Timed Loop to the VI.

    —Create an I16 control in the loop and name it Value In.

    —Create an I16 indicator in the loop and name it Value Out.

    —Place a Decrement function in the loop.

    —Wire the control to the Decrement function and wire the output of the Decrement function to the indicator. Using a Decrement function in place of an Increment function is an intentional bug that you will fix later. 

Back to Top

2. Configure the LabVIEW FPGA Module for Simulation

1.       Before you build a simulation export, configure the simulator.

            —In LabVIEW, select Tools»Options to display the Options dialog box.

            —Under Simulator, select ModelSim.

If ModelSim is installed correctly, LabVIEW populates the Simulator directory field. If it does not, you can enter the path to the directory manually.

Note: You must select an SE version of ModelSim in order to use co-simulation.

 

Figure 5. Configure the Simulator

2. To configure the execution mode of the FPGA target, right-click the PXI-7854R target in the Project Explorer window and select Execute VI On»Third-Party Simulator.

Figure 5. Select the Third-Party Simulator Execution Mode for the FPGA Target

3. Before creating simulation exports, you must configure the FPGA simulation libraries. In LabVIEW, select Tools»FPGA Module»Update FPGA Simulation Libraries. This compiles the VHDL libraries that the simulation model needs. You need to do this only once.

 

Figure 6. Configure the FPGA Simulation Libraries

Back to Top

3. Create and Build Simulation Exports

    1. Create a simulation export build specification under FPGA Target. You use the build specification to create the simulation test bench and specify various options for it. To create the build specification, right-click Build Specifications under FPGA Target and select New»Simulation Export.
  1.  

    Figure 7. Create a Simulation Export Build Specification

    1. This opens a dialog box where you can configure simulation export settings. Notice that the build specification name is currently My Simulation Export. First, set the top-level VI to simulate by displaying the Source Files page and selecting incrementer.vi. Go back to the Information page and notice that the build specification name has changed to incrementer to match the name of the VI.

     

    Figure 8. Confirm that VI Name and Build Specification Name Match

    3.       Note that, on the Information page, you also can set the top-level simulation model name and destination directory. The top-level simulation model name is the name of the VHDL file that will be generated for the VI. Leave this field as the default value. You can specify where to generate simulation files in the destination directory. On the Source Files page, you can specify different signals that will be automatically added to the simulation waveform. Leave these as the default value. The Model Fidelity page contains information about the I/O fidelity level, bus model, and framework.

     

    4.       Click Build to save the build specification and generate the test bench files. LabVIEW displays a dialog box indicating that the simulation export has built successfully. Click Done to close the dialog box.

     

    Figure 9. Simulation Dialog Window

    5.       Next, navigate to the simulation directory by right-clicking the build specification and selecting Explore.

     

    Figure 10. Explore Simulation Export Directory

    6.       Look at the folders generated in the simulation export directory.The ModelSim folder contains files used by the ModelSim simulation tool. You do not need to view or edit any files in this folder.The niFpga folder contains VHDL files that LabVIEW generates to implement the simulation model. These files are regenerated each time the simulation export is built and should not be modified. However, some of these files can be useful later in the application. The user directory contains files that you can edit, including the top-level test bench file. In this case, the file is named tb_NiFpgaSimulationModel.vhd. This file is a template to implement a test bench and will be described later. This file is only generated the first time LabVIEW generates the build specification and will not be overwritten. On each build, LabVIEW generates a copy of the template in the niFpga directory for reference.

  2. .

Back to Top

4. Option #1: Develop a Test Bench and Simulate the VI with Co-Simulation

With co-simulation it is not necessary to modify the VHDL test bench for any host interface interactions with the FPGA VI. The test bench is created in LabVIEW with a LabVIEW FPGA host VI. If you want to model any external I/O, you will need to add the I/O models to the co-simulation test bench, located at /user/cosim/tb_NiFpgaSimulationModel_cosim_ModelSim.vhd.

  1. Add a new VI under My Computer. Name the VI HostIncrementer.vi.
  2. Place an Open FPGA VI Reference function on the block diagram. Configure the node as shown below.
  3. Place an FPGA Interface Read/Write function on the block diagram. Select the Value In control and wire in a value of 5.
  4. Drop an FPGA Interface Method Node. Select the Run Method.
  5. Drop an FPGA Interface Read/Write Node. Select the Value Out control and wire it to an indicator.
  6. Drop an FPGA Interface Close Node.

Your diagram should now look like the figure below. The test bench is now ready to run. When the VI is run, ModelSim will automatically launch in the background and begin running the simulation. The value that we get back for Value Out is 4. ModelSim should still be open after the VI finishes running. You can inspect the waveform for more timing information. For more detailed information on waveform signals refer to the “Using Waveform Viewer” help topic.

Back to Top

5. Modify the VI and Re-simulate

  1. Next, edit the VI. Close the simulator. In the VI, change the decrement operator to an increment, and change the name of the control from Value In to Input.

Figure 16. Edit the VI

  1. Save the VI and build the build specification.
  2. In "HostIncrementer.vi" change "Value In" to "Input".
  3. Run "HostIncrementer.vi" again. "Value Out" is now one plus the value of "Value In". Note that a new instance of ModelSim launches. Every time the test bench is run a new instance of ModelSim will be used. You can have LabVIEW automatically close the instance of ModelSim when the simulation is complete by checking the option "Close simulator when simulation completes" from the FPGA Module options page in Tools>>Options

 

Back to Top

6. Option #2: Develop a Test Bench and Simulate the VI

1.       In this case, the test bench is named tb_NiFpgaSimulationModel.vhd. Look closely at the VHDL template that your test bench generated. From the root simulation directory, open the file /user/tb_NiFpgaSimulationModel.vhd. The architecture block contains a block named MainStimulusBlock and an instantiation of the NiFpgaSimulationModel component.

 

  • MainStimulusBlock contains the host interface code. The existing code performs a download, open, run, and then close. Typically, user code to exercise the test bench is inserted between the run method and close.
  • NiFpgaSimulationModel implements the VI simulation model. You should not have to modify this instantiation.

2.       Test your VI by writing a value to the Value In control and then reading the Value Out indicator. To do this, use the NiFpga_Write and NiFpga_Read procedures. Look up the definitions of these procedures in one of the VHDL packages in the niFpga directory. You should be familiar with these packages:

 

  • PkgNiFpgaSimControlAndIndicatorProcedures.vhd contains control/indicator read/write procedure definitions.
  • PkgNiFpgaSimFifoProcedures.vhd contains FIFO method definitions, such as reading, writing, starting, and stopping FIFOs.
  • PkgNiFpgaSimMiscProcedures.vhd contains the other host interface procedure definitions, such as open, download, close, run, and reset.

Because this example uses control/indicator procedures, look in PkgNiFpgaSimControlAndIndicatorProcedures.vhd. Since your control and indicator are I16 types, you need the NiFpga_Read and NiFpga_Write versions that use signed data types. There are overloaded versions of these functions for unsigned, Boolean, and tFxpGen (for fixed-point types).

 

3.       Add a call to NiFpga_Write and NiFpga_Read before the call to NiFpga_Close in the test bench file, as shown below. You can fill in the parameters in the next step.

 

    -- This is an example of how to write a control:

    -- NiFpga_Write (

    --  Address => <constant in PkgRegister k[ControlName]_ctl>,

    --  Data => <variable [ControlName]_ctl_Data>,

    --  FiClock => FiClock,

    --  fiHostToTargetInterface => fiHostToTargetInterface,

    --  fiHostToTargetReady => fiHostToTargetReady,

    --  fiErrorStatusIn => fiErrorStatus,

    --  fiErrorStatusOut => fiErrorStatus);

    --*********************************************************************

   

    NiFpga_Write

    (Address => ,

     Data => ,

     FiClock => ,

     fiHostToTargetInterface => ,

     fiHostToTargetReady => ,

     fiErrorStatusIn => ,

     fiErrorStatusOut => );

 

    NiFpga_Read

    (Address => ,

     Data => ,

     FiClock => ,

     fiHostToTargetInterface => ,

     fiHostToTargetReady => ,

     fiTargetToHostInterface => ,

     fiTargetToHostReady => ,

     fiErrorStatusIn => ,

     fiErrorStatusOut => );

    

    PrintHostInterfaceStatus("Closing",HostInterfaceStatus);

    NiFpga_Close (

     FiClock => FiClock,

     fiTargetToHostInterface => fiTargetToHostInterface,

     fiHostToTargetInterface => fiHostToTargetInterface,

     fiHostToTargetReady => fiHostToTargetReady,

     fiTargetToHostReady => fiTargetToHostReady,

     fiErrorStatusIn => fiErrorStatus,

     fiErrorStatusOut => fiErrorStatus);

 

4.       To fill in the address parameter, find the constants for control/indicator addresses in the /niFpga/PkgRegister.vhd file. Open this file to find the following constants for control and indicator: kValue_In_ctl_0 and kValue_Out_ind_1. Fill these in as the address parameter in the procedure calls.

5.       To fill in the data parameter, use tI16 type variables, since the control and indicator are I16. (Find these data-type definitions in the /niFpga/PkgNiFpgaSimInterfaceLvDataTypes.vhd file.) If you look at the beginning of the MainStimulusProcess statement, you can see that there are variables already defined.

  MainStimulusProcess:process

   variable fiErrorStatus : tErrorStatus;

    --=====================================================================

    --Control and Indicator Data Variables

    -----------------------------------------------------------------------

    -- Use these variables with the NiFpga_Write and NiFpga_Read commands

    -- for controls and indicators to guarantee the correct data type read

    -- or write operation is performed. Refer to the auto-generated

    -- file, PkgRegister.vhd, in the nifpga directory for the register

    -- offsets for the controls and indicators.

    -----------------------------------------------------------------------

    variable Value_Out_ind_1_Data: tI16;

    variable Value_In_ctl_0_Data: tI16;

    --=====================================================================

6.       Connect the parameters that begin with fi* to standard signals defined within the test bench. These communicate with the test bench framework defined in the simulation model. You only need to be familiar with ErrorStatus signals. Monitor the signal fiErrorStatus for warnings and errors.

7.       Once you have filled in these parameters, the read and write procedures should look like this:

 

    NiFpga_Write

    (Address => kValue_In_ctl_0,

     Data => Value_In_ctl_0_Data,

     FiClock => FiClock,

     fiHostToTargetInterface => fiHostToTargetInterface,

     fiHostToTargetReady => fiHostToTargetReady,

     fiErrorStatusIn => fiErrorStatus,

     fiErrorStatusOut => fiErrorStatus);

 

    NiFpga_Read

    (Address => kValue_Out_ind_1,

     Data => Value_Out_ind_1_Data,

     FiClock => FiClock,

     fiHostToTargetInterface => fiHostToTargetInterface,

     fiHostToTargetReady => fiHostToTargetReady,

     fiTargetToHostInterface => fiTargetToHostInterface,

     fiTargetToHostReady => fiTargetToHostReady,

     fiErrorStatusIn => fiErrorStatus,

     fiErrorStatusOut => fiErrorStatus);

 

8.       Now, set the Value_In_ctl_0_Data variable value before writing it to the control. Insert the following line before the call to NiFpga_Write:

    --  fiErrorStatusIn => fiErrorStatus,

    --  fiErrorStatusOut => fiErrorStatus);

    --*********************************************************************

    Value_In_ctl_0_Data := to_signed(5, Value_In_ctl_0_Data'length);

    NiFpga_Write

    (Address => kValue_In_ctl_0,

     Data => Value_In_ctl_0_Data,

9.       You are ready to simulate. Make sure that you have saved the tb_NiFpgaSimulationModel.vhd file, and then launch the simulator by right-clicking the simulation export and selecting Launch Simulator. This brings up the ModelSim tool with the incrementer project already loaded.

 

Figure 11. Launch the Simulator

10.    Next, compile the test bench file and put some useful debug information on the waveform. LabVIEW generated a script in the user directory that you can run to do this. Select Tools»Tcl »Execute Macro and select the /user/niFpgaSimulate.do file.

 

Figure 12. Execute the Macro

11.    The macro should compile the test bench file successfully and list some control/indicator information and test bench status signals on the waveform. You can now run the test bench by typing “run –all” at the command line. You should see the following output:

 

Figure 13. Test bench Output

12.    Now, examine the waveform signals. Zoom out to full by selecting Wave»Zoom»Zoom Full so that you can see the entire waveform. Then, expand the Value In and Value Out headings in the waveform to see detailed information on the control and indicator.

 

Figure 14. Examine the Waveform Signals

13.    There are several items under Value In and Value Out. Clock is the clock signal that the registers implementing the control/indicator are synchronous to. Data is the register value for the control/indicator.You can see in this waveform that the Value In value begins with a default value of 0, gets set to 5, and then returns to the default value of 0 when the VI resets during close. Value Out begins with its default value of 0, goes to 0xFFFF (-1) when the VI starts running, goes to 4 a short time after Value In is set to 5, and goes back to the default value of 0 when the VI resets during close.

 

14.    Expand the Host Interface Register Access Signals section. The host writes Value In with a value of 5 at around 3580 ns, when the Host Write signal goes true for one clock cycle. This corresponds to the NiFpga_Write command issued in the test bench. You can also see that, at around 4000 ns, the Host Read pulse asserts for one cycle for Value Out, and the value read is 4. For more detailed information on waveform signals refer to the “Using Waveform Viewer” help topic.

 

Figure 15. Read the Clock Signals

Back to Top

7. Modify the VI and Resimulate

1.       Next, edit the VI. Close the simulator. In the VI, change the decrement operator to an increment, and change the name of the control from Value In to Input.

 

Figure 16. Edit the VI

2.       Save the VI and build the build specification.

3.       Launch the simulator and run the niFpgaSimulate.do macro. Refer to steps 9-11 of the previous section for help.

4.       The macro now errors when compiling the test bench. The constant you used for the Value In control address is no longer valid because you renamed the control, and the constant generated in PkgRegister.vhd changed.

 

Figure 17. Test Bench Macro Errors

5.       Open niFpga/PkgRegister.vhd to find the new name of the Input control. It is kInput_ctl_0. Replace kInput_ctl_0 where you used kValue_In_ctl_0 in tb_niFpgaSimulationModel.vhd and rerun the macro.

6.       When the macro completes successfully, run the test bench by typing “run –all” in the ModelSim command line.

7.       Examine the waveform data. The indicator value is the value of the control plus one.

 

Figure 18. Examine the Waveform Data

 

Back to Top

8. Additional Resources

Testing and Debugging LabVIEW FPGA Code

Cycle-Accurate Simulation in LabVIEW FPGA

Cycle-Accurate Simulation with Xilinx ISim

The NI LabVIEW High-Performance FPGA Developer's Guide

Back to Top

Bookmark & Share


Ratings

Rate this document

Answered Your Question?
Yes No

Submit