Reader small image

You're reading from  Hands-On Embedded Programming with C++17

Product typeBook
Published inJan 2019
Reading LevelIntermediate
PublisherPackt
ISBN-139781788629300
Edition1st Edition
Languages
Tools
Right arrow
Author (1)
Maya Posch
Maya Posch
author image
Maya Posch

Maya Posch is a senior C++ developer with more than 15 years of experience. Discovering the joys of programming early on, and later the joys of electronics, she has always expressed a profound interest in technology, a passion that she gladly shares with others. Describing herself as a C developer who happens to like C++ and Ada, she likes to seek the limits of what can be done with the minimum of code and hardware to accomplish everything that is cool, new, and exciting. She also enjoys FPGA development, AI, and robotics research, in addition to creative writing, music, and drawing.
Read more about Maya Posch

Right arrow

Chapter 6. Testing OS-Based Applications

Often, an embedded system uses a more or less regular Operating System (OS), which means that, often much, is the same as on our desktop OS in terms of runtime environment and tools, especially when targeting embedded Linux. Yet, differences in terms of performance and access offered by the embedded hardware versus our PC makes it essential to consider where to perform which parts of developing and testing, as well as how to integrate this into our development workflow.

In this chapter, we'll cover the following topics:

  • Developing cross-platform code
  • Debugging and testing cross-platform code under Linux
  • Effectively using cross-compilers
  • Creating a build system that supports multiple targets

Avoiding real hardware


One of the biggest advantages of OS-based development on platforms such as embedded Linux is that it's so similar to a regular desktop Linux installation. Especially when running an OS such as a Debian-based Linux distribution (Armbian, Raspbian, and others) on SoCs, we have practically the same tools available, with the entire package manager, compiler collections, and libraries available with a few keystrokes.

This is, however, also its biggest pitfall.

We can write code, copy it over to the SBC, compile it there, run the test, and make changes to the code before repeating the process. Or, we can even write the code on the SBC itself, essentially using it as our sole development platform.

 

 

The main reasons why we should never do this are as follows:

  • A modern PC is much faster.
  • Testing on real hardware should never be done until the final stages of development.
  • Automated integration testing is made much harder.

Here, the first point seems fairly obvious. What takes a single...

Cross-compiling for SBCs


The compile process takes the source files, turning them into an intermediate format, after which this format can be used to target a specific CPU architecture. For us, this means that we aren't limited to compiling applications for an SBC on that SBC itself, but we can do so on our development PC.

To do so for an SBC such as the Raspberry Pi (Broadcom Cortex-A-based ARM SoCs), we need to install the arm-linux-gnueabihf toolchain, which targets the ARM architecture with hard float (hardware floating point) support, outputting Linux-compatible binaries.

On a Debian-based Linux system, we can install the entire toolchain with the following commands:

sudo apt install build-essentialsudo apt install g++-arm-linux-gnueabihfsudo apt install gdb-multiarch

The first command installs the native GCC-based toolchain for the system (if it wasn't already installed), along with any common related tools and utilities, including make, libtool, flex, and others. The second command installs...

Integration test for club status service


In order to test the club status service on a regular desktop Linux (or macOS or Windows) system before we embark on cross-compiling and testing on real hardware, a simple integration test was written, which uses mocks for the GPIO and I2C peripherals.

In the source code for the project covered in Chapter 3, Developing for Embedded Linux and Similar Systems, the files for these peripherals are found in the wiring folder of that project.

 

 

We start with the wiringPi.h header:

#include <Poco/Timer.h>


#define  INPUT              0
#define  OUTPUT                   1
#define  PWM_OUTPUT         2
#define  GPIO_CLOCK         3
#define  SOFT_PWM_OUTPUT          4
#define  SOFT_TONE_OUTPUT   5
#define  PWM_TONE_OUTPUT          6

We include a header from the POCO framework to allow us to easily create a timer instance later on. Then, we define all possible pin modes, just as the actual WiringPi header defines:

#define  LOW                0
#define  HIGH...

Testing with Valgrind


Valgrind is the most commonly used collection of open source tools for analyzing and profiling everything from the cache and heap behavior of an application to memory leaks and potential multithreading issues. It works in tandem with the underlying operating system as, depending on the tool used, it has to intercept everything from memory allocations to instructions related to multithreading and related. This is the reason why it is only fully supported under Linux on 64-bit x86_64 architectures.

Using Valgrind on other supported platforms (Linux on x86, PowerPC, ARM, S390, MIPS, and ARM, also Solaris and macOS) is definitely also an option, but the primary development target of the Valgrind project is x86_64/Linux, making it the best platform to do profiling and debugging on, even if other platforms will be targeted later on.

On the Valgrind website at http://valgrind.org/info/platforms.html, we can see a full overview of the currently supported platforms.

One very attractive...

Multi-target build system


Cross-compilation and multi-target build systems are among the words that tend to frighten a lot of people, mostly because they evoke images of hugely complicated build scripts that require arcane incantations to perform the desired operation. In this chapter, we'll be looking at a simple Makefile-based build system, based on a build system that has seen use in commercial projects across a range of hardware targets.

The one thing that makes a build system pleasant to use is to be able to get everything set up for compilation with minimal fuss and have a central location from which we can control all relevant aspects of building the project, or parts of it, along with building and running tests.

For this reason, we have a single Makefile at the top of the project, which handles all of the basics, including the determining of which platform we run on. The only simplification we're making here is that we assume a Unix-like environment, with MSYS2 or Cygwin on Windows...

Remote testing on real hardware


After we have done all of the local testing of our code and are reasonably certain that it should work on the real hardware, we can use the cross-compile build system to create a binary that we can then run on the target system.

At this point, we can simply copy the resulting binary and associated files to the target system and see whether it works. The more scientific way to do this is to use GDB. With the GDB server service installed on the target Linux system, we can connect to it with GDB from our PC, either via the network or a serial connection.

For SBCs running a Debian-based Linux installation, the GDB server can be easily installed:

sudo apt install gdbserver

Note

Although it is called gdbserver, its essential function is that of a remote stub implementation for the debugger, which runs on the host system. This makes gdbserver very lightweight and simple to implement for new targets.

After this, we want to make sure that gdbserver is running by logging...

Summary


In this chapter, we looked at how to develop and test embedded, OS-based applications. We learned how to install and use a cross-compilation toolchain, how to do remote debugging using GDB, and how to write a build system that allows us to compile for a wide variety of target systems with minimal effort required to add a new target.

At this point, you are expected to be able to develop and debug an embedded application for a Linux-based SBC or similar, while being able to work in an efficient way.

In the next chapter, we'll be looking at how to develop for and test applications for more constrained, MCU-based platforms.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Hands-On Embedded Programming with C++17
Published in: Jan 2019Publisher: PacktISBN-13: 9781788629300
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Maya Posch

Maya Posch is a senior C++ developer with more than 15 years of experience. Discovering the joys of programming early on, and later the joys of electronics, she has always expressed a profound interest in technology, a passion that she gladly shares with others. Describing herself as a C developer who happens to like C++ and Ada, she likes to seek the limits of what can be done with the minimum of code and hardware to accomplish everything that is cool, new, and exciting. She also enjoys FPGA development, AI, and robotics research, in addition to creative writing, music, and drawing.
Read more about Maya Posch