on 09-29-2010 04:01 PM
NI realtime targets running VxWorks, which include PowerPC versions of single-board RIO (sbRIO) and CompactRIO (cRIO), may be programmed in C, C++, and Java. This guide details two independent ways of compiling C/C++ code for your target, uploading the compiled binary, and invoking the binary through a shell, by using the Call Library Function Node within LabVIEW RT, or configuring the controller to execute the binary as a standalone process at startup.
See the DevZone article What Operating System is my Real-Time Controller Running and Why? to determine if your controller is a VxWorks target.
See the downloads section for a tool that will automatically configure your target to work with the methods described here.
Recommended:
Wind River Workbench 3.0.1 or later with VxWorks 6.x configuration
Minimum requirements:
Wind River Workbench 2.3 or later with VxWorks 6.x configuration
Additional minimum requirements for integrating custom binaries with LabVIEW Real-Time applications:
Additional minimum requirements for integrating custom binaries with LabVIEW FPGA VIs:
PowerPC versions of sbRIO, cRIO, and NI 17xx Smart Camera run the Wind River VxWorks real-time operating system. Binaries can be created by any toolchain that compiles to the PowerPC PPC603 architecture and links against standard VxWorks libraries. We demonstrate two such toolchains: the open-source compiler gcc, and the commercially available Wind River Workbench.
| gcc | Wind River Workbench | |
|---|---|---|
| Cost | free | annual license (academic discounts available) |
| Development environment | command line | Eclipse |
| Debugging | console only | advanced debugging and tracing |
| Target support | makefile | integrated into project |
National Instruments recommends using Wind River Workbench for developing binaries for VxWorks, since it provides advanced debugging capabilities, a full-featured development environment, and technical support.
Make sure your compile toolchain links against the correct VxWorks library version. For targets that have been programmed with LabVIEW Real-Time 8.5 through 2011, VxWorks version 6.3 has been installed; for targets programmed with LabVIEW Real-Time 8.2.x, VxWorks version 6.1 has been installed. If your target has never been programmed by LabVIEW Real-Time, then use the VxWorks Kernel Shell (see below) to view boot messages when the target is booted to determine which version of LabVIEW Real-Time was installed during manufacturing.
C code may be generated from standard LabVIEW VIs using the LabVIEW C Code Generator. See LabVIEW 2010 C Generator Help for more information.
Choose the guide according to the toolchain you are using:
The Definitive Guide: Programming NI VxWorks Real-Time Controllers in C/C++ (gcc Method)
The Definitive Guide: Programming NI VxWorks Real-Time Controllers in C/C++ (Workbench Method)
Upload your compiled binary to the target via FTP. Binaries that are to be called from within LabVIEW Real-Time using the Call Library Function Node (see below) should be located in the ni-rt/system directory on the target.
The VxWorks Kernel Shell is a task running on the target that receives commands from a host and displays debug messages over a standard RS-232 serial port. This is an optional step, and not required to program your target, however we strongly recommend connecting to the VxWorks Kernel Shell to display debug messages.
A standard null modem RS-232 serial cable or a USB to RS-232 cable can connect your machine to the target console port; standard serial port settings are 9600 baud, 1 start bit, 8 data bits, 0 stop bits, no parity, and no flow control. Consult your product documentation for instructions to enable and connect to the serial console on your target.
We take the following steps to connect to the serial console. Connecting to your target may be slightly different, and we encourage you to follow your product documentation.
As soon as the device restarts, startup messages are displayed in the console. When the startup is complete, you may send commands to the target.

Figure 3a: Startup messages when using the VxWorks Kernel Shell.
Visit the references section for several straightforward guides to using the VxWorks Kernel Shell.
There are three methods for executing your binary library; the first is to manually load your library using the VxWorks Kernel Shell; the second is the Call Library Function Node, which integrates your library into LabVIEW Real-Time VIs; the third is to use your library as a standalone executable, which bypasses LabVIEW Real-Time and executes at the VxWorks operating system level. Only one execution method should be used at a time.
4.1.1 Loading a Library
The VxWorks Kernel Shell may be used to manually load libraries into memory. Loading a library verifies the library has been compiled correctly, and makes it available to other modules. This is useful if you receive an error that a library could not be found or loaded, since the shell may produce more verbose errors that can help you track down an issue. To load a library, use the ld command with file indirection. Make sure you are loading from the current directory if you use a relative path to the library.

