Using RTI Connext DDS on the NVIDIA Jetson Nano

Preface: The vast majority of this article was extracted from a White Paper that RTI's Don Gochenour originally authored. Don is a Senior Application Engineer with extensive knowledge in embedded systems and RTI's products.  Don is located on the East coast of the US.

Introduction

This content piece illustrates how to cross-compile a basic DDS HelloWorld application on a Linux host and run it on the Jetson Nano embedded target.   

This will be illustrated on DDS Professional (6.0.1) and DDS Micro (3.0.2) --- the steps are very similar.

Content is broken into the following sections:

  • NVIDIA Jetson Nano

  • Preparing the Target

  • Preparing the Development Host

  • Using Connext DDS Professional 6.0.1

    • Installing the Target Libraries

    • Generating an Example Application and Type Support Code

    • Updating the Generated Makefile

    • Building the Example Application

  • Using Connext DDS Micro 3.0.2

    • Adding a Toolchain File

    • Building the Libraries

    • Generating an Example Application and Type Support Code

    • Building the Example Application 

Note:  It is also possible to build directly on the target rather than cross-compiling on a host and downloading to the target, as described in this article.  See the following community article for details:   How do I create a Connext DDS application with RTI Code Generator and build it on my ARM-embedded target?

NVIDIA Jetson Nano

The Jetson Nano Developer Kit is a development board based on a quad-core ARM Cortex-A57 (CPU) and an NVIDIA Maxwell with 128 NVIDIA CUDA cores (GPU).

While the CPU puts the Nano in a class similar to the Raspberry Pi 3B, the Maxwell GPU makes the Jetson a useful board to use in AI applications such as vision recognition.   

Preparing the Target

It is assumed you have programmed the Jetson with the standard Ubuntu 18.04 JetPack Image as described in the Jetson Getting Started Guides.  The steps should be the same as on other operating systems such as Raspian.

Preparing the Development Host

Because cross-development for the ARM architecture is usually easiest on Linux, this document discusses the steps required when working with an Ubuntu 18.04 host. 

Before any software--either applications or libraries--can be compiled for the Nano, we will need to install an appropriate ARM toolchain on the host. ARM itself maintains a site where you can download a GNU Toolchain for the A-profile Architecture

As mentioned previously, the Nano is based on the Cortex-A57, so at the time of this writing, the appropriate toolchain would be the gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz package. Download this package and extract it to the location of your choice, as shown below. In this document, we will assume /opt as the target location:

 $tar -xJf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt

Using Connext DDS Professional 6.0.1

 The following assumptions are made:

 Connext DDS Professional 6.0.1 is already installed on your host

  • The $NDDSHOME environment variable is set to your Connext DDS Professional 6.0.1 installation directory

  • $NDDSHOME/bin is included in your PATH

Installing the Target Libraries

With Connext DDS Professional 6.0.1 already on the host, the only additional RTI package that needs to be installed is a set of libraries appropriate for targets running Linux on ARMv8. While none of the standard, downloadable libraries were designed explicitly for the Jetson Nano, the target-armv8Linux4.4gcc5.4.0 libraries will work; the appropriate *.rtipkg file is available in the Download section of the RTI Support Portal - https://support.rti.com/.   In order to download RTI packages, you must have an account on the support portal and you must be licensed for the architecture of interest; otherwise the packages will not be visible to  you.

Generating an Example Application and Type Support Code

We will create an IDL and example application to demonstrate a working build. In this example, consider a simple IDL file named my_type.idl, with the following contents:

my_type.idl

const long MAX_PAYLOAD_SIZE = 128;
struct my_type { 
    long sample_id; 
    sequence<octet, MAX_PAYLOAD_SIZE> payload;
};

Type support code, example application source, and the Makefile necessary to build the example can be created with the following command. Note that this generated example uses C++11 and STL’s std::vector.

$ cd /path/to/your/project/dir
$ $NDDSHOME/bin/rtiddsgen -ppDisable -language C++11 -create typefiles -create examplefiles -create makefiles -platform armv8Linux4.4gcc5.4.0 -unboundedSupport -alwaysUseStdVector ./my_type.idl

Updating the Generated Makefile

After completing the previous step, the project directory is now populated with the source, header, and XML files, as well as a makefile named makefile_my_type_armv8Linux4.4gcc5.4.0. Opening this file in an editor, you will see a block of code similar to that shown below:

ifndef COMPILER 
COMPILER = g++
endif
COMPILER_FLAGS = -std=c++0x
ifndef LINKER 
LINKER = g++
endif

The COMPILER and LINKER variables need to be set to match the ARM toolchain binaries instead of the default gcc/g++ binaries on the host, which target the native architecture of the host. There are a few different ways to point Make to the proper compiler and linker: 

  • Pass the values of these variables on the command line when calling make.

  • Make sure that the desired compiler and linker is found first in your PATH environment variable.

  • Modify the makefile. 

In this case, we will choose #3: modify the makefile.

