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!