Figure 4.1.1a: Successful loading of a library in the VxWorks Kernel Shell.
If the library is not found, or is incorrectly compiled, errors are displayed and the library is not loaded. Common errors include duplicate symbols, or omitting the -mlongcall compile toolchain flag.

Figure 4.1.1b: Unsuccessful loading of a library in the VxWorks Kernel Shell. The error reflects the omission of the -mlongcall compile toolchain flag.
4.1.2 Symbol Lookup and Execution
Once a library is loaded into memory, it's symbols are in a global symbol space. The address of each symbol can be found with the lkup command. This verifies a library has been loaded and has exported global symbols.

Figure 4.1.2a: Symbol lookup of the functions "fpgaInterfaceInit" and "blink" in the loaded library exampleFpga.out.
Symbols are executed in the shell by simply typing the symbol name; alternatively, they may be spawned as a process using the sp command.

Figure 4.1.2b: Spawning of symbols loaded into memory.
In the above figure, an initialization function "fpgaInterfaceInit" is called, followed by the function "blink", which flips the state of an FPGA LED. No errors occur (errorno=0), and each function returns 0 (status=0) which we have used to indicate the functions complete successfully. Arguments can be passed to spawned functions; consult the references section for more details.
Your binary application may be executed by a LabVIEW Real-Time application. This is equivalent to calling a Dynamic Link Library (DLL) on a Windows machine, or a shared object (SO) library on a UNIX-based system. The Call Library Function Node is the easiest way to deploy C/C++ code to a LabVIEW Real-Time target, and since your code is executed within a VI, you can integrate your code with LabVIEW Real-Time VIs.
Visit LabVIEW 2010 online help to learn more: Configuring the Call Library Function Node.
4.2.1 Differences from Call Library Function Node on a Desktop Target
4.2.2 Compile and Upload
Follow your toolchain compile guide to compile the following C code into the binary library file fib.out (part of the Fib project).
Transfer the library to the ni-rt/system folder on the target via FTP.
4.2.3 Invoke with the Call Library Function Node
The Call Library Function Node is available in the LabVIEW Real-Time palette under Connectivity -> Libraries and Executables -> Call Library Function Node. We build the following application that invokes our two exposed library functions:

Figure 4.2.3a: Fib.vi (part of the Fib project)
Below is the Call Library Function Node configuration dialog for fib.out:fib; the other configuration is similar, though the function prototype will be different.

Figure 4.2.3b: Call Library Function Node configuration dialog in Fib.vi (part of the Fib project).
Running the VI while connected to the target allows the user to request an element from the sequence of Fibonacci numbers and display it on the host PC.
4.2.4 Resolve Load Errors
If a library cannot be loaded, LabVIEW Real-Time will report an error similar to the figure below:

