Over the last couple of months, I’ve been writing several articles at embedded.com that explore programming languages and embedded software architecture. For example, in Is it time to retire C?, I tongue in cheek argued that C doesn’t provide developers with the tools they need to implement an embedded system. C is the go-to language for embedded software developers! It’s used in more than 80% of embedded systems and a recent study entitled “Energy Efficiency across Programming Languages”, the authors found that C is the most energy efficient language as shown below:
Figure 1 – The language results in the energy, time, and execution domains found in Table 4 of “Energy Efficiency across Programming Languages”.
If C is the most energy efficient, optimized, and popular language for programming embedded systems, why do I always feel like one hand is being tied behind my back when I program?
As I contemplate this question, I realized that the problem to some degree is a problem of scale. The use of C is great for interfacing to hardware and low-level real-time code, but it lacks the mechanisms and tools at higher levels of abstraction, in the application code, to really write code that can be reused and scaled well. If you look at higher-level languages, they do the high-level code well, but not the low-level hardware interfacing.
I recently started to write a series for embedded.com about software architecture that discusses how embedded systems really have two architectures, a hardware dependent real-time architecture and then a hardware independent application architecture. These two architectures are separated by an abstraction layer. Does our code need to be separated in the same way?
Multiple Languages for Multiple Concerns
In a strange way, many embedded systems have become complex enough that just using a single language like C may not make sense! Sure, if you are creating a simple controller, then C might be all that you need. What about those complex applications with connectivity, displays, and the need for reuse and scalability? Or applications that have a high need for a core code base that then has other components added based on the application needs?
I am starting to believe that the answer is for developers to leverage multiple programming languages. The language that is used should be developed to offer the widest range of tools and efficiency necessary for its area of concern. For example, real-time code that interfaces with the hardware should be written in C. (Perhaps one day Rust). C is well suited for these low-level interactions with hardware.
When you start to look at higher-level application code that is concerned with interacting with the user and has zero interest in the hardware, it makes more sense to use a language like C++ or Rust. These languages provide far better tools for dealing with scalability and reuse than C. We can use object-oriented tools like inheritance, virtual functions, templates, and the like. These tools provide us with a wide variety of ways to produce the most reusable application code. In fact, languages like C++ and Rust have bindings to use C code. We can have code from multiple languages co-exist in the same code base.
Initially the idea of using multiple languages might leave a sour taste in your mouth. We want to select a single language and move on. For years the industry has fought battles as to whether we should use C or C++. C or Rust. And so forth. The reason we have never been able to come to any conclusion is that all sides are correct. The problem may have been that we simply want to apply these languages across all areas of concern in the system, when in fact, their use should be limited to the concerns that they provide the best tools for solving.
Are Multiple Languages Needed for Embedded Systems?
I think in some cases, the answer is a resounding YES!
GUI frameworks, Google’s buffers use C++ so grumble, grumble C purists but …
Unless you opt for the Crank’s Storyboard as your GUI and now you are facing some … Lua!
Yep, being multilingual is never a handicap! (in programming or life …).
Thanks for the comment!
Yes, it’s definitely hard to get away from other languages that help to support embedded software development. In addition to using C/C++, Rust, MicroPython on microcontrollers, it’s not uncommon to use XML, YAML, and Python to support configuration and the build tools. For small teams, I’ve even seen people using React to create web applications to interface to their device to gather telemetry and other support activities.
“Energy Efficiency” is only relevant if we assume more or less equal system deployment time and the actual duty cycle of the system in the wild. If the system wakes up for less than 1% of the time and/or its deployment time is limited not by the battery (for example limited by the lifetime of a sensor) it may be as well written in Lua or Python. Specially if if the necessary functionality is already provided in the ever growing universe of the libraries for these langs …
There is no difficulty in accepting the argument that C is good for the hardware level and some other language is better for a more abstract level. But the question is left hanging as to what the more abstract language should be. I have learned Algol 60, PL/1, PERL, C++ and a little Python. So far they don’t mesh well with C or are expensive in time.
Julia looks like a good thing to try. The greenlab article approach will be tried to see how well Julia can be fit in.
Thanks for the comment.
I think to a large degree, that more abstract language is going to be based on the industry and the product requirements along with the development team skills and needs. For microcontroller-based systems, I see the choices as being C++ or Rust for the most part with maybe some MicroPython. (MicroPython is written in C). Obviously, each language has their advantages and disadvantages and a subset of the language(s) would be the most useful.
I agree (mostly) with Jacob, but nowadays things get quite complicated. Even when assuming “microcontroller” platform one cannot just say that only C/C++ or Rust apply. The thing may be a multiple-core Cortex-M7 with megabytes of memory required to run Wi-Fi, BLE and 2 more wireless protocols (not joking here! These ARE REAL requirements!).
On another hand “embedded” may mean 64-bit Cortex (see the newest Rpi!) … which will run Linux along with Python, Perl (ugh…) and more …
That’s a good point. When I think of a microcontroller I generally think of the “smaller traditional” devices. You’re definitely right that some of the very high-end parts are more like application processors with GHz clock rates, megabytes of memory, etc.
I’ve not seen too many companies using these types of parts yet, but it is only a matter of time before they are probably the standard.
Thanks for the comment and feedback! I really appreciate it.
I agree that higher level languages are needed even in small embedded systems. I think the gap that exists is in interfacing/interoperability between the high level languages and C. As you said “languages like C++ and Rust have bindings to use C code” but, in my experience, actually marrying C and C++ code in a system can be very difficult. Perhaps this would be a good topic for a series of blogs/articles that might help move the industry in the multi-language direction.
Thanks for the comment. I think doing a series on this could be interesting as well. I’ve put it on the list of topics for the future. Thanks for sharing your experience and the suggestion!
Yes, I agree.
As I mentioned “embedded” nowadays does not necessarily means “small” and/or “limited” … Well even in the (very) old days a racked PC in the NEMA cabinet hooked to multi-ton machine on the factory floor was named “a controller” and classified as “an embedded system”!
The interoperability of languages was a problem then and it is becoming more of a problem now …
The Zen-like austere paradise of micros running C-only had been once gained and then has been lost …
The only constant thing in universe, apart from the physics constants (maybe!), is change … 🙂