Comments on How do I test firmware code for my embedded system that also controls hardware peripherals?
Parent
How do I test firmware code for my embedded system that also controls hardware peripherals?
I am soon finished writing the firmware for a relay control board that will help us automate some of the electrical tests we run at the company I work. In essence, the board more or less just turns relays on and off by communicating with an external PC via USART - simple enough.
The challenge I am facing now, is that I want to test the correctness of my firmware before we start deploying these boards around the facility, but I'm not sure how to do that. To me, firmware is special in that regard.
In hardware/analog electronics everything exists "outside" the PC. If I want to check if my low pass filter works correctly I apply an excitation waveform at the input, measure the output and test if it matches my expectation.
In software everything exists "inside" the PC. The variables, the memory, the commands and results are all available to see in the PC as long as you know where to peek.
But firmware is sort off in-between. Some things happen on the outside world (control of hardware, switches, interfaces ...) while some things happen inside the embedded system (incrementing counters, handling interrupts, controlling program flow and logic, etc.).
What are some of the ways I can test my firmware to see if it operates correctly?
Post
If the main function is logic and activation of on/off signals, and the number of output combinations and their timing needs are not prohibitive, The minimal test with actual mcu hardware, actual relays, and indicator lights to make visible the relay output. You could also use an oscilloscope with digital inputs, or any gpio device to record the outputs in more detail. You'd have to arrange whatever inputs needed to exercise all your code.
Big agreement with Lundin's answer about doing not only black-box design verification testing (ie as opposed to production testing, where you take for granted the design is robust if executed to plan), but also subsystem verification testing, since it's just so common that black-box-only misses failmodes that could've been elicited at subsystem level. Beyond that you're entering into the world of DFMEA, which will make your designs better but also tremendously laborious.
Design verification of the electronics seems to take a different pattern from that of the firmware. In both cases, besides the actual function of the device, the real challenge lies in demonstrating robustness, which can be idiosyncratic in both firmware and hardware, but in different ways.
Implicit functions of hardware, IME, seem to be dominated by the pattern "withstands X Y Z". For an industrial setting, there are also common weak points related field wired hook ups (ie when your test equipment gets integrated into an industrial automation system on a production line, which commonly have field-wired terminal strip type construction in the control boxes). Special attention should be paid to these miswire scenarios, including multiple failures (ie wires exchanged, line of connections shifted down on a terminal strip).
When faced with this in the context of "operational qual" for a factory test equipment of a product which has with 24V "high speed" plc signaling, I took the time to generate all possible combinations of miswiring in its 8p connector (in that case, the miswiring originating from production errors in the internal wire harness of the DUT), identify the stress cases, reduce them for symmetry (ie since there were multiple output lines of identical design), which resulted in qualitatively unique stress-cases and non-stress DUT-test-fail-cases that numbered slightly more than the number of connections, something like 10 vs 8p connector in that particular case. We then constructed physical specimens of the "bad" DUT's with each of those miswirings, to demonstrate that the test equipment design survived them, caught the failure, and kept going normally -- and this was repeated for several specimens of the test equipment, each vs all specimens of the "stress-case-generating" DUT, as well as non-stress specimens of "good DUT" and "bad DUT" representing the known test-fail-cases of the DUT (in that case not reduced for symmetry, since it was an operational qualification as well as a design verification - but do reduce if multiple DUT-production-failmodes result in the same production-test-failure, because the purpose of the OQ was to show the test-equipment function, which is catching the DUT-test-fail-case, regardless of which DUT-production-failmode caused it.
To back up a little bit, the take-home of this might be that you should be clear (ie we're talking spreadsheets here) about:
- what are the functions of the DUT, and of the test-equipment
- distinguish between design-failmode and production-failmode, of both the DUT and test-equipment
- aggregate DUT-production-failmodes into DUT-test-fail-cases
- have a systematic process to identify design-failmodes connected to implicit functions, which are most often functions which can be verbalized in the form "withstands X Y Z"
- special attention to "withstands defective DUT stress-cases" and "withstands miswire in the context of the test-equipment being integrated into field-wired industrial automation"
Returning to the firmware, you can apply the same principles, but attempting a generative approach with spreadsheets (systematically identifying failmodes), which is a huge chore but at least doable for the hardware, gets out of hand entirely for nontrivial firmware projects. The possibility space includes all the known weaknesses of the C language, for starters. The "CERT" guidelines can be starting point there.
Previously mentioned difference in emphasis in implicit requirements between firmware vs hardware. For firmware, it's not as much dominated by the "withstands external-condition-X" pattern. Although there's some of that too, e.g. improper string input to the UART. But you also have things like not crashing (test: clear-on-reset variable and watchdog timer), not having unbounded memory use (test: stack paint and guard, best-practice: no heap, possible practice: periodic resets), not corrupting your data in a million ways that C lets you do, not using uninitialized data, etc etc etc.
But those above are all one-offs and and I haven't yet worked on anything with a comprehensive approach, since for better or worse the quality people I've encountered have all been too scared to touch the software and firmware side of things, and basically left the geeks to their own devices.
Other helpful mentalities to reduce the combinations of the unpredictable in firmware, are the principles of functional decomposition (helps testability), encapsulation (reduces cross-interactions), and actual functional programming (guarantees testability in principle) even if you can't realize it across the board due to the nature of the platform/language/stateful system. But it should be possible to structure the code so that like 90%+ is written in a style that facilitates verification and behaves consistently when parts of the system not directly connected to it are changing.
Also since you mentioned it, mechanical relays can introduce electrical transients which if you're just starting out in designing this stuff, they can stress both the relay's drive circuit, and the relay contact themselves can be overstressed by certain loads and are vulnerable to the contacts spot-welding themselves closed-circuit ... but that's its own subject.

0 comment threads