Skip navigation

Community

Currently Being Moderated

The Definitive Guide: Programming NI VxWorks Real-Time Controllers in C/C++

VERSION 19  Click to view document history
Created on: Sep 29, 2010 4:01 PM by elgeeko - Last Modified:  Sep 2, 2012 1:31 AM by elgeeko

1. Introduction

1.1 Abstract

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.

 

1.2 Requirements

Recommended:

 

Minimum requirements:

  • Windows XP SP2
  • NI sbRIO, NI cRIO, or NI 17xx Smart Camera; PowerPC based
  • NI-RIO software
  • C/C++ development toolchain; one of the following:
    • gcc compiler 3.4.4 with VxWorks 6.3 distribution (targets running LabVIEW Real-Time 8.5 through 2011) or gcc compiler 3.3.2 with VxWorks 6.1 distribution (deprecated; targets running LabVIEW Real-Time versions 8.2.x). Note: gcc compiler 3.3.2 with VxWorks 6.1 distribution is not compatible with Windows Vista or Windows 7.
    • Wind River Workbench 2.3 or later with VxWorks 6.x configuration

    • Custom toolchain capable of producing PowerPC PPC603 binary executables. This option is for experts only and is not documented here.

 

Additional minimum requirements for integrating custom binaries with LabVIEW Real-Time applications:

    • LabVIEW 8.2.0 or later
    • LabVIEW Real-Time Module 8.2.0 or later.

 

Additional minimum requirements for integrating custom binaries with LabVIEW FPGA VIs:

 

1.3 Toolchains

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.

 


gccWind River Workbench
Costfreeannual license (academic discounts available)
Development environmentcommand lineEclipse
Debuggingconsole onlyadvanced debugging and tracing
Target supportmakefileintegrated 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.

 

2. Compile

2.1 General Compile Guidelines
  • Use unique function and global variable names. Global symbols, such as function names and global variables, are loaded into a single symbol table in VxWorks that is shared by all open libraries. If a symbol is replicated within a single library, or across two seperately loaded libraries, unexpected behavior may occur. Limit the scope of global symbols by making them local, using the "static" keyword, or prefixing global symbol names with a library-specific prefix. Compile warnings may indicate the presence of duplicate symbols. When loading the library from the serial console, a warning will indicate the presence of duplicate symbols.
  • Use long (32-bit) symbol addresses. Most compile toolchains enable this by using the -mlongcall compile flag. This flag will prevent an error that may occur when loading your library. The default compiler settings reference memory in a way that is compatible with legacy PowerPC architectures, but is insufficient for targets with large amounts of memory. This flag allows function calls across libraries that are separated by more than 32MB of memory; if your code references any VxWorks or other library functions, and this flag is not set, the library will likely fail to load. If you are connected to the VxWorks console (see below) and this error occurs, it will print "Relocation value does not fit in 24 bits".
2.2 Compile an Executable Library

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)

 

2.2 Upload to the Target

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.

 

3. Connect to the VxWorks Kernel Shell

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.

 

    1. Turn serial console switch to ON.
    2. Connect an RS-232 null modem cable from the target to the computer.
    3. On the computer, open terminal interface software, and configure the port for 9600 baud, 1 start bit, 8 data bits, 0 stop bits, no parity, and no flow control.
    4. Restart the target by cycling power or soft-booting from NI-MAX.

 

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.

 

consoleInit.jpg

Figure 3a: Startup messages when using the VxWorks Kernel Shell.

 

Visit the references section for several straightforward guides to using the VxWorks Kernel Shell.

 

4. Execute

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 Executing via VxWorks Kernel Shell

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.

 

ldErrorSuccess.jpg

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.

 

ldErrorConsole.jpg

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.

 

consoleLkup.jpg

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.

 

consoleSpawn.jpg

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.

 

4.2 Executing via Call Library Function Node

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

  • Paths specified in the Call Library Function Node configuration dialog are ignored. All libraries must be located in the ni-rt/system directory.
  • The Calling Convention parameter in the Call Library Function Node is ignored. All functions called from LabVIEW must follow the C calling convention.
  • Library symbols are not parsed by LabVIEW. You must manually configure the arguments and return type of the Call Library Function Node, and they must exactly match the function prototype.
  • DllMain() is not called on library instantiation. One workaround is to use C-style global constructors (this is an advanced technique; see the Standalone Method below for details). Proper initilization is covered in the VxWorks Application Programmer's Guide 6.3.

 

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).

 

