March 2019: Deprecating Volatile

Welcome to the March 2019 edition of the Embedded Artistry Newsletter! This is a monthly newsletter of curated and original content to help you build superior embedded systems. This newsletter supplements the website and covers topics not mentioned there.

This month we'll cover:

  • How we get to main()
  • Deprecating volatile
  • Volatile load/store
  • Embedded news from around the web
  • Embedded job postings
  • Updates to the Embedded Artistry Website

What Happens Before Main?

We're working on a new article which describes the steps our programs take to get to the main() function. Whether we are running our programs on a PC or an embedded system, there are plenty of behind-the-scenes steps needed to get our system into a usable state.

At a high level, Embedded Artistry systems tend to match the following general pattern:

  1. Initialize the processor & external memory
  2. Setup the C/C++ runtime (reloc, BSS, constructors)
  3. OS initialization
  4. Hardware platform initialization (configure IO, start drivers)
  5. Jump to main()

We would love to hear from you: what steps does your embedded system take to get to main()?

Your feedback will help us refine our general boot model.

Deprecating Volatile

A recent C++ proposal for deprecating the volatile keyword has surfaced. This may surprise our readers, because as Michael Caisse said, "volatile is the embedded keyword."

The original intent of the volatile keyword in C89 is to suppress read/write optimizations:

No cacheing through this lvalue: each operation in the abstract semantics must be performed (that is, no cacheing assumptions may be made, since the location is not guaranteed to contain any previous value). In the absence of this qualifier, the contents of the designated location may be assumed to be unchanged except for possible aliasing.

The problem with its use in C++ is that the meaning is much less clear, as it is mentioned 322 times in the current draft of the C++ Standard.

One problematic and common assumption is that volatile is equivalent to atomic. All the volatile keyword denotes is that the variable may be modified externally, and thus reads/writes cannot be optimized. This means that the volatile keyword only has a meaningful impact on load and store operations.

Where programmers run into trouble is using volatile variables in a read-modify-write operation, such as with the increment (++) and decrement (--) operators. These operations create a potential for a non-obvious race condition, depending on how the operation is implemented in the compiler and platform. Chained assignments of volatile values (a = b = c) are another problematic volatile scenario. Is b re-read before storing the value to a, or not?

To address these usage problems, the authors created a separate proposal for volatile_load() and volatile_store() functions. We suggest an initial implementation of these functions in the next section.

Here's the list of proposed changes with the "deprecation" of volatile:

  1. Continue supporting the time-honored usage of volatile to load and store variables that are used for shared memory, signal handling, setjmp / longjmp, or other external modifications such as special hardware support.
  2. Deprecate (and eventually remove) volatile compound assignment op=, and pre / post increment / decrement -- ++.
  3. Deprecate (and eventually remove) volatile-qualification of member functions. Don’t change volatile-qualification of data members.
  4. Deprecate (and eventually remove) partial template specializations involving volatile, overloads on volatile, and qualified member functions for all but the atomic and numeric_limits parts of the Library.
  5. Deprecate (and eventually remove) volatile member functions of atomic in favor of new template partial specializations which will only declare load, store, and only exist when is_always_lock_free is true. Preserve most volatile free function overloads for atomic.
  6. Deprecate (and eventually remove) non-reference and non-pointer volatile parameters. Deprecate (and eventually remove) constas well as volatile return values. References and pointers to volatile data remain valid.

A rationale for each of these is provided in the original paper, "§3.6 Why the proposed changes?".

For more on the proposals related to deprecating the volatile keyword:

A Volatile Load/Store Implementation

Prior to the proposal, Embedded Artistry implemented volatile_load<T>() and volatile_store<T>() template functions to encourage better volatile behavior in our programs.

Since developers commonly misinterpret volatile access as atomic, seemingly innocuous statements can have surprising effects on program behavior if you are depending on atomic access.

volatile int i = 2; //probably atomic
i++; //not atomic ...

Using volatile_load() and volatile_store() ahead of standards changes will force a better usage pattern:

auto r = volatile_load(&i);
 r++;
 volatile_store(&i, r);

You can use these functions to refactor your programs and control volatile use cases. While this implementation does not meet the proposed specification, it's a step toward cleaning up the volatile keyword.

Here is our implementation of volatile_load() and volatile_store():

#include <cassert>
#include <type_traits>

/** Read from a volatile variable
 *
 * @tparam TType the type of the variable. This will be deduced by the compiler.
 * @note TType shall satisfy the requirements of TrivallyCopyable.
 * @param target The pointer to the volatile variable to read from.
 * @returns the value of the volatile variable.
 */
