New embedded systems books
Jack recommends some interesting reading for embedded systems engineers.
Today, a search of "embedded systems" in the books section of Amazon.com turns up 1,600 hits. How many would have turned up two decades ago when this magazine was in its infancy? Well, Amazon.com didn't even exist back then. And neither did the web. There were no books on embedded systems, either.
Surveys show the average software developer reads but one technical book a year. Yet the quantity and quality of books on embedded systems and related subjects is impressive. It's rare that I run into one that doesn't inform or challenge my thinking. Recently a couple of new books came out that are very worthwhile.
Some months ago James Grenning and I had a point/counterpoint in these pages about test-driven development (TDD). He's has been writing a book on the subject for some time, and it should be available shortly after this column goes to press. He generously sent me a PDF of the nearly final version.
Test Driven Development for Embedded C (I think there should have been a hyphen in the adjectival phrase) is hands down the best book on the subject. This is an amiable, readable book with an easy style that is fairly code-centric, taking the reader from the essence of TDD through mastery using detailed examples. It's a welcome addition to the genre as the book is completely C-focused, unlike so many others, and is specifically for those of us writing firmware.
James skips no steps and leads one through the gritty details, but always keeps the discussion grounded so one is not left confused by the particulars. The discussion is laced with homey advice and great insight. He's not reluctant to draw on the wisdom of others, which gives the book a sense of completeness.
The early phases of a TDD project are mundane to the point of seeming pointlessness. One writes tests to ensure that the most elemental of things work correctly. Why bother checking to see that what is essentially a simple write works correctly? I've tossed a couple of books on the floor in disgust at this seeming waste of time, but James warns the gentle reader to adopt patience, with a promise, later fulfilled, that he'll show how the process is a gestalt that yields great code.
TDD does mean one is buried in the details of a particular method, a particular test, and the path ahead can be obscured by the tests at hand. If you're a TDD cynic or novice be sure to read the entire book before forming any judgments so you can see how the details morph into a complete system accompanied by a stable of tests.
Better than any book I've read on the subject, Test Driven Development for Embedded C lays out the essential contrast between TDD and conventional write-a-lot-of-code-and-start-debugging. With the latter technique, we're feeding chili dogs to our ulcers as the bugs stem from work we did long ago, and are correspondingly hard-to-find. TDD, on the other hand, means today's bug is a result of work one did 10 minutes ago. They're exposed, like ecdysiast Gypsy Rose Lee's, uh, assets. A test fails? Well, the bug must be in the last thing you did.
One of TDD's core strengths is the testing of boundary conditions. My file of embedded disasters reeks of expensive failures caused by code that failed due to overflows, off-by-one errors and the like. TDD—or, at least James' approach to it—means getting the "happy" path (his word—the basic functionality) working and tested, and then writing tests to ensure each and every boundary condition is also tested. Conventional unit testing is rarely so extensive and effective.
The problem with early testing is that the target system will not be available. In TDD, stubs replace the hardware, the stubs exactly mirroring the operation of the I/O. Examples show, for instance, stubbed-LEDs and other components, but one would imagine a complex device could require quite a bit of simulation code. The book is less clear about how one eventually merges the target hardware with the stubbed-out code. The stubs are a hugely valuable asset in terms of starting testing very early, but of course they are merely facsimiles of reality. NASA's mantra "Test what you fly, fly what you test" may pose problems for TDD.
Embedded TDD revolves around creating a test harness, which is a software package that allows a programmer to express how production code should behave. James delves into both Unity and CppUTest in detail. Although the latter is targeted to C++ applications it does support C. Each test invokes creation and teardown routines to setup and remove the proper environment, like, for instance, initializing a buffer and then checking for buffer overflows. I found that very cool.
The book goes much further than showing how to evolve code in concert with the tests. For instance, it shows design patterns based on Bob Martin's SOLID principles (read the book for the details!) that illustrate design techniques that lead to innately testable and reliable code.
It would have been interesting to see more on design, which feels relegated to a minor role. And I wished for some comments on TDD in a regulated environment, like avionics.
But Test Driven Development for Embedded C is an active-voice work packed with practical advice and useful aphorisms, like "refactor on green" (in other words, get the code working first, and when the tests pass you can improve it if necessary). I was also fascinated to find the book had been in beta for some time, and a wide range of developers had made critiques and suggestions for improvement. Rather agile-like.
Above all the book stresses having fun while doing development. And that's why most of us got into this field in the first place.