This choice is made based on the fact that we plan to build this application only for the armv8Linux4.4gcc5.4.0 architecture. Make the following change to the makefile:

ifndef COMPILER 
COMPILER = /opt/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++
endif
COMPILER_FLAGS = -std=c++0x
ifndef
LINKERLINKER = /opt/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++
endif

Additionally, the default linker arguments need to be changed. In previous versions of glibc, libnsl was provided as part of glibc itself, but this is no longer true in recent versions, such as the one used by the ARM toolchain in this document. In fact, Connext DDS has no dependency on this library and enhancement request PLATFORMS-1764 exists to track the removal of the -lnsl reference from the generated makefiles. In the current release, we can fix this manually in makefile_my_type_armv8Linux4.4gcc5.4.0 by simply making the following change:

From this:

SYSLIBS = -lnsl -ldl -lm -lpthread -lrt

To this:

SYSLIBS = -ldl -lm -lpthread -lrt

Building the Example Application

From your project directory, call make as shown below:

$ make -f makefile_my_type_armv8Linux4.4gcc5.4.0

The cross-compiled my_type_publisher and my_type_subscriber files can now be moved to the target and run. 

Using Connext DDS Micro 3.0.2

The Micro product is delivered as source, thus the product libraries must be built before any user application can link against them. Micro supports CMake builds, and also supports targeting Linux in general, so making the leap to Linux on the Cortex-A57 is painless. In this document, the name of the architecture (are libraries) will be armv8Linux4gcc921

The following assumptions are made:

  • Connext DDS Micro 3.0.2 is already installed on your host

  • CMake® is installed on your host and is in your $PATH

  • The $RTIMEHOME environment variable is set to your Micro 3.0.2 installation directory

Adding a Toolchain File 

Out of the box, the Micro 3.0.2 release supports building for a number of CPU/OS combinations under CMake, but Linux on armv8 (the underlying architecture of the A57) is not one of them. This can be fixed by creating and adding a single file to the Micro installation.  

In the $RTIMEHOME/resource/cmake/architectures directory you will see a number of *.tc files; these are CMake toolchain files that let the build system know where to find the appropriate C and C++ compilers for a given architecture (among other things.) Because it doesn’t exist yet, we need to create a file in this directory called armv8Linux4gcc921.tc. Create that file and give it the following contents:

armv8Linux4gcc921.tc

INCLUDE(CMakeForceCompiler)
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_PROCESSOR arm)
SET(COMPILER_PATH /opt/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin)
SET(CMAKE_CXX_COMPILER ${COMPILER_PATH}/aarch64-none-linux-gnu-g++)
SET(CMAKE_C_COMPILER &nbsp; ${COMPILER_PATH}/aarch64-none-linux-gnu-gcc)&nbsp;
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/Platform/linux.tc)
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/Compiler/gcc.tc)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

 

Building the Libraries

With the newly created toolchain file in place, building the Micro platform libraries is simple. Use the following three commands: (1) change to the Micro installation directory, (2) build the Debug version of the libraries, and (3) build the Release libraries.

$ cd $RTIMEHOME
$ ./resource/scripts/rtime-make --target armv8Linux4gcc921 --name armv8Linux4gcc921 -G "Unix Makefiles" --build --config Debug
$ ./resource/scripts/rtime-make --target armv8Linux4gcc921 --name armv8Linux4gcc921 -G "Unix Makefiles" --build --config Release

Generating an Example Application and Type Support Code

We will create an IDL and an example application to demonstrate a working build. In this example, consider a simple IDL file named my_type.idl and with the following contents:

my_type.idl

const long MAX_PAYLOAD_SIZE = 128;
struct my_type {&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp; &nbsp; long sample_id;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp; &nbsp; sequence&lt;octet, MAX_PAYLOAD_SIZE&gt; payload;
};

Type support code, example application source, and the CMake files necessary to build the example can be created with the following command. Note that this example uses C++, with namespaces.

$ cd /path/to/your/project/dir

$ $RTIMEHOME/rtiddsgen/scripts/rtiddsgen -micro -language C++ -namespace -create typefiles -create examplefiles -create makefiles ./my_type.idl

Building the Example Application

The previous step--generating the source code and CMake files--did not create anything that is platform specific. Now that those files exist, we can build the user project, specifying the architecture for which we want to target (armv8Linux4gcc921 in this case.)

The following command builds an executable linked against the Release configuration of the Micro libraries:

$ $RTIMEHOME/resource/scripts/rtime-make --config Release --build --name armv8Linux4gcc921 --target armv8Linux4gcc921 --source-dir . -G "Unix Makefiles" --delete

Inspecting one of the executables produced confirms that it is intended for the correct architecture:

$ file objs/armv8Linux4gcc921/my_type_publisher&nbsp;
my_type_publisher: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.7.0, with debug_info, not stripped

 The cross-compiled binaries my_type_publisher and my_type_subscriber files can now be moved to the target and executed.

Product:
Platform: