Testing with Hardware
Where practical, tests with hardware should be
automated. Let’s look at three kinds of tests we can
create that interact with the hardware:
Automated Hardware Tests
Your embedded hardware will probably have areas that
are well-suited for
automated testing. Other areas of the hardware will probably need
special instruments to test hardware functionality.
Where possible, you should write tests that help you learn what
the hardware does and give you confidence that the hardware is working.
As the inevitable hardware changes happen, your tests can
help you see when a new hardware design has problems.
You might find that some of these tests are valuable during production
and may want some to be included in a built-in test sequence that ships with the
product.
Let’s say your design uses a Common Flash-Memory Interface
(CFI)--compliant device. There are
operations we can use to interrogate the flash memory device
to see whether it is responding properly. For example, when a
0x98 is written to flash offset 0x55, a CFI-compliant flash
memory device will respond with Q, R, and Y when offsets 0x10,
0x11, and 0x12 are read, respectively. The device must be
reset after the query by writing a 0xff. This simple test,
run on the target, will pass if the device is responding
properly. It’s not a thorough test, but it is a quick sanity
test.
| | TEST(Flash, CheckCfiCommand) |
| | { |
| | FlashWrite(0x55, 0x98); |
| | CHECK( FlashRead(0x10) == 'Q'); |
| | CHECK( FlashRead(0x11) == 'R'); |
| | CHECK( FlashRead(0x12) == 'Y'); |
| | FlashWrite(0, 0xff); |
| | } |
In the following story, the tests that software
developers put together to test-drive their code
became an invaluable tool for their hardware
developer colleagues.
No Fear of Change
by Randy Coulman, embedded software development engineer
We started a new project involving several custom
hardware devices, all with an embedded processor
and FPGA. We resolved to avoid problems we had in
the past with buggy FPGA designs and fixes breaking
other features. As with most projects with
concurrent hardware/software development, we needed
to start developing the software well before the
hardware was available. We had a mostly complete
spec for the hardware.
We decided that the best approach was to write
tests for the hardware. We started with the most
foundational feature of the hardware and wrote
tests for it. We called these hardware acceptance
tests. Since we didn’t have hardware yet, we also
wrote a simple simulation that would pass the test.
We continued on this way, writing tests for
features of the hardware and simulations that
passed them. We used TDD to write unit tests for
our software as we went along as well.
When the hardware became available, the integration
effort was much shorter and simpler than it had
been in the past. We encountered three types of
problems:
-
Places where certain language constructs worked
differently on the embedded processor than they
did on our development platform
-
Places where the compiler was generating memory
access code that the FPGA didn’t support
-
Places where we misinterpreted the hardware spec
Initially, the hardware acceptance tests were for
the software team’s benefit. Over time, the EEs
came to trust our tests more and more. We had
implemented a set of automated builds that would
compile the software on the desktop platform and
run all the tests against our hardware simulation and
then install the latest software and FPGA binary on
our target hardware, run the hardware acceptance
tests (and some others as well), and report the
results. At the request of the EEs, we added a
“sandbox build” where they could drop in a new FPGA
binary and have the automated tests run against it.
Once it was passing all of the tests, they would
then deliver the binary for us to integrate into
the system. This allowed them to verify their work
even if they were working in the middle of the
night while the software engineers were home in
bed.
These hardware acceptance tests have caught several
regressions in the FPGA, allowing our EEs to upgrade
their toolset, recompile their designs, and be
confident that they didn’t break anything. Overall, the
integration effort was much less than in the past,
and we’ve been able to continue to add new features
over time with great confidence.
Partially Automated Hardware Tests
The LedDriver example,
completed in the previous chapter, shows how a
hardware-dependent piece of code can be tested
outside the target. But, how do you know it really
turns on the right LEDs? The
LedDriver thinks
it is controlling LEDs, but any number of mistakes
could lead to software that thinks it is doing the
right thing but actually does nothing or possibly
something harmful. So, you have to make sure the
last inch of code, right next to the
hardware, is right.
What kind of problems could we have with the
LedDriver? It can be
initialized with the wrong base address. It is
possible that you misread the spec and the bits are
inverted. It is possible that the schematic and the
silk screen don’t match. Maybe some of the
connections on the board are not right. You’re not
just testing software; you’re testing an embedded
system. So, to be sure that the
LedDriver really turns on the
right LED at the right time, you have to look at it!
This is a good application for a partially automated
test. A partially automated test displays a cue
prompting the operator to manually interact with the
system or view some system output. In this case, we
would verify that a specific LED is either on or
off. This would be repeated for each LED. This could
also be part of built-in test capability shipped
with the product or used to support manufacturing.
Manual tests are more expensive to run than
automated tests, but they can’t be completely
avoided. If we are effective at minimizing the code
that depends on the hardware, it is likely that the
hardware-dependent code will not be changed very
often. Consequently, the manual acceptance test will
likely not need to be rerun as often. You will have
to decide when these are run. A new hardware
revision or changes to the hardware-dependent code
would trigger a manual retest. You might also
consider short and long versions of the partially
automated tests, running the short one regularly and
the longer one less frequently or when necessitated
by change.
Tests with External Instrumentation
Special-purpose external
test equipment can help automate hardware-dependent
tests. This story was ahead of its time.
In the late 1980s, we developed a digital
telecommunication monitoring system that monitored
1.544 Mbps (T1) signals. A major part of the behavior
depended on a custom application-specific integrated
circuit (ASIC). The ASIC monitored the T1 signals in
real time; our embedded software interrogated the
ASIC and reported performance information on demand
and alarm conditions as they occurred. Testing this
system required specialized test equipment to
generate T1 signals and inject errors found in the
real world.
After tiring of the manual tests that involved poking buttons
on the T1 signal generator, our test engineer, Dee, dug into
the instrument’s capabilities and discovered it could be
controlled through a serial port. Dee started writing test
scripts. Her scripts instructed the external signal generator
to corrupt the digital transmission with a specific bit error
rate, then interrogate the system under test to see whether it
reported the correct diagnosis. Incrementally, she automated
her manual procedure, growing the automated test suite each
day. The regression test grew to have wide functional
coverage. This practice allowed Dee to report defects to the
group within one day of their introduction.
Dee was not popular at first, walking from the lab
smiling with a fresh list of bugs. After a while,
the development team met the challenge and grew to
rely on the morning bug report. A healthy
competition developed; developers worked diligently
to produce bug-free code. They used the test scripts
before releasing to check their work. The quality
improved.
We had zero defects reported from our installed base
of thousands of units. Many other products developed
at about the same time, by teams using manual test
practices, had long bug lists and expensive field
retrofits. This test investment had a great return.