As an embedded software engineer, it’s really easy to think that all the software for the product is reliant upon the hardware. Most embedded developers that I encounter want to get a development board right out of the gate and start writing software, including myself most of the time. It’s not necessarily wrong, it’s just familiar and allows us to understand the complexities and issues that are associated with the hardware. The fact is though, if we designed our application code correctly, we could simulate and test most of our application code without the underlying hardware. There are several different ways that a developer can simulate their code to prove it out before integrating it with the hardware.
First, and perhaps the easiest way to simulate code is to simply develop and test it on a PC. Developers can use gcc or g++ to run their code on a computer and then generate and verify the output. For Linux and Mac developers it’s pretty easy to do this since they have direct access to a terminal that supports gcc or g++. Windows developers might find they need to install Cygwin or Mingw, both of which is pretty trivial to setup.
Earlier I mentioned that properly designed software can be simulated or tested on a PC. “Properly designed” software is software that is architected to minimize dependencies and designed to be flexible and isolated components. This allows them to be ran and tested without having to bring in the entire code base. In fact, they can be executed on the PC and the inputs can be fed in via a file and the outputs can be written to a file so that the results can be analyzed or even plotted if necessary.
A good example of how this technique can work is from a recent project that I was working on where we focused most of our software development on the end hardware since it was available. Eventually, we ran into an issue where an application algorithm just was not providing the expected output. It was close but just not quite right. This left us with an interesting question, “Was there some interaction with the hardware or RTOS or was the algorithm just coded wrong?”.
Normally, an embedded software developer would monkey around with the software on the hardware for weeks trying to figure out what could be causing the problem. In that type of situation, a team is gambling with finding a solution in any reasonable period of time. Due to the way that the software was designed and implemented, I was able to extract the algorithm, which one would have considered to be hardware dependent, and then wrap it in test code that I executed on a PC. I was then able to plot the output and compare it to the expected results. This allows for a developer to perform a sanity check on part of the software. If it matches, there is an issue with the way the algorithm is integrated into the embedded processor. If it doesn’t match, then the C implementation might need some adjustments.
This brings us to a second option for simulating embedded software, using a tool such as Matlab. Matlab can allow a team to simulate state machines, algorithms and dig into how a system will behave. In fact, in many automotive and aerospace applications teams will even let Matlab generate the embedded code for them and the team then just needs to maintain their model! In the above example, the algorithm I was working with had actually been simulated in Matlab. I knew exactly what it was supposed to output based on known inputs. Comparing the embedded C code simulation with the Matlab output then provided the details to show whether there was an issue with the algorithm or something in the embedded system.
Simulations can also help the embedded developer understand the system as a whole. Sometimes we blindly implement algorithms without really understanding how they work, what the inputs really mean and what the outputs should look like for those inputs. Sometimes we can get away with this, but if an issue arises, its often critical that the developer understand the details. The simulation can help with that especially if all the knobs and dials are included. A developer can increase parameter A and see how it changes the output. Then parameter B can be adjusted and so forth until they fully understand how the system can work. I’ve found that in many cases, it’s not the algorithm so much as its interaction with real-time behaviors that is the issue.
Embedded software developers don’t have to be dependent upon the hardware in order to get the job done. Certainly, it is great when the hardware is available, and it can allow developers to get through hurdles on the hardware. At the end of the day though, many algorithms and application features can be simulated and tested off target. In fact, this simulating can help to prove that the system is working the way it should or to help troubleshoot small nuances that might otherwise take considerable time to debug. Developers need to add off target simulation to their bag of tricks if they aren’t already doing so.