Cross-compilation

6 minute read

Lely CANopen can be cross-compiled for a variety of target architectures and operating systems. This page documents the process for the supported targets. Other targets may also work, but they are not tested in our CI/CD pipeline.

MinGW on Linux

Requirements

To cross-compile Lely CANopen for Windows under Linux, you first need to install the dependencies required for native builds.

MinGW

We will use Mingw-w64 instead of the original MinGW. It is available in the repositories of most Linux distributions. If not, download it from http://mingw-w64.org/doku.php/download.

On Debian/Ubuntu-based distributions, you can install the compiler with

sudo apt-get install mingw-w64

Wine

To run the Windows tests under Linux, you need to install Wine 3.18 or newer.

Wine 3.17 and older do not implement the GetQueuedCompletionStatusEx() function, which is used by the asynchronous I/O library.

To install Wine, follow the instructions at https://wiki.winehq.org/Download. In case of Ubuntu 18.04 LTS:

curl -L https://dl.winehq.org/wine-builds/winehq.key | sudo apt-key add -
sudo apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main'
curl -L https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/xUbuntu_18.04/Release.key | sudo apt-key add -
sudo apt-add-repository 'deb https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/xUbuntu_18.04/ ./'
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install winehq-stable

The OBS (openSUSE Build Service) repository contains libfaudio0, which is a dependency of Wine 4.5 and later.

Initialize your local Wine environment with

wineboot -i

Wine needs to be able to find some runtime DLLs from MinGW. Add their locations to WINEPATH with

export WINEPATH=Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\7.3-posix\;Z:\\usr\\i686-w64-mingw32\\lib

for 32-bit Windows or

export WINEPATH=Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\7.3-posix\;Z:\\usr\\x86_64-w64-mingw32\\lib

for 64-bit Windows. For convenience, you can add one of these lines to your ~/.bashrc.

Note: The GCC version number (7.3) may be different on your platform. Change it accordingly.

IXXAT VCI

Lely CANopen supports IXXAT CAN controllers on Windows via the Virtual Communication Interface (VCI).

To build the stack with IXXAT support, you need the VCI SDK. Download and install the VCI V4 Driver on a Windows machine, and copy the C:\Program Files\HMS\IXXAT VCI 4.0\sdk\vci folder to the Linux machine on which you will cross-compile the stack.

Note: Make sure the C SDK is selected during install.

At runtime, the stack will try to load vcinpl2.dll (or vcinpl.dll if CAN FD support is disabled).

Build instructions

As with native builds, clone the repository and prepare the build environment:

git clone https://gitlab.com/lely_industries/lely-core.git
cd lely-core
autoreconf -i
mkdir -p build
cd build

Configure the project with

../configure --host=i686-w64-mingw32 --disable-python

for 32-bit Windows, or

../configure --host=x86_64-w64-mingw32 --disable-python

for 64-bit Windows.

--disable-python implies --disable-cython. Additionally, it disables the DCF tools.

If you have the IXXAT VCI SDK installed, add --with-ixxat=DIR to configure; it will look for vcinpl2.h (or vcinpl.h if CAN FD support is disabled) under DIR/inc.

DIR is the copy of C:\Program Files\HMS\IXXAT VCI 4.0\sdk\vci.

Build the libraries and tools with

make

If you have Wine installed, you can run the test suite with

make check

Although it does not make much sense to install the Windows tools and libraries on Linux, it is useful to run make install to collect the build artifacts:

make install DESTDIR=$(pwd)/../install

You will find the tools and libraries in ../install/usr/local/bin and the headers in ../install/usr/local/include.

Note: When you copy the artifacts to your target platform, make sure to also include libwinpthread-1.dll from /usr/i686-w64-mingw32/lib (or /usr/x86_64-w64-mingw32/lib) and libgcc_s_seh-1.dll and libstdc++-6.dll from /usr/lib/gcc/i686-w64-mingw32/7.3-posix (or /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix).

ARM on Linux

Cross-compiling Lely CANopen for ARM systems running Linux, such as a Raspberry Pi, is quite straightforward.

Requirements

First, install the dependencies required for native builds. Then, install the cross-compiler and binutils for the desired target triplet:

Target triplet ARM architecture
arm-linux-gnueabi 32-bit ARMv6, ARMv5 or ARMv4T
arm-linux-gnueabihf 32-bit ARMv7, hard-float
armv8l-linux-gnueabihf 32-bit ARMv8, hard-float
aarch64-linux-gnu 64-bit ARMv8

If your distribution does not provide the required cross-compiler, you can download the toolchain from Linaro.

On Debian/Ubuntu-based systems, run

sudo apt-get install crossbuild-essential-armel

for arm-linux-gnueabi,

sudo apt-get install crossbuild-essential-armhf