#include <math.h>
#include <types/vxTypes.h>

/* Binet's formula for the nth element of the Fibonacci sequence */
#define goldenRatio ((1 + sqrt(5)) / 2)
extern uint32_t fib(const uint32_t n){
return (pow(goldenRatio, n) - pow(1 - goldenRatio, n)) / sqrt(5);
}

/* Returns the next element of the Fibonacci sequence (stateful) */
extern uint32_t nextFib(void){
static uint32_t F1 = 0;
static uint32_t F2 = 1;
const uint32_t F0 = F1;
F1 = F2;
F2 += F0; /* F[2] = F[1] + F[0] */
return F0;
}

 

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:

 

call library node.jpg

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.

 

call library node config.jpg

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:

 

ldErrorLabview.jpg

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.

 

4.3 Standalone Method

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.

 

4.3.1 Create an Entry Point

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 */

}

 

 

4.3.2 Add Library to System Startup

Modify /ni-rt.ini by appending to StartupDLLs under the [LVRT] section. For our example project exampleFpga, we change

 

StartupDLLs=nisysapirpc.out;[...];sysstatepublisher.out;

 

to

 

StartupDLLs=nisysapirpc.out;[...];sysstatepublisher.out;exampleFpga.out;

 

so that exampleFpga.out is loaded at startup.

 

5. FPGA Interface C API

5.1 Introduction to the FPGA Interface C API

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.

 

fpga vi.jpg

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 Generate Code and Update 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.

 

launch C API.jpg

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.

 

generated code.jpg

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 Reference the C API

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, &binaryTemp);

*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).

 

blinkyRt.jpg

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.

 

5.4.2 Trace Errors with Return Status

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.

 

5.5 Automatically Download the FPGA Bitfile

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.

 

6. Debug

6.1 Disable CPU Exception Handler

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.

 

6.2 Debug Toolchain

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)

 

7. Downloads

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

  • exampleFpga.zip - C code and LabVIEW project files for the exampleFpga project. The project demonstrates the Call Library Function method to invoke a C library that interfaces with the FPGA.
  • Fib.zip - C code and LabVIEW project files for the Fib project. The project demonstrates the Call Library Function method to invoke a C library. The FPGA is ignored.

 

Other Tools

  • PuTTY - a free Telnet client, used to connect to the serial console.

 

8. References

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/5A267EF5471758CA86257233006EF3D8. [Accessed November 24th, 2010].

 

LabVIEW FPGA, "FPGA C Interface API Help 1.3", National Instruments, December 2010. Available:http://zone.ni.com/reference/en-XX/help/372928D-01/capi/examples/.

 

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-LabVIEW-FPGA/m-p/830645. [Accessed November 24th, 2010].

 

Collider Detector at Fermilab, "VxWorks Tutorial", Fermilab, 2002. Available: http://www-cdfonline.fnal.gov/daq/computing/vxworks/tutorial.html. [Accessed November 24th, 2010].

 

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/lab3.pdf. [Accessed November 24th, 2010].

 

Chethan Parameswariah, "VxWorks Command Cheat Sheet", Ligo Livingston Observatory, January 2004. Available: http://touro.ligo-la.caltech.edu/~cparames/CDS/vxWorks_commands.html. [Accessed November 24th, 2010].

 

9. Version History

  • 2010-11-29 Initial release. Author: Jeff C. Jensen.
  • 2010-12-20 Clarified requirements, noted issue with gcc 3.3.2 and Windows Vista / 7.
  • 2010-12-30 Updated for FPGA Interface C API 1.3 and completed Standalone Method section.
  • 2011-02-09 Updated with new library constructor method; updated stdint.h with preprocessor directives to avoid redefinitions; corrected incorrect directory in gcc setup that resulted in the Wind River license file having to be manually located for each compilation. Updated makefiles in all projects for multiple file compilation, and optional automatic transfer to the target via FTP.
  • 2011-11-17 Tested and revised for LabVIEW 2011 and NI-RIO 4.0.
  • 2012-06-25 Added tool to automatically configure a target for C programming.

 

10. Legal Disclaimer

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.

Attachments:
Comments (12)