Figure 4.2.4a: LabVIEW RT deployment errors when a library cannot be loaded.
This error may occur when a library cannot be found in the ni-rt/system directory on the target, if the library is not executable, or if the library was not compiled correctly. In this case, the library was not compiled with the -mlongcall compile toolchain flag.
To resolve load issues, load the library in the VxWorks Kernel Shell to produce more verbose errors. See the section Executing via VxWorks Kernel Shell for instructions. Errors returned by the LabVIEW FPGA C Interface API are documented in the generated header files.
The target can be configured to load a custom binary at startup; since this runs at the VxWorks operating system level, it will not have access to any of the functionality of LabVIEW. We recommend using the VxWorks Kernel Shell or LabVIEW Call Library Function Node to initially test and debug your application. When your application is complete and debugged, you can set up the system to run it on startup.
We mentioned that DllMain() (or a similar default entry point) is not executed upon loading a library in VxWorks; however, a constructor may be declared using the _WRS_CONSTRUCTOR preprocessor definition, which defines a function to be an entry point with a fixed priority:
/* library constructor */
_WRS_CONSTRUCTOR(fibConstructor, 110){
/* library constructor code goes here. */
return; /* void */
}
Modify /ni-rt.ini by appending to StartupDLLs under the [LVRT] section. For our example project exampleFpga, we change
StartupDLLs=nisysapirpc.out;[...];sysstatepublishe
to
StartupDLLs=nisysapirpc.out;[...];sysstatepublishe
so that exampleFpga.out is loaded at startup.
The C Interface to LabVIEW FPGA allows C/C++ applications to interact directly with compiled LabVIEW FPGA VIs on RIO devices. The interface includes functionality for downloading a VI to a RIO target, performing DMA data transfers, waiting on and acknowledging interrupts, and reading and writing named controls and indicators using C function calls. The FPGA Interface C API is a free download:
FPGA Interface C API 1.3 - LabVIEW 2009 SP1 through 2011
FPGA Interface C API 1.0 (deprecated) - LabVIEW 8.6 only
Visit FPGA Interface C API Help 1.3 for more information on using the C API.
Note: The FPGA Interface C API references <stdint.h>, which is not in all Workbench distributions, or in the gcc distribution for VxWorks 6.1. Download stdint.h for VxWorks 6.x and save it to your toolchain installation in vxworks-6.x\target\h .
5.2 Compile and Run the FPGA VI
We use the exampleFpga project for this section. The FPGA VI (below) outputs the chassis temperature to an indicator, and sets the FPGA LED state by reading a control.

Figure 5.2a: fpgaLabview.vi (part of the exampleFpga project)
Compile and run the FPGA VI. This will verify the VI can be synthesized to FPGA without errors, and produces an FPGA bitfile to be referenced by the C Code Generator. Changing the FPGA VI requires regenerating code to work with the new bitfile.
5.3.1 Generate C Code with the C API Generator
Right click on the FPGA VI in your project and select, Launch C API Generator.

Figure 5.3.1a: Accessing the C API Generator for LabVIEW FPGA
By default, the C API Generator points to the bitfile generated by LabVIEW FPGA for you VI. Press Generate to synthesize C code. The generated .c and .h files may now be included in your C project and referenced from within your source. Copy these files, along with the FPGA bitfile, to your project directory.

