How to run your code
What is involved in writing software? That is a rather broad question. I will start with considering the situation if you are writing a program for your desktop computer. The steps are:
- design the program
- code it
- build it, correcting syntax errors etc.
- run it [maybe using a debugger or perhaps just hoping for good luck]
That is straightforward enough. But life for an embedded software developer is just not that simple …
Developing embedded software has some similarities to working on the desktop. Designing the code is likely to use similar techniques. Coding it is quite similar with the choice of programming language much the same. A certain minority of embedded developers need some “low level” skills – they are working closer to the hardware and need to be comfortable so doing.
Building the application is the first point that big differences appear. The required tools typically run on a desktop computer, but generate code to execute on some other target. At this stage the code may be integrated with some externally sourced components – typically a real-time operating system [RTOS]. The link/location process takes care of creating an executional image that corresponds to the target environment – using appropriate memory address space(s), mapping on to peripherals etc.
Executing the code is when things get very exciting. It sounds easy enough: put the code on the target and run it. However, it really is not that simple. Some challenges:
- getting the code onto the target
- connecting a debugger to see what is happening
- figuring out the real-time behavior of the code
- is the target hardware available?
- is there enough hardware for all of the software team?
- is the hardware reliable or buggy?
It is best to stand back and consider that there are broadly 3 ways to execute embedded code, each of which has advantages and disadvantages:
- run the code natively on the host computer
- execute the code using some kind of simulation technology
- run the code on the real target with a connected debugger
Each of these approaches is appropriate at different times during the development cycle and different members of the team have different needs and priorities that may be addressed by one of more of them. I will take them in turn:
Native code execution
Building the code for the host [desktop] computer is a possibility. This is a long way from executing it on the target hardware, but it can suffice for testing algorithms etc. Various vendors offer tools to enable the approach to be taken further, with simulation models of peripherals and even the operation of an RTOS included. The attraction of this approach is that it is readily available and cheap [or even free!].
Simulation
The term “simulation” means different things to different people; broadly it describes something that is not “the real thing”. In the context of embedded code execution, it can mean [among other things]:
- logic simulation, where the electronics of a complete system is simulated in detail
- instruction set simulation, where the operation of the CPU is simulated using a fast simulator; other system components may be simulated in different ways
- emulation, which is much the same as logic simulation, except that some dedicated hardware is used
In all cases, it would be usual to connect a conventional debugger to the simulation. The developer would then experience something very close to using a real target, though with reduced execution speed, but with very good execution behavior visibility.
Real target execution
Many engineers see an actual target as the only realistic option. For some applications this might be true. However, even with the best modern debug tools, the visibility of the code operation has limitations [interestingly much more so than with in-circuit emulation technology used in the past] and real-time behavior may be affected by the debugger. There are broadly two distinct ways that on-target debugging and testing can be performed:
- Using a conventional debugger, connected to the target most commonly using a JTAG interface. This is commonly referred to as “stop and stare” debugging.
- Using an on-target agent that buffered code execution trace information that is buffered and uploaded to the host periodically for later, off-line analysis. For complex, probably multicore, designs this approach may be the only viable option.
These approaches are not competitive. A given developer might use all three, at different times, as the need arises.