MATRIXx SystemBuild models can be compiled into a DLL using MATRIXx AutoCode and LabWindows/CVI. The DLL then can be run in LabVIEW Real-Time. The process described below will take a SystemBuild model and, using a customized template, generate C code using AutoCode. The generated code then will be compiled into a DLL using LabWindows/CVI. The DLL then can be called from LabVIEW or LabVIEW Real-Time through a Call Library Function node. Once the model is incorporated into LabVIEW, data acquisition or instrument I/O and a powerful user interface can be added.
- MATRIXx SystemBuild is a powerful simulation environment that allows modeling of linear and nonlinear systems. Large models easily can be managed by creating several layers of SuperBlocks. Models also can contain continuous and discrete components.
- MATRIXx AutoCode generates real-time C or Ada source code for a SystemBuild model. The code that is generated can be customized for different targets and application needs through templates.
- LabWindows/CVI is an ANSI C compiler that features code generation tools and prototyping utilities for quick C code development. LabWindows/CVI also can generate compact DLLs for use in LabVIEW Real-Time.
LabVIEW is a graphical programming environment that makes I/O operations, analysis and presentation easy. Using the LabVIEW Real-Time module LabVIEW code can be run deterministically on National Instruments Real-Time targets. LabVIEW provides tight integration with a wide variety of I/O through PXI boards as well as FieldPoint and CompactFieldPoint modules.
MATRIXx Product Information
LabWindows/CVI Product Information
LabVIEW Product Information
LabVIEW Real-Time Product Information
2. Generating C code from AutoCode
AutoCode is used to generate the C code for the model. A custom template is used to generate the code. MATRIXx generates a header file that defines the functions that are exported by the DLL, a c file that contains the model implementation, and a CVI project file (.prj) that contains the settings for CVI to build the DLL.
Download and copy the attached templates to the same folder as the shipping templates for MATRIXx (default location is C:\Program Files\National Instruments\MATRIXx\mx_63.2\case\ACC\templates). The templates depend on the templates that ship with MATRIXx. If the templates are placed in a different folder, then the shipping templates also should be copied to that same folder. These templates are intended for use with MATRIXx 6.3.
- Open the file that contains the model.
- In the Catalog Browser select the top-level SuperBlock.
- Go to Tools>AutoCode.
- Choose the destination folder and name of the files.
- Keep the CodeStyle as Subsystem and the Language as C. (Although the template also supports Procedure only code, the tutorial will focus on the Subsystems style code.)
- Click the Advanced button and select the Templates tab.
- Click the Browse button and select the c_cvidll.tpl file.
- Click OK twice.
- Verify, in the Xmath window, that the code generation completed successfully.
MATRIXx has now generated the source files necessary for building the DLL.
3. Compiling Code in CVI
All the necessary settings for CVI to build the DLL are contained in the CVI project file.
- Locate the files that were generated by AutoCode.
- Double click the CVI project file (.prj) The CVI project file should already be associated with CVI. If it isn't, launch CVI and choose File>Open>Project and select the project file.
- Build the DLL by choosing Build>Create Dynamic Link Library.
NOTE: The path to the AutoCode source files is hard-coded and may not be correct for your installation. If the path is incorrect you will get the prompt:
Select Yes, browse to the file in the AutoCode source directory (default location is C:\Program Files\National Instruments\MATRIXx\
mx_63.2\case\ACC\src). When prompted choose to add the directory to the project.
You can modify the template to have the correct path to the source files. Open the c_cvidll.tpl in a text editor and locate the gen_project segment (@SEGMENT gen_project()@@). Modify the Include Path 1 to be the correct path:
Include Path 1 Is Rel = False
Include Path 1 = "/C/Program Files/National Instruments/matrixx/mx_63.2/case/ACC/src"
4. Calling the DLL in LabVIEW
Now that the DLL has been created the next step is calling it from LabVIEW. This will be done using a call library function node. There are two functions in the DLL that are used to interact with the model. The first function is InitModel which initializes the scheduler and subsystems. InitModel also returns the number of inputs and outputs that the model expects as well as the scheduler frequency. Once the model is initialized then the second function (TakeTimeStep) is called in a While Loop to have the model compute one time step of the scheduler. Both InitModel and TakeTimeStep use fixed function prototypes that do not depend on the model. Only the number of elements in the arrays Data_In and Data_Out change depending on the number of inputs and outputs of the model.
As a starting point use the attached VI template to run the model.
The block diagram is shown below.
Figure 1. VI Template Block Diagram
The following changes need to be made before running the model.
- Right-click the call-library function nodes and select Configure. In the dialog box click Browse and locate the DLL.
- Repeat for the second call-library function node.
LabVIEW offers a lot of flexibility for expanding the program. Instead of taking data from the user interface, the input could come from a file, a data acquisition card, a CAN board, or a variety of other sources.
5. Explanation of Implementation
The templates used to generate the DLL are based on the default templates that ship with MATRIXx. They use the same scheduler and can handle both multi-rate and hybrid models as well as models that have state-transition diagrams. This section describes the changes that were necessary to compile the source files into a DLL.
- Split the source code into a header file and a c source file. When LabWindows/CVI builds a DLL it exports the functions that have prototypes in the headers file.
- Compiler directives of #pragma pack(1) and #define MSWIN32 1 were added in the header file. #pragma pack(1) tells the compiler to align structures on 1 byte segments which is needed for compatibility with LabVIEW. #define MSWIN32 specifies Windows as the operating system.
- The function calls SCHEDULER_STATUS = External_Input(); and SCHEDULER_STATUS = External_Output (); were removed from the scheduler function. The DLL does not directly perform the external I/O. It receives the inputs from LabVIEW and then returns the outputs.
- The main function has been removed and three new functions were added:
- void InitModel(RT_INTEGER *NI, RT_INTEGER *NO, RT_FLOAT *SCHED_FREQ)
InitModel implements the initialization that was located in main. It initializes the scheduler and application data. It also returns the number of inputs (NI) and number of outputs (NO) and the scheduler frequency (SCHED_FREQ).
- RT_INTEGER TakeTimeStep(RT_FLOAT *Data_IN, RT_FLOAT *Data_OUT, RT_FLOAT *Time)
TakeTimeStep calls the scheduler function in order to advance the model one time step. In the original implementation the scheduler function was called by the functions in the sa_utils file. The external inputs are passed to TakeTimeStep as the Data_IN input. The external outputs that are calculated for the current time are returned in the Data_OUT array. The current simulation time is returned as Time. If there is an error TakeTimeStep will return a nonzero result. The error codes are defined in sa_defn.h.
- void Error()
Error is called by the Signal_An_Error macro. It sets the global variable error that is then returned by TakeTimeStep.
- void InitModel(RT_INTEGER *NI, RT_INTEGER *NO, RT_FLOAT *SCHED_FREQ)
For an explanation of the default AutoCode scheduler please see the AutoCode Users Guide.
Manuals: MATRIXx AutoCode User's Guide
6. Caveats and Limitations
There are two known limitations to this process. The execution time of the call to TakeTimeStep depends on the subsystems scheduled to execute during that minor cycle. The external outputs are not available until TakeTimeStep finishes.
TakeTimeStep has a variable execution time in multi-rate models.
The scheduler will schedule a subsystem based on its timing information. Take a simple model consisting of two discrete subsystems, A and B. A executes every 0.25 seconds and B executes every 0.5 seconds. The minor cycle in this case would be 0.25 seconds. At 0 seconds, 0.5 seconds, 1 second, both system A and B will be scheduled. At 0.25 seconds, 0.75 seconds, 1.25 seconds, only system A is scheduled. This is shown in the figure below
Figure 2: Scheduling of Two Subsystems
At time 0, 0.5, and 1 seconds the execution time of TakeTimeStep is the sum of the execution time of subsystem A and B. At time 0.25, 0.75 and 1.25 seconds the execution time of TakeTimeStep is only that of subsystem A. The maximum real-time loop rate is limited by the longest execution time. In this case the execution time at 0, 0.5, and 1 seconds.
External outputs are delayed by one minor cycle.
When TakeTimeStep is called, the external inputs for the current time (ti) are passed to the function. TakeTimeStep then determines the external outputs for the current time (ti) and then compute the model. The external outputs will not be available to LabVIEW until after TakeTimeStep finishes, which will introduce a delay in the output. For maximum determinism, the outputs and inputs are often hardware timed using the same scan/update clock. In this scenario the outputs would be loaded but not updated. The update would happen at the next rising edge of the update clock, which happens at the beginning of the next iteration. When using this technique the effect is that the outputs are updated one cycle later (ti+1). This could be accounted for in the model with a discrete delay of one minor cycle.
Using this process, SystemBuild models can be built into a DLL using AutoCode and LabWindows/CVI. The DLL can then be called from LabVIEW or any other programming language. LabVIEW allows easy access to a wide variety of I/O options. Deterministic real-time execution can be achieved by using LabVIEW Real-Time to embed the program on a PXI or FieldPoint Real-Time target.