Better even at the lowest levels - Embedded.com

Better even at the lowest levels

For the past several years, I've been presenting a talk at the Embedded Systems Conferences called “Writing ISRs in C++.” The talk is aimed at programmers who've already written interrupt-handling code in C and would like to see how to use the features of C++ to make such code even better. More recently, I started presenting a talk at the Software Development Conferences entitled “C++ on Bare Metal”, which presents ways to use C++ features such as classes, inheritance, and templates to model memory-mapped devices as tidy, yet efficient, abstractions.

This fall, both conferences took place at the Hynes Convention Center in Boston during the same week. I was one of two or three lecturers who spoke at both conferences, and I presented both talks. This time, I was struck by the number of attendees at each talk who were not so much interested in how to write such low-level code in C++, but rather why you'd want to write it in C++ rather than in C.

Ten or twelve years ago, C++ was still a novelty in the embedded world. Most of the developers at my conference lectures were as interested in why as in how, so I typically devoted time to both. Over the years, more and more programmers came to my talks already sold on why and were just interested in how. For the past few years, I've focused on how because that's what I thought my audiences wanted to hear. That's why this recent uptick in concern about why caught my attention.

So the question is “Why would you prefer to write low-level code that directly manipulates hardware and handles interrupts in C++ rather than in C?” If C and C++ were different languages, as different as say C++ and Ada, or even C++ and Java, this might be harder to answer. But C++ is essentially C with a bunch more stuff. I believe the only substantive features in C that aren't also in C++ are some built-in support for numerical computing. Device drivers and interrupt handlers don't do much number crunching.

C++ provides all the facilities of C for controlling hardware efficiently. At times, controlling hardware can be tricky and error prone. C++ has features that C lacks, principally classes and templates, that let you bundle device controls into components that are, as Scott Meyers would say, “easy to use correctly and hard to use incorrectly.”1

For example, many processors have an interrupt controller with an interrupt-pending register. That register has a bit for each interrupt-generating device that indicates whether an interrupt is pending for that device. An interrupt handler for a given device should clear that device's interrupt-pending bit after handling the interrupt.

In C, you might represent the interrupt-controller register as a struct, such as:

struct interrupt_controller{    device_register INTMOD;    device_register INTPND;    device_register INTMSK;};   

and access the device via a pointer:

interrupt_controller *const ic    = (interrupt_controller *)0x3FF4000;   

With most control registers, you clear a bit using a masking operation, such as:

ic->INTPND &= ~button;   

Here, button is presumably a one-bit mask that corresponds to the interrupt-pending bit for a push button. However, interrupt-pending bits should be cleared atomically, and few, if any, processors implement the &= operation so that it's atomic. Thus, many interrupt controllers are wired so that you clear an interrupt-pending bit by assigning rather than and-ing to the register, as in:

ic->INTPND = button;   

This behavior is unusual. You could easily forget that this register is special and lapse into clearing the bit in the conventional way, which in this case, would be incorrect.

In C++, you can package this protocol inside a class that's easy to use and hard to misuse. That class might look like:

class interrupt_controller{public:    void clear(device d)    {        INTPND = d;    }    ~~~private:    device_register INTMOD;    device_register INTPND;    device_register INTMSK;};   

Then you can clear the bit by calling:

ic->clear(button);   

Can you write a function similar to that in C? Sure you can. You can write:

void ic_clear(interrupt_controller *ic, device d){    ic->INTPND = d;}   

and call it using:

ic_clear(ic, button);   

Not a whole heckuva lot of difference between this and the C++ version, but I'd give C++ a small notational edge. However, the much more compelling advantage for using C++ is that if you forget the function exists and write:

ic->INTPND &= ~button;   

your C++ compiler will complain–and rightfully so–because the INTPND register is private to the interrupt_controller class. This reduces opportunities to misuse the interrupt controller. In C, the &= expression would compile and link, and then you'd get to experience the joy of hunting for the bug at run time.

My experience is that, with modern C++ compilers, introducing such abstractions rarely has any runtime cost. On the rare occasion that it does have a cost, you can always strip away the abstraction and use C++ as if it were C.

Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. For more information about Dan Saks, visit his website at www.dansaks.com. Dan also welcomes your feedback: e-mail him at . For more information about Dan .

1. Meyers, Scott, “The Most Important Design Guideline?”, IEEE Software , July/August 2004.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.