Burkhard on Qt Embedded Systems: No. 4
Welcome to Episode 4 of my newsletter on Qt Embedded Systems. I hope this episode finds you alive and kicking despite the COVID-19 pandemic. Bavaria has been under curfew for 11 days and will be for at least another 19 days. My customers moved to remote work over the last two weeks. For me, remote work has been the normal mode (roughly 75% of my time) for the last 7 years. So, it's not much change for me. Take care, stay safe and enjoy reading the newsletter - guaranteed Corona-free.
My Blog Posts
Creating Simple Installers with CPack. This post is a follow-up to my earlier post Benefits of a Relocatable Qt, where I showed how to relocate Qt from a build server to a developer PC and finally to an embedded system. Relocation became very easy with Qt 5.14. My new post fixes two problems. First, I replace the absolute install rpath by an rpath relative to the application's directory. Second, I use CPack to create a gzipped tarball instead of doing that manually.
CPack also allows to create RPM and DEB packages or UI installers like the Qt Installer Framework (Qt IFW). So stay tuned for some more posts about CPack 😉
News
Available now: Qt for Small Business by Daniel Hartung (The Qt Company). Petteri Holländer announced the Qt for Small Business license in his post Qt offering changes 2020. Many people including yours truly (see my post Less Love for FOSS Qt Users) heavily criticised The Qt Company for limiting their offer to businesses with less than 100,000 USD yearly revenue. The number of eligible businesses would have been negligible.
Thankfully, The Qt Company listened and increased the limit to 250,000 USD yearly revenue. The license comes as a subscription at a fair 499 USD per year. Here is a summary of your rights and obligations.
Your business gets up to four developer licenses for desktop, mobile and embedded (Qt for Device Creation).
You can distribute Qt applications on desktop and mobile without extra fees, but not on embedded.
If you want to distribute Qt applications on embedded devices, you must buy extra distribution licenses. In other words, you must pay royalties per device.
Your subscription auto-renews unless you terminate it at least 30 days before the end of the license term (one year).
If you terminate the license, you can still use the latest Qt version received during the license term to support your customers. Your customers can still run your application with this Qt version. Starting a new development with this expired license violates the license agreement (see Section 12.4 of the SLA).
If you exceed the limit of 250,000 USD revenue during the license term, you must upgrade to the standard license at the end of your license term or terminate your license.
Although I don't need a commercial Qt license, I bought it. It's my way of saying thank you to The Qt Company for providing a fantastic product 🙏
I hope this is only the first step in coming up with a more reasonable pricing model for Qt Commercial. I made a proposal in Episode 3 of my newsletter.
New Linux-powered SoC taps an old ARM9 architecture by LinuxGizmos.com. I built my first Qt embedded Linux system on an ARM9 SoC, the Freescale i.MX27, in 2007. The i.MX27 reached its end of life in 2017. NXP, now the owner of Freescale, doesn't build ARM9 SoCs any more. NXP replaced the ARM9 SoCs with the i.MX6 and i.MX7 variants with Cortex-A7 cores.
Microchip fills the void at the very low end and revives the ARM9 SoCs. The freshly launched SAM9X60 has a single core running at 600 MHz. The 2D GPU drives displays with up to 1024x768 pixels. The interfaces include 2x Ethernet 10/100, 2x CAN, 2x USB 2.0. The development kit comes with 256 MB RAM and 512 MB NAND flash. The SoC is automotive-grade and operates in the temperature range from -40 to +105 °C. It supports SecureBoot and comes with crypto accelerators and tamper pins.
The ARM9 SoC costs only 4.34 USD at 5000 units. I'd guess that this is about one third of the price of the Cortex-A7 based SoCs. The price is comparable to that of the Cortex-M4 and Cortex-M7 microcontrollers that can run Qt. The advantage of the ARM9 SoC is that it runs on Linux. In contrast, the microcontrollers run bare-metal or on an RTOS.
Reading
How to Give Respectful and Constructive Code Review Feedback by Michaela Greiler. Doctor McKayla gives ten valuable tips for code reviews. Tip #1 is to ask questions instead of ordering people to do changes. Tip #2 suggests to make it about the code: "This function leaks memory through variable X" is much better than "You leak memory in this function through variable X".
Tips #3, #7, #8 and #9 are the general rules of constructive feedback applied to code reviews. As a reviewer, phrase your message from your perspective using "I"-messages. Give reasons for the suggested code change. Good reasons are code smells or violations of the SOLID principles. Give hints (not complete solutions) how to fix a problem. Adapt your advice to the skill level of the developer. A beginner needs more detailed advice than an expert.
Avoiding sarcasm and condescending words (tips #4 and #5) and assuming miscommunication over malice (tip #10) are ground rules for constructive reviews. Brightening up code reviews with emojis (tip #6) is a great idea 👍
I'd like to add a tip from my experience. The team should agree on guidelines what good code and bad code is. The guidelines will most likely include code smells and the SOLID principles. Observing coding guidelines and eliminating warnings from compilers and static analysers should be part of the guidelines as well. For me, they are basic code hygiene. Such guidelines help avoid fruitless discussions about opinions. Walter Matthau's character in the film First Monday in October nails it: Opinions are like assholes. Everybody's got one and everyone thinks everyone else's stinks. Discussions tend to be more objective and more constructive with guidelines in place.
The Matthew Test: 15 Steps to Better Embedded Softare (and Firmware) by Matthew Eshleman (Cove Mountain Software). The Joel Test gives you a quick way to assess the quality of a software development team. The Joel Test will celebrate its 20th anniversary this year. Matthew updated the Joel test and adapted it to embedded software and firmware. He came up with 15 questions for The Matthew Test. He explains the rationale behind the questions in detail with lots of valuable links.
I'd like to add one question: Do you receive user feedback at least once a week? Some years back I helped develop the driver terminal of a maize harvester with a resistive touch screen. The HMI had some scrollable list views. Scrolling the list view and selecting an item worked perfectly on the touch screen in the office. Selecting an item didn't work at all on the harvester. Due to the heavy vibration on the 550 horse-power harvester, every selection was detected as scrolling.
Fortunately, we could just walk out of the office and try out new features on the harvester. So, we detected the problem early. If we had detected the problem only in the next harvest six months later, we would have needed much more time to fix the problem. Receiving early and frequent user feedback will save you a lot of time.
Matthew's questions map well to the indicators that predict the performance of a team - as introduced by Nicole Forsgren, Jez Humble and Gene Kim in their must-read book Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations. If a team rates Excellent in The Matthew Test, you are looking at a high-performing team. If a team rates Ouch, you are looking at a low-performing team.
As a solo consultant, I use similar questions to find out how I can best help improve the team's performance. Based on the answers, I can gauge how much value I will bring to a project. This enables me to set a price that is fair for the client and me. Similarly, you can use the questions from The Matthew Test to assess your potential future employer. Joining a team that rates Ouch or Lucky might not be a good idea.
The Performance Benefits of Final Classes by Sy Brand (Microsoft). When you declare a class final, you forbid other developers to derive from this class. When a function is called on a pointer or reference of the final class, the compiler knows that the function cannot be called on a derived class. The compiler devirtualises the function, that is, it resolves the virtual function call at compile time rather than runtime. The compiler avoids indirect calls through the vtable and instruction cache misses due to wrong branch predictions. Sy illustrates the behaviour of final classes in their post.
In-Class Member Initialisation: From C++11 to C++20 by Bartłomiej Filipek. Before C++11, you could initialise member variables only in the initialisation list of constructors. If you had multiple constructors, you had to write the initialisation lists multiple times - a clear case of code duplication. From C++11, you can initialise non-static member variables where you declare them. C++17 even allows you to initialise static member variables at their place of declaration - with the magic keyword inline. The following declaration and initialisation is perfectly OK in C++17.
class A {
qreal m_penWidth = 2.0;
QColor m_penColor{Qt::green};
inline static QString m_label{"???"};
}
The first two declaration work in C++11 already, whereas the third declaration requires C++17 at least. The inline static declaration saves you from initialising the variable in the corresponding source file.
Bartłomiej elaborates on member initialisation in his post. And, don't miss his book C++17 in Detail.
Introduction to Trusted Execution Environment and ARM's TrustZone by Sergio Prado (Embedded Labworks). Sergio gives an overview how to run programs safely and securely in a Trusted Execution Environment (TEE) using ARM's TrustZone. Applications of TEEs include biometric authenticaction (e.g., facial or fingerprint recognition), contactless payments, mobile wallets and digital rights management.
Systems with a TEE are split into a non-secure world and a secure world. Some hardware resources like processor cores, busses, peripherals and memory areas are exclusively assigned to the secure world. In a hybrid i.MX8 SoC, the Cortex-M cores, the CAN interfaces and some special memory could be assigned to the secure world. The controllers for robots, high-speed label printers or multi-head scales would run in the secure world. The HMI applications would run in the non-secure world on the Cortex-A cores and use the remaining peripherals and memory.
Special hardware, the ARM TrustZone, ensures that untrusted applications from the non-secure world cannot access the data in the secure world and cannot change the trusted applications running in the secure world. The ARM TrustZone provides hardware isolation for the trusted applications.
When an untrusted application requests a secure service, say the encryption of some data, it calls the API of the TEE. The TEE API hands the request over to the Linux kernel, which switches from kernel mode into secure monitor mode. A trusted application encrypts the data with the secure key and sends back the encrypted data to the Linux kernel and the untrusted application. Sergio has promised a follow-up post, where he explains how to build a system with the FOSS OP-TEE (Open Portable TEE).
CMake Resources
As most of you know by now, CMake will replace QMake in Qt 6 as the standard build system for Qt itself and for Qt applications. Lars Knoll wrote in the Technical Vision for Qt 6:
"QMake as the build system used in Qt 5 has lots of quirks and limitations. For Qt 6, we aim to use CMake as a standard 3rd party build system to build Qt itself. CMake is by far the most widely used build system in the C++ world, and better integration with it is sorely needed. We will continue to support our users on QMake, but not develop it further or use it to build the Qt framework itself."
I moved from QMake to CMake in the winter of 2017/2018. The transition was painful. I had known QMake for more than 15 years. CMake was new to me. Even simple things took ages in CMake. Fortunately Daniel Pfeifer, a CMake expert, worked on the same project for an e-bike startup and patiently answered my questions. My final farewell to QMake came on the next project, where I had to integrate a code generator into the build of a Qt application. I couldn't get it working with QMake. So, I gave CMake a chance and got it working pretty quickly.
So, give CMake a fair chance. It will be a nuisance in the beginning as nearly every new tool. But soon it will become a good and reliable companion. CMake is not just a cross-platform build system generator but also provides a packaging tool (CPack), a testing tool (CTest) and a dashboard (CDash). Here is a list of resources I find useful for learning CMake and for working with CMake.
Professional CMake: A Practical Guide (5th Edition) by Craig Scott. Just buy the book! Its value is a multiple of its 30 USD price. The book covers all of CMake in three parts.
Part I: Fundamentals. The first part teaches you the CMake language: application and library targets, variables and their scopes, flow control, functions, properties and splitting up projects into subdirectories. This is normally enough to build your Qt application with some libraries.
Part II: Builds in Depth. The second part explains how to generate release, debug and other configurations, how to add compiler and linker flags, how to generate and copy files during a CMake run, how to integrate toolchains (32-bit GCC, Raspberry Pi and Android) and how to handle versioning.
Part III: The Bigger Picture. The third part goes beyond CMake. It explains how to run tests with CTest and how to show the results in a dashboard (CDash). Installing your applications, libraries and auxiliary files with CMake amounts to some calls to CMake's install function. CPack enables packaging the installation files as simple archives, Qt IFW, WIX, NSIS, RPM, DEB, etc.
Every chapter ends with a section Recommended Practices. Craig illustrates with hundreds of well thought-out examples how CMake and its companion tools work. I can often copy one or two lines from the book and use them in my own CMake files - with little or no modifications. The book also contains a lot of useful tips for building Qt applications.
Effective Modern CMake by Manuel Binna (BMW) (Note: Follow the link and click on the title Effective Modern CMake to see the full post in its latest version). Manuel has compiled a list of 40+ dos and don'ts, which he maintains and extends regularly. The explanation for each item is short and to the point. If the explanation is too short, you can head over to Craig's CMake book for more details. Here are my favourite items.
Treat CMake code like production code. Keep your CMakeLists.txt files as simple as possible and improve them regularly.
Define project properties globally. Variables, options or properties defined in the top-level CMakeLists.txt file are propagated to all subdirectories and included files. Examples are compiler warnings and flags for crossbuilds, for static-analysis builds or for adding license checks.
Follow a naming convention for test names. A unique prefix of the test project names helps you to identify a set of tests to run with CTest.
Imagine targets as objects. The functions add_executable and add_library are the constructors. Target properties like target_compile_definitions, target_include_libraries and target_link_libraries are the member variables.
Manuel's list will save you considerable time, as you don't have to figure out the dos and don'ts for yourself.
Effective CMake by Daniel Pfeifer (video from C++Now 2017). Yes, this is the same Daniel Pfeifer who patiently helped me with my first CMake steps. Many of Manuel's dos and don'ts (see above) are extracted from Daniel's talk.
Daniel covers a wide range of CMake topics from basics over best practices for real-life projects to packaging and testing in just under 90 minutes. His talk is packed with tons of invaluable advice but can be a bit overwhelming. I still need to stop the video regularly and read up on the topics in Craig's Professional CMake book.
Using Modern CMake Patterns to Enforce Good Modular Design by Mathieu Ropert (video from CppCon 2017). This talk is the foundation for quite a few of Manuel's dos and don'ts (see above). It especially elaborates on the tip Imagine targets as objects. Mathieu's talk will make the difference whether you end up in CMake hell or not. So you better watch it!
Mathieu's core point is that you regard every library defined by add_library as an object or module with a public and private interface. Clients depending on the library don't have to know all the compiler and linker options required to build the library. These are implementation details best hidden from clients.
All the target properties have PUBLIC and PRIVATE keywords to control the visibility of compiler and linker options. If, for example, library A depends on library B only internally, you can write
target_link_libraries(A PRIVATE B)
Clients of library A won't know anything of B. If you replace PRIVATE by PUBLIC or leave out the keyword, clients of A will see library B. Library B will be listed in the linker options of A. Similarly, you can specify which headers are visible to clients and which are not.
target_include_directories(A PUBLIC include)
target_include_directories(A PRIVATE src)
Mathieu's advice is to use PRIVATE whereever possible to avoid polluting the global namespace. Otherwise, your CMake projects will quickly become unmaintainable.
The target_link_libraries command above refers to a module B instead of linker options -L/usr/local/lib -lB. The linker options are hidden inside module B. Library A uses the command find_package(B) to address module B as an object. find_package only works, if CMake finds a config package file BConfig.cmake.
If library B is a CMake project, you can generate most of the config package file with the command install(EXPORT). Unless for simple projects, you need to extend the config package file a little bit. Craig gives a detailed example in section 25.7.1 of his book Professional CMake. If library B is not a CMake project, Mathieu describes how to write a hand-made finder (starting at position 39:22 in the video).
My Articles about CMake. I have written four articles about CMake with a special focus on Qt.
CMake Cross-Compilation Based on Yocto SDK. I assume that you have created an SDK from your Yocto build. Then, I walk you through writing a toolchain file step by step. The toolchain file for an i.MX6 SoC is used during cross-building the Qt application.
Deploying Qt Projects to Embedded Devices with CMake. QtCreator allows you to cross-build a Qt application, deploy it to the target embedded system and run it there - all with one command. The QtCreator based on Qt 5.12 could not generate all the information required for deployment from the CMakeLists.txt files. I describe a workaround. Newer QtCreator and CMake versions are better integrated and don't need the workaround any more.
Benefits of a Relocatable Qt. Starting with version 5.14, Qt is relocatable. I show how to relocate Qt from a build server to a developer PC and from the developer PC to the target system. The second step demands special treatment of rpaths, which CMake provides.
Creating Simple Installers with CPack. See the section My Blog Posts at the beginning of this newsletter for a summary.