Embedded software may have come a long way in the last 10 years but we generally did the following:
- for algorithms that didn't depend on the target hardware, we simply had unit tests that were built and tested on a non-embedded platform.
- for stuff that did require the hardware, unit tests were conditionally compiled into the code to use whatever hardware was available. In our case, it was a serial port on the target pushing the results to another, more capable, machine where the tests were checked for correctness.
- Depending on the hardware, you could sometimes dummy up a "virtual" device on a non-embedded platform. This usually consisted of having another thread of execution (or signal function) changing memory used by the program. Useful for memory mapped I/O but not IRQs and such.
- typically, you could only unit test a small subset of the complete code at a time (due to memory constraints).
- for testing of time-sensitive things, we didn't. Plain and simple. The hardware we used (8051 and 68302) was not always functional if it ran too slow. That sort of debugging had to be done initially with a CRO (oscilloscope) and (when we had some more money) an ICE (in-circuit emulator).
Hopefully the situation has improved since I last did it. I wouldn't wish that pain on my worst enemy.