
Hello everyone, I am Bug Jun!
Today’s article depicts what a true embedded programmer should look like. In fact, Bug Jun has always believed that embedded programmers are the artisans of the programmer world, creating exquisite masterpieces with extremely minimal “objects”!
Well, enough bragging, you will understand after reading the translation below:
Registers! Oscilloscopes! Beards! Serial ports! C! Loop shaving! Beards! Interrupts! Assembly! And did I just mention beards?
If I mention the term “embedded programmer”, most people in our industry would immediately think of an image of a heroic figure.
A great developer with mysterious skills, possessing profound knowledge of esoterica, holding a skeptical attitude toward personal hygiene, with a scruffy beard, resembling a deity.
However, after secretly studying this topic for several years, I am here to tell you that your imagination is completely incorrect.
Most embedded programmers do not have beards, and being an embedded programmer does not require dreaming of using assembly language, and embedded programmers do take showers.
I can also tell you that embedded programming is very interesting, rewarding, and challenging. If you are interested in this article, then it is possible that you will also engage in this field in the future.
Please note that the following content may be a bit ambitious. I will introduce what an embedded programmer should look like, rather than what most embedded programmers actually look like.
This may be an ideal situation. Suddenly encountering embedded code, you might find it very intimidating, thinking that this code was written by some clever guys with high-end skills.
But I want to tell you that you will be interested in Spotify’s embedded programming, and excellent code is our goal.
So, if you are also an embedded programmer, then the code presented in this article may differ from what you use every day. I understand your feelings; reality is indeed so, but life can be better.
First, let’s introduce the difference between embedded systems programming and embedded application programming.
Embedded Systems Programming: It may be what you imagine embedded programming to be.
It involves all the work necessary to get the embedded hardware platform running: writing device drivers, bootloaders, porting or writing operating systems, various bit manipulations, and considering clock cycle issues. Debugging lost interrupts.
Staring at the oscilloscope for hours, complaining about the compiler, hating the interrupt controller, as if it were a person. Often waking up in the middle of the night, drenched in cold sweat, trying to convince yourself that it was the cat that stole the interrupt signal. Sorry, I digress. Embedded systems programming indeed involves very low-level things.
Embedded Application Programming: Refers to the techniques for writing applications for resource-constrained systems. This type of development is easier, and the development environment is good.
You can also write, test, and debug code on a desktop computer. While some constraints can be challenging, you do not need to worry about assembly, GPIO pins, or DMA descriptors.
However, you must consider memory usage, runtime environment, code size, and portability. But seriously, does anyone not like this work?
This article discusses embedded application programming, which is the type of embedded programming we mostly engage in at Spotify.
Constraints make our lives more interesting.
Recording a music album with just two pillars and a glass of water requires more creativity compared to an album recorded with a full orchestra.
While I have an unconventional understanding of creativity or the term “music”, I want to express that embedded programming is fun, but where is the fun?
The primary constraint of most embedded programs is size; the code must be compact. Common programming habits (code needs to be modular, easy to maintain and test, and must be tested) still apply to embedded programs, in addition to minimizing the amount of code and ensuring self-sufficiency.
In summary:Elegance. Good embedded code is very elegant.
So what is embedded programming? What is an embedded programmer? Let’s continue to look down…
Memory Utilization – The Invisible Killer
Embedded programmers need to avoid modern memory management concepts. It is almost impossible to implement garbage collection. A garbage collector could exhaust the code capacity limit.
Moreover, the garbage collector needs to run actual garbage collection from time to time, which can disrupt the real-time performance of embedded programs.
You even need to avoid conventional malloc(); calling malloc() can take a lot of time because the allocator might have to defragment the allocated area to release a sufficiently large memory block to respond to the request.
Embedded programmers are happy to manage memory directly, writing custom allocators and even eliminating memory allocation failures through statically allocated memory blocks.
A major difference between most embedded systems and ordinary computers is the organization of memory.
The programming model used by mainstream desktop and server processor architectures (such as Intel x86) stores code and data in the same address space.
This means that if your machine has 64MB of RAM (wow, that’s huge!), and your program is 40MB (incredible!), then you only have 24MB left for your data (so much, it’s hardly used!).
At home, we call this the von Neumann architecture, but we wouldn’t dare say it in a bar, as it could spark a debate.
Since RAM is the largest power consumer in the processor and takes up a lot of chip area, many embedded systems use models where code and data are stored in different memory.
Code and static data are stored in ROM (usually flash or EEPROM); dynamic data is stored in RAM. ROM is much cheaper than RAM, so it is usually used more (typically 5-10 times more).
RAM and ROM can have separate address spaces (Harvard architecture) or be mapped to a unified single address space (a modified Harvard architecture, unfortunately never called clown architecture).
Unless you need to write a bootloader or system upgrade functionality (in which case you need to write to the code storage space), you generally wouldn’t notice the difference between the two.
But this does mean that the size of the code and the usage of RAM need to be calculated separately. In other words: in embedded systems, the limits on code size are different from the limits on RAM usage.Keep in mind that RAM usage is usually the most critical parameter.
What You See Is What You Get
Embedded programmers are indeed the elite of the software world. They love to write programs by hand, create their own code libraries, using languages from the 1960s, with mechanical keyboards.
There are rarely ready-made libraries that meet the special constraints of embedded systems. While there are many JSON parsing libraries, few libraries can support parsing documents larger than the RAM capacity.
Embedded programmers are always happy to try existing libraries because they are all lazy. However, if there is no suitable library, embedded programmers are also happy to reinvent smaller and faster tools.
Since embedded programmers value understanding and controlling code execution and resource usage, almost all code is written in C.
Sometimes new programming languages attempt to invade the embedded world, only to be questioned by embedded programmers.
Does this language have some peculiar threading model? See the discussions about concurrency below.
Does this language have garbage collection? See the discussions about performance below.
Does this language’s compiler support every known computer architecture?
See the discussions about portability below. In short, the standards are set very high, and C is very good.
Embedded software systems are often easy to understand. The lack of third-party code libraries and fancy inheritance structures, combined with strict code size limits, ensures that the code is small and easy to understand.
In well-written embedded code, everything you see on the page is all the code. Embedded programmers do not need to peel layers to figure out what the code actually does.
This is not to say that the logic of actual embedded applications is not very complex, but at least we do not need to hide this logic through layers of abstraction.
Embedded developers do not like concurrency. For Charles Babbage (the inventor of programmable computers and a pioneer of computing), doing one thing at a time is enough, and so should you.
Most forms of concurrency support require saving state from time to time and switching to new tasks.
This requires multiple stacks, one for each task, and a lot of RAM, sometimes more than a little. Nowadays, a stack can easily occupy 1KB or even more.
Embedded programmers cannot tolerate this. They would rather manually handle cooperative task switching, non-blocking I/O, polling, callbacks, manual task scheduling, and main loops.
These are the most commonly used things by embedded programmers. If you do not understand what these mean, do not worry; you can refer to the recommended books at the end of this article.
Smart readers might wonder: “Isn’t saving task state required for all types of task switching? Aren’t you shifting this work from the operating system to the programmer?”
Embedded programmers would respond: “Indeed!” or say: “Shifting this work from the operating system to the programmer is exactly what embedded programming is all about!”
Concurrency is a challenge. However, putting aside preconceived notions of multithreading, concurrency is actually much simpler. You know your code will not be interrupted, so you can easily grasp the execution order.
The downside is that you need to write more code for I/O, as you cannot use blocking.
The poor embedded system programmers mentioned earlier are unlucky; for them, hardware interrupts disrupt all work.
If your program cannot run on all known processor architectures, then your program is not considered embedded.
Big-endian, little-endian, stack-based addressing, register-based addressing, RISC, CISC, scalar, vector, DSP, or PIC, it doesn’t matter.
The code should run anywhere. Does a byte contain 8 bits? Not so fast! Embedded programmers do not make any assumptions; they question everything.
If one day, a client asks you to run your program on a Chewbacca 5000 12.5-bit stack-based vector CPU with segmented writable memory and branch coprocessors, then the embedded programmer can calmly tell him: no problem.
What does this mean for you? Go crazy brushing up on the old C standards.
Since embedded programs can undergo various types of testing “outside the device”, embedded programmers can fully embrace modern software development when writing test code.
Test code can be written in Node.JS, Python, or C++. There is no greater guilty pleasure than allocating large objects on the stack in test code.
When developing embedded programs for hardware, testing is absolutely crucial. The code you write will eventually end up in devices that will never be updated.
Sometimes your code will be burned into ROM chips. If a bug is discovered after release, the cost can be very high.
Therefore, firmly implementing modern testing principles, maintaining enthusiasm for writing easily testable code, and the determination to achieve 100% test coverage are invaluable qualities for embedded programmers.
For most programmers, performance is a simple question: “Is the code fast enough?”
If the code runs faster than required, then everything is great.
For embedded programmers, the situation is a bit more complex: “Is the code fast enough? Is the real-time performance adequate? Is the power consumption within plan?”
Besides the limits on RAM and code size, the speed of the code must also be fast enough.
But not too fast. Unused cycles help reduce power consumption. Lowering power consumption means longer battery life, less heat generated, and the possibility of using cheaper, slower CPUs in the next hardware upgrade.
We are always at odds with the underlying hardware personnel.Good embedded code design can facilitate the CPU to enter sleep mode, for example, by using explicit polling, allowing the system to sleep between polls. This also means that the code should not block for any reason.
Some embedded systems have hard real-time requirements, such as pacemakers. Any thread or task running in such systems must guarantee that it can return within a fixed time frame.
If the runtime is too long, the system may completely fail. Taking pacemakers as an example, such pacemakers would be classified as “defective”. Undoubtedly, writing code for heart pacemakers is not for the faint-hearted.
Most embedded media applications have soft real-time requirements – if a thread or task occupies CPU time longer than it should, system performance will degrade, but it will not completely fail.
Audio or video may glitch or stutter. The user interface may feel delayed. This is definitely not good, but it is not disastrous.
Writing code with good real-time characteristics is very similar to writing code that helps the CPU sleep; both utilize very short time slices, pay close attention to loop lengths, and never block. Understand your code paths. Avoid recursion at all costs.
How to Become an Embedded Programmer
What are the important qualities of an ideal embedded programmer?
You need to have a deep understanding of the fundamentals of computer architecture. Understand memory hierarchy, throughput bottlenecks, and hardware-level concurrency issues. Also, a strong mental grasp of how addressing works (pointers).
Additionally, depending on the type of software you need to use, you may need to master some industry-specific technologies. For example, at Spotify, you need to have a good understanding of networking.
If you feel that you do not have a deep understanding of the fundamentals of computer architecture?
Then please refer to the recommended books below:
-
“Computer Architecture: A Quantitative Approach”
This book is an absolute classic and is very well written. If you can understand chapters on memory hierarchy design and thread-level parallelism, you can handle all issues related to memory management, concurrency, or throughput bottlenecks.
-
“Modern Operating Systems”
This is also a classic. The author wrote: “In five years, everyone will run free GNU on their 200 MIPS, 64M SPARCstation-5.” This book was written in 1991, but the content far exceeds the author’s foresight. The chapter on memory management in this book is an excellent introductory reading on memory management.
-
“C Programming Language K&R”
The standard C programming book. One of the best programming books of all time. It might also be one of the best examples of technical writing ever. Of course, it is also one of the best-selling books of all time. A must-read!
-
“C Interfaces and Implementations”
This is a book about using C correctly, with examples of how to use the most practical data structures and algorithms to write maintainable and testable code.
-
“C Expert Programming”
This book delves deeply into the C language and its interaction with systems, covering memory management, pointer algorithms, compiler workings, and interrupt handling.
In addition, curiosity is also the most important quality of embedded programmers, the impulse to understand the principles of things, resilience, and the willingness to fully invest in work and continuously learn.
Author | Per Knytt
Translator | Wan Yue
Editor | Tu Min
Original: https://labs.spotify.com/2019/04/09/an-opinionated-anthropology-of-the-embedded-programmer-its-habits-and-habitat/
This article is a translation by CSDN, please indicate the source when reprinting.
Copyright belongs to the original author. For the dissemination and learning discussion of technology only, if there are copyright issues, please contact me for deletion.
If you think this article is good,remember to give it a thumbs up!

Recommended Albums Click the blue text to jump
☞ MCU Advanced Album 
☞ Embedded C Language Advanced Album 
☞ “Bug Says” Album 
☞ Album | Linux Application Programming Complete Works
☞ Album | Learn Some Networking Knowledge
☞ Album | Handwritten C Language
☞ Album | Handwritten C++ Language
☞ Album | Experience Sharing
