These ideas can help you simulate your firmware and embedded software quickly and efficiently.

Jacob Beningo

February 14, 2024

5 Min Read
How to simulate firmware and embedded software
Alernon77/iStock/Getty Images Plus via Getty Images

At a Glance

  • Consider these tools to simulate low-level software
  • If you don't need to focus on low-level code, focus on simulating application code
  • And then learn how to test your code on a host instruction set simulator

Teams developing firmware and embedded software often develop their software on-target. While there isn’t necessarily anything wrong with using a development board to understand the processor, peripherals, and devices you’re writing code for, on-target development is inefficient and time-consuming. Before running your code, you need to cross-compile it, erase the target, program it, verify it, and then start your debug session. 

Potential scaling issues exist in integrating with CI/CD and testing your code's work. Have you ever tried to run unit tests or coverage reports on an embedded target? It can be done, but it is usually quite painful to set up and get working correctly. Instead, there are several techniques that you can use to simulate your firmware and embedded software that will help you move quickly without the extra hassle. 

Simulation Technique #1: Renode.io and QEMU

Embedded developers' most significant challenge when simulating their system is how to simulate low-level software. The lowest levels interact with hardware, meaning special tools need to be in place to understand the hardware. Two tools are relatively popular when working with microcontrollers: Renode.io and QEMU. 

QEMU is a generic and open-source machine emulator and virtualizer. QEMU can emulate a variety of processors, allowing software designed for one kind of CPU to be run on another. This feature is precious for developers working on firmware or applications for embedded systems that use different architectures, such as ARM, PowerPC, MIPS, or x86. The downside to QEMU is that you’ll find there is not a lot of support for microcontrollers. You’ll definitely find some of the most popular, but in many cases, there will be significant gaps in what is available and supported. For example, you may find that your microcontroller is available along with a USART peripheral, but timers and ADCs are not supported. 

Related:How to Debug a Microcontroller Using Visual Studio Code (VS Code)

Renode is a development framework that accelerates IoT and embedded systems development through physical hardware simulation. Developers can simulate CPUs, peripherals, and devices external to the microcontroller, like sensors. There is support for peripherals like I2C, CAN, SPI, Flash, USB, UART, etc. Renode is more embedded-focused than QEMU. So far, I’ve found it has much better support for embedded targets. 

Simulation Technique #2: Focus on the Application Code

I often mention that an embedded software architecture is a tale of two architectures. The first is high-level business logic that delivers customer features and doesn’t care about the underlying hardware. The second is the real-time code that interacts with the hardware and does care about what hardware is there. 

Related:How to Debug a Hard Fault on an Arm Cortex-M

When you want to simulate your embedded application, you should determine whether you need to simulate that hardware-dependent code. Most customers only care about the features they interact with. If that is the case, simulating your application code is far more critical than the low-level code. If you can develop and run your application code before the hardware is ready, you can get it in front of your customer sooner. If you get the code in front of the customer, you can get their feedback and adjust sooner rather than later. The earlier you make changes, the less money you spend on those changes and the less time-consuming those changes are. 

Simulating application code on a host is usually nothing more than adding a build target for that host. For example, if you are writing in C or C++, you invoke a build target for your host that is set up to compile your application code. You can then run the generated executable. You might think this poses an issue for systems using an RTOS. It doesn’t usually. Many RTOS vendors provide versions of the RTOS that can be compiled on Windows, Linux, and MacOS. That means you can run your application code on top of your selected RTOS! You’ll find that you could add code to visualize system behaviors, dump data to databases, etc. 

The question you should be asking yourself is whether you need to simulate that low-level code. If not, focus on simulating your application code. You’ll discover that by doing so, you’ll also naturally write code decoupled from your hardware. The result is more reusable, portable, and scalable software. 

Simulation Technique #3: Leverage Instruction Set Simulators

Another type of simulator you can leverage to test your code on a host is an instruction set simulator. These simulators allow you to run your application code and your firmware. They do this by directly running the low-level instructions for your processor and peripherals in your IDE. You’ll find these simulators in IDEs like Keil MDK, IAR EWARM, MPLAB X, etc. 

These simulators often vary in their capabilities. For example, you may find that the processor core can be simulated, but peripherals are not supported. You can verify that instructions run correctly, algorithm results, and so forth, but you can’t prove that interactions with peripherals work. While this may not be optimal, it can allow you to run your code off target and make rapid progress on higher-level code without unique instrumentation. 

I’ve personally found these to be useful if I’m developing some algorithm that I want to test in the IDE. I’ve found that the other two techniques are far more helpful, partially because the instruction set simulators don’t include support for peripherals. Their limitations make them not as beneficial as you might hope.

Conclusions

Leveraging simulation is a great modern technique that can help you to develop your firmware faster. The more work you can do off-target, the more efficient you’ll be because you can avoid those long compile, program, and debug cycles. You’ll find working off-target that writing unit tests and running code in simulation forces you to write more portable and reusable code. Running off-target forces you to write decoupled code that abstracts out the hardware. The result is a cleaner, scalable, and testable software implementation you can verify before your hardware is built. 

About the Author(s)

Jacob Beningo

Jacob Beningo is an embedded software consultant who currently works with clients in more than a dozen countries to dramatically transform their businesses by improving product quality, cost and time to market. He has published more than 300 articles on embedded software development techniques, has published several books, is a sought-after speaker and technical trainer and holds three degrees which include a Masters of Engineering from the University of Michigan.

Sign up for the Design News Daily newsletter.

You May Also Like