Figure 5.3.1b: Generated files from LabVIEW FPGA and FPGA C Interface API.
5.3.2 Set the Path to the FPGA Bitfile
The FPGA bitfile is opened by the C API, and must be present in the target. If the function that loads this bitfile is called from LabVIEW, then the bitfile must be located in the /ni-rt/system/ directory; we suggest saving the bitfile to this directory in all cases.First, edit the .h file generated for the top-level VI, in our case, NiFpga_fpgaLabview.h, by prepending the /ni-rt/system directory to the bitfile path:
#define NiFpga_fpgaLabview_Bitfile "/ni-rt/system/NiFpga_fpgaLabview.lvbitx"
Use FTP to transfer the FPGA bitfile to the /ni-rt/system directory. Your generated code now points to the correct location of its associated bitfile. If the function that loads this bitfile is executed from the VxWorks Kernel Shell, make sure the present working directory is /c/, otherwise the path is not properly appended by the above definition. If you reprogram the FPGA, you will need to regenerate code, set the path to the FPGA bitfile, and update the bitfile on the target.
5.4.1 Initialize, Read Indicators, and Write Controls
You may now reference the FPGA C API as outlined in the DevZone article, Building a R Series FPGA Interface Host Application in C. Please note that the both the host and target are different, LabWindows CVI and an R-Series PCI FPGA, respectively; however, the C API calls remain the same. The control and indicator variable names for a VI are defined in its .h file.
For the exampleFpga project, we write the following C application:
#include "NiFpga_fpgaLabview.h"
static NiFpga_Session fpgaSession;
/* Initialize the FPGA. This assumes the FPGA bitfile
* has been loaded to the FPGA. Note that the FPGA
* does not retain its bitfile across power cycles. */
extern int32_t fpgaInterfaceInit(void){
NiFpga_Status status = NiFpga_Initialize();
if(NiFpga_IsNotError(status)){
NiFpga_MergeStatus(
&status,
NiFpga_Open(
NiFpga_fpgaLabview_Bitfile,
NiFpga_fpgaLabview_Signature,
"RIO0",
0,
&fpgaSession
)
);
}
return status;
}
/* reads the chassis temperature and converts to Celcius
* Temperature in C = Binary Value / 4 */
extern int32_t temperature(double * const pCelciusTemp){
NiFpga_Status status;
int16_t binaryTemp;
status = NiFpga_ReadI16(fpgaSession, NiFpga_fpgaLabview_IndicatorI16_ChassisTemperature
*pCelciusTemp = binaryTemp / 4.0;
return status;
}
/* toggles the state of the FPGA LED */
extern int32_t blink(void){
static NiFpga_Bool ledState = 0;
NiFpga_Status status;
status = NiFpga_WriteBool(fpgaSession, NiFpga_fpgaLabview_ControlBool_FPGALED, ledState);
ledState = ~ledState;
return status;
}
These functions can be called by a standalone library, or from LabVIEW Real-Time (below).

Figure 5.4.1a: rvLabview.vi (part of the exampleFpga project).
Upon executing the Real-TimeT VI, the library initialization function is called, then each parallel loop calls a C function that reads or writes to the FPGA using the C FPGA interface.
Functions in the FPGA Interface C API return a status indicating error or success of an operation. A value of 0 indicates success, a negative value indicates an error. The specific error messages may be found in the generated file NiFpga.h. Follow the practice of returning the status of any FPGA Interface C API call to detect errors at runtime and display the error value. The exampleFpga project follows this practice.
Bitfiles downloaded to an FPGA are not retained across power cycles; if the device loses power, the bitfile must be downloaded again. This can be done programatically from LabVIEW or C using the FPGA Interface C API. Review the LabVIEW FPGA 2010 Help or header files generated by the C API Generator for more information.
A processor exception is treated by RIO devices as a fault case, and the device will restart. While this is the preferred behavior for deployed applications, we need to change this behavior during the development period, since debug breakpoints work by triggering processor exceptions. Using FTP, modify the target file /ni-rt.ini by adding the following setting:
[debug]
InstallExceptionHandlers=false
This setting prevents the device from restarting when an exception occurs.
Choose the guide according to the toolchain you are using:
The Definitive Guide: Programming NI VxWorks Real-Time Controllers in C/C++ (gcc Method)
The Definitive Guide: Programming NI VxWorks Real-Time Controllers in C/C++ (Workbench Method)
Tool to Configure NI VxWorks Target for C Programming. Enables console output, disables CPU exception handlers, and ensures debuggingInit() is called at startup.
National Instruments drivers
gcc Compiler
FPGA Interface C API
Example Projects
Other Tools
LabVIEW Real-Time, "Developing Shared Libraries for the cRIO-901x and Other VxWorks Targets", National Instruments, August 2010. Available: http://zone.ni.com/devzone/cda/tut/p/id/5694. [Accessed November 24th, 2010].
This is the primary reference for this guide. It was written for LabVIEW 8.5 (and updated for LabVIEW 2009), and we hope to have faithfully and succinctly represented its content here.
LabVIEW Real-Time, "Developing with the C Interface to LabVIEW FPGA for VxWorks Targets", National Instruments, July 2009. Available: http://zone.ni.com/devzone/cda/tut/p/id/9317. [Accessed November 24th, 2010].
LabVIEW Real-Time, "Calling External Code on VxWorks Targets", National Instruments, November 2006. Available: http://digital.ni.com/public.nsf/websearch/5A267EF
LabVIEW FPGA, "FPGA C Interface API Help 1.3", National Instruments, December 2010. Available:http://zone.ni.com/reference/en-XX/help/372928D-01
LabVIEW FPGA, "Building a R Series FPGA Interface Host Application in C", National Instruments, August 2010. Available: http://zone.ni.com/devzone/cda/tut/p/id/8638. [Accessed November 24th, 2010].
NI Labs, "C Interface to LabVIEW FPGA" forum, National Instruments, January 2009. Available: http://forums.ni.com/t5/NI-Labs/C-Interface-to-Lab
Collider Detector at Fermilab, "VxWorks Tutorial", Fermilab, 2002. Available: http://www-cdfonline.fnal.gov/daq/computing/vxwork
James Harden, "ECE 4733 Lab 3 - VxWorks Familiarization", Mississippi State University, Department of ECE. September, 2009. Available: http://www.ece.msstate.edu/~harden/ECE4733/labs/la
Chethan Parameswariah, "VxWorks Command Cheat Sheet", Ligo Livingston Observatory, January 2004. Available: http://touro.ligo-la.caltech.edu/~cparames/CDS/vxW
This tutorial was developed by National Instruments ("NI"). Although technical support of this tutorial may be made available by National Instruments, the content in this tutorial may not be completely tested and verified, and NI does not guarantee its quality in any way or that NI will continue to support this content with each new revision of related products and drivers. THIS TUTORIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND AND SUBJECT TO CERTAIN RESTRICTIONS AS MORE SPECIFICALLY SET FORTH IN NI.COM'S TERMS OF USE (http://ni.com/legal/termsofuse/unitedstates/us/). If anything melts, it's not our fault.
on 03-17-2011 07:48 PM
on 03-18-2011 10:05 AM
THanks, Peter! Unfortunately I do not have much information about our Pharlab systems, though CVI should interface with them. I would be interested in learning more about your use case.
on 06-04-2012 08:12 PM
This article was much needed. Thanks for creating it elgeeko.
One thing I notice is the "FLEXlm License Finder" dialog kept popping up when building the "fib" example. Turns out the "setup-gcc.bat" is calling:
"set LM_LICENSE_FILE=%GCCPATH%\supp\zwrsLicense.lic"
However the actual path is %GCCPATH%\supplemental\zwrsLicense.lic
I just renamed the "supplemental" directory to "supp" and the issue is fixed.
This is the case in the gcc compiler 3.4.4 with VxWorks 6.3 distribution. I didn't check the 6.1 distribution.
Thanks again.
on 06-05-2012 10:44 AM
Thanks Holt, I'm glad this was of use! You are exactly right as to the bug with the setup script. I need to update the file online, but your solution should work in the interim.
I'm curious to know, what is your application?
on 01-24-2013 09:30 AM
Great article, I have already tried this and works perfectly, are you tried this in the new Smart Cameras or cRIO with the processor Intel Atom?
on 01-24-2013 12:06 PM
Hi Emmanuelol, thanks! I don't have instructions for programming our Phar Lap ETS (x86) embedded targets, in fact I have not done it myself. My understanding is that Phar Lab ETS is binary compatible with win32 binaries; I suggest you try building a simple program on a windows desktop, compile with gcc, FTP the DLL to the target, and load the library using the Call Library Function Node in LabVIEW. Good luck!
on 01-31-2013 01:07 AM
Hi elgeeko,
This is really a good article for developing a shared library for device with VxWorks.
I tried to build a library with regular expression functions and use it on sbRIO-9626 with LabVIEW 2011 SP1 but it returned an error.
Did you have experience to build library with regular expression functions?
on 01-31-2013 09:45 AM
Hi Tony, sorry, I do not have experience with regular expression libraries.
on 02-13-2013 03:22 AM
Great article.
I am interested on implementation of Real time workshops C generated code in a SB RIO 9631. At the moment, I have the error on labview and this afternoon i will try to connect to the kernel via RS232 in order to diagnose. If anybody has done this yet or is interested, please contact me and maybe we can help us together.