template<typename TType>
constexpr inline TType volatile_load(const TType* target)
{
    assert(target);
    static_assert(std::is_trivially_copyable<TType>::value,
        "Volatile load can only be used with trivially copiable types");
    return *static_cast<const volatile TType*>(target);
}

/** Write to a volatile variable
 *
 * Causes the value of `*target` to be overwritten with `value`.
 *
 * @tparam TType the type of the variable. This will be deduced by the compiler.
 * @note TType shall satisfy the requirements of TrivallyCopyable.
 * @param target The pointer to the volatile variable to update.
 * @param value The new value for the volatile variable.
 */
template<typename TType>
inline void volatile_store(TType* target, TType value)
{
    assert(target);
    static_assert(std::is_trivially_copyable<TType>::value,
        "Volatile store can only be used with trivially copiable types");
    *static_cast<volatile TType*>(target) = value;
}

Around the Web

Our February reading recommendations are grouped by category as follows

  • General Software
  • Firmware
  • Hardware
  • Product Development
  • C++

General Software

John Regehr collected samples of ASCII art that explains source code. If you're interested in adding ASCII art to your own code documentation, try out Buttersquid.ink. Buttersquid provides a link in the generated text so you can make updates to the diagram in the future.

We've talked about Spectre and Meltdown in past editions of our newsletter. IEEE published an informative article reviewing how the two attacks actually work.

The "Small Change" Fallacy makes us think we can skip full validation because a change is "small". All changes, no matter how innocuous, deserve to be treated seriously by running through the full code change process.

Jack Ganssle republished an old April Fool's Day joke: "Goto considered needed'.

Firmware

Jack Ganssle discussed the impending failure of older GPS devices due to an overflow problem.

Segger has launched the Smash-V2 compression algorithm, which is targeted for embedded systems. Compression requires 900B of ROM, and decompression requires 500-800B. No working RAM is required. Information is available on the Segger website.

Dan Luu analyzed the comparative performance of least-recently-used (LRU) cache eviction and random cache eviction. It turns out that random eviction performs pretty well under certain scenarios.

Arduino launched their own IoT cloud platform.

Mohammad Afaneh published two articles on prototyping with the nRF52840 USB dongle:

All About Circuits has published a set of articles introducing processor architectures:

Hardware

Japanese scientists have developed a MEMS energy harvester which can turn vibrations into power.

"Ensuring Fail-safe Data Storage in Battery Powered IoT Sensor Nodes" provides a strategy to address a common concern in battery-powered embedded systems.

The race is on to find an alternative to SRAM due to energy inefficiencies that are highlighted by the increase in data that the computing industry is managing. Part one of the series describes some of the problems with SRAM.

EE Times discussed the US-China trade crisis and the fallout for the chip industry.

Not sure what the difference is between slew rate and rise time? EDN clarifies the two concepts in "Slew Rate and Rise Time: Not Quite the Same".

Product Development

Pete Staples of Blue Clover Devices was on The Hardware Entrepreneur podcast. He discussed pitfalls with the NPI process and their new production line tool.

Bunnie Huang shared a talk on Supply Chain Security, a topic we've discussed in a previous edition.

C++

Jonathan Müller put together a great reference for when you need to declare different special member functions.

Rainer Grimm, a C++ trainer, addresses common template misconceptions and surprises.

Embedded Job Postings

iRobot is hiring for several embedded software roles in Boston, MA and Pasadena, CA. Please check out all of their open jobs here: https://irobot.wd5.myworkdayjobs.com/iRobot and email Chris Svec (csvec at irobot.com) if you'd like to learn more about building robots that help make people's lives easier.

Hiring Embedded Engineers?

Is your company hiring for embedded systems roles? Send us a short (< 100 words) job ad with a link to the description and we will be happy to include it in our newsletter.

Website Updates

We added additional links and book recommendations to both the Software References and Hardware References pages.

For Beginners was updated with additional links.

We updated the following articles with new content:

New Articles

We published the following article in February:

These were our most popular articles in February:

  1. Creating a Circular Buffer in C/C++
  2. std::string vs C-strings
  3. Jenkins: Configuring a Linux Slave Node
  4. C++ Casting, or: "Oh No, They Broke Malloc!"
  5. Installing LLVM/Clang on OSX
  6. Jenkins: Kick off a CI Build with GitHub Push Notifications
  7. Jenkins: Running Steps as sudo
  8. Migrating from C to C++: NULL vs nullptr
  9. Demystifying Microcontroller GPIO Settings
  10. An Overview of C++ STL Containers

Thanks for Reading!

Have any feedback, questions, suggestions, interesting articles, or resources to recommend to other developers? Simply reply to this email!

While you're waiting for our next edition, check out the website or follow us on Twitter.

Happy hacking!

-Phillip