This all started early last year, when I received an email from Clive (Max) Maxfield. He was hosting and moderating a session at Design West, and he asked me whether I would care to participate. The session was called “10 Languages in 45 Minutes.” The idea was that there would be 10 speakers, each of whom would have four minutes to talk about a specific language of relevance to embedded system developers. The additional five minutes would be for Max's introduction and closing statements. He asked me to cover C++, and I was more than happy to accept this invitation.
The session went very well. We had a very good-sized audience, which was pleasing, particularly as we were starting at 8:30 a.m. After a brief introduction from Max, the speakers made their pitches, with Max running a timer to ensure fair play. The brisk pace made for a very dynamic session, and I believe that the audience enjoyed it. I certainly did.
After the session, I had an idea — why not write an article to share the concepts presented in this session with a wider audience? This is it, though I should note that I am confining myself to the five software programming languages. The other five were hardware description languages (HDLs); maybe we can return to consider those another day.
Embedded programming languages
In many ways, programming an embedded system is not too dissimilar to coding for a desktop computer, but there are some key differences:
- On an embedded system, resources — memory and CPU power — are limited. By comparison, with desktop systems, it is commonly assumed that they have no limits.
- Embedded systems are commonly real-time.
- The number of operating systems in use on desktop computers is quite small. There are numerous options for embedded systems, including “bare metal” (i.e., no operating system at all).
- To the first approximation, the hardware of all PCs is identical. By comparison, every embedded system is different, so programming close to the hardware is more common.
In the early days of embedded systems, factor No. 1 — and, to some extent, Nos. 2 and 4 — resulted in most programming being performed using assembly language. This remains an option, but now assembly is used only when it is absolutely essential.
The needs of embedded developers are sufficiently special that it might be expected for specialized languages to have developed to address their unique requirements. There are examples of languages that have been designed from the ground up for embedded applications (e.g., PL/M, Forth, Ada), but they have not found universal acceptance.
C was designed in the 1970s by Dennis Ritchie at AT&T Bell Labs. The language was based on an earlier attempt to develop a high-level language called B that had the benefits of assembly. This, in turn, had started out as BCPL back in Cambridge, England. The original reference book, The C Programming Language by Brian W. Kernighan and Dennis M. Ritchie, was published in 1978. Full standardization (by ANSI) took another 10 years, and there have been a number of iterations since.
Though never designed or intended for embedded use, C remains the most widely used embedded programming language, with compilers available for almost every microprocessor, microcontroller, and processor core on the market.
Even though C is a structured language, it offers a great deal of flexibility, enabling code to be written that is quite readable, but with obfuscation a readily available option.
Here is an example of quite well laid-out C code.
The following style is slightly more compact but still valid.
The following is also valid, but it's also a good example of unreadable code.
Notwithstanding its challenges, C syntax has been the basis for a number of other languages (as we shall see). Apart from needing care with layout, C has a number of other “flexibilities” that can lead to problems.
- Pointers are powerful but can easily lead to confusion.
- The language is weakly typed, thereby facilitating accidental conversions, which can very easily lead to subtle errors.
- Dynamic memory facilities are quite primitive and not well suited to real-time systems.
Many C users value the language's strengths and have taken action to mitigate its weaknesses. This has resulted in a number of approaches to constrain the use of C to avoid the pitfalls. Probably the best-known example is MISRA C, which started out in the automotive world but is finding favor elsewhere.