for arm-linux-gnueabihf, or

sudo apt-get install crossbuild-essential-arm64

for aarch64-linux-gnu.

If you want to run the tests, you’ll also need to install the QEMU user-mode emulation tools.

On Debian/Ubuntu, run

sudo apt-get install qemu-user

or

sudo apt-get install qemu-user-static

Build instructions

The steps for cloning the repository and preparing the build environment are the same as for native builds:

git clone https://gitlab.com/lely_industries/lely-core.git
cd lely-core
autoreconf -i
mkdir -p build
cd build

Configure the project for the desired target architecture with

../configure --host=HOST --disable-python

where HOST is the target triplet.

To prevent overwriting the native libraries during make install, you might want to provide an installation prefix with the --prefix or --exec-prefix option. For multiarch-capable systems, at least add the --libdir=${prefix}/lib/HOST option.

Build the libraries and tools with

make

If you have QEMU installed, you can run the test suite with

make check

It may be useful to install the cross-compiled libraries on your build system so you can use them to cross-compile other applications. If you configured an installation prefix, simply run

make install

If you only want to collect the build artifacts, do not configure a prefix, but run

make install DESTDIR=$(pwd)/../install

Bare-metal ARM

Compiling and using Lely CANopen on a bare-metal platform is necessarily a custom process that needs to be tailored to each platform. In this section we will describe the general process.

You can find a specific example — an Eclipse project building a CANopen slave for the NXP LPC1700 series (ARM Cortex-M3) — at https://gitlab.com/lely_industries/lpc17xx.

Requirements

For bare-metal platforms it is generally more convenient to include the stack as a Git submodule and use the build system of the project instead of the stack (see the Eclipse example). However, you can use the build system of the stack to build a set of static libraries and link them with your project. In that case, you first need to install the dependencies for native builds.

Compiler

Install the cross-compiler and binutils for the target triplet, typically arm-none-eabi or aarch64-none-elf. If your distribution does not provide the toolchain, download it from Linaro.

On Debian/Ubuntu, run

sudo apt-get install gcc-arm-none-eabi

for arm-none-eabi.

Standard library

Bare-metal platforms are freestanding environments that typically lack full support for the C standard library. Newlib is a popular implementation that provides all the functionality required by Lely CANopen.

On Debian/Ubuntu you can install it with

sudo apt-get install libnewlib-arm-none-eabi

or

sudo apt-get install libstdc++-arm-none-eabi-newlib

if you also need the C++ standard library.

Note: You need to compile the stack with -D__NEWLIB__ to use it without errors. Newlib provides some POSIX definitions without advertising it properly, which may cause multiple-definition errors. If __NEWLIB__ is defined, the stack omits the definitions already provided.

When linking with -lnosys, you’ll need to implement a few functions to get the stack to work correctly:

Function Notes
_exit() Call NVIC_SystemReset().
_gettimeofday() Needed for clock() and time().
_read() Perform a UART read in case of STDIN_FILENO to enable standard input over a serial port.
_sbrk() Needed for malloc().
_times() Can be implemented in terms of gettimeofday().
_write() Perform a UART write in case of STDOUT_FILENO or STDERR_FILENO to enable standard output over a serial port.

Additionally, liblely-libc needs clock_gettime() for its implementation of timespec_get(); liblely-io2 also needs clock_getres() and clock_settime() for its standard system clock interface.

Build instructions

To build the static libraries, clone the repository and prepare the build environment:

git clone https://gitlab.com/lely_industries/lely-core.git
cd lely-core
autoreconf -i
mkdir -p build
cd build

and configure the project:

CFLAGS="-g -fno-builtin -O2 -mcpu=cortex-m3 -D__NEWLIB__" \
CXXFLAGS="-g -fno-builtin -O2 -mcpu=cortex-m3 -D__NEWLIB__" \
LDFLAGS="--specs=nosys.specs" \
../configure --host=arm-none-eabi \
  --disable-shared \
  --disable-tools \
  --disable-tests \
  --disable-python \
  --disable-threads \
  --disable-daemon

Replace cortex-m3 with the desired target ARM processor.

If you plan on using threads, you probably want to omit the --disable-threads option. However, in that case you will need to provide your own implementation of the C11 threads functions.

To reduce the size of the libraries, it may be useful to disable assertions and tracing by specifying -DNDEBUG to the C/C++ flags. Also, the size of the final binary (but not the libraries) can be reduced by specifying -ffunction-sections and -fdata-sections. Use -Wl,--gc-sections when linking the binary to remove all unused functions.

Many features provided by Lely CANopen can be disabled at compile time to reduce the size of the binary. See the build configuration options for an overview.

Collect the libraries by running

make install DESTDIR=$(pwd)/../install

and copying the .a files from ../install/usr/local/lib.

Updated: