Object-oriented firmware (2)

Dynamic memory is a common feature of OO implementations. Typically, an object instance is created and used. Some time later, it is destroyed, and its memory returned to the pool. This is a perfectly good way to work, but it can introduce problems of its own, notably memory fragmentation. This is more serious the smaller the available reservoir of memory.

In firmware, fragmentation could lead to a system crash, and that’s a serious matter. Especially as the use of dynamic memory is not necessary. In some applications, it is actually unhelpful. In our development environment, it would only have introduced new problems, with no clear benefit.

Our applications started by initialising the system (the microcontroller and its peripherals), and then initialising the application itself. Execution continued by means of (RTOS and application) tasks, created and scheduled during initialisation. Because these tasks executed repeatedly, with time in between for other tasks to run, their variables needed to be static (persistent across task invocations).

The same applied to other system resources, such as timers. Creating and destroying them was a needless overhead, when many (all?) of them were required to function across task invocations. So we decided to allocate all necessary memory during initialisation, and make it persistent. Thus we avoided the problems associated with dynamic memory by simply not using it.

Don’t misunderstand. We didn’t avoid problems by crippling our applications, or the way they worked. We didn’t run away from problems, or hide from them. Rather, the way we worked allowed the simplification of not using dynamic memory. And firmware is, or should be, as simple as it can be (but no simpler). The lack of resources (principally memory) that our target platforms exhibit makes this necessary. But we take advantage of this simplicity to improve our firmware, working with it to keep things as simple as they can be.

Even the smallest firmware application is a complicated thing, perhaps too complicated for the human mind to encompass all in one go. Any simplification is a bonus, something to be sought after. So avoid dynamic memory, or any other problematic feature, if you can do so without compromising your end product. The simpler you can keep things, the easier it will be to add those unexpected new features that the Product Manager now wants. [How could she possibly want to use the product like that?!!!]

 

As ever: please leave a comment, and thanks for dropping by. The purpose of this blog is to stimulate discussion of all things firmware. Will you gift us with your thoughts?

Advertisements
Object-oriented firmware (2)

Object-Oriented firmware

OO is a mind-set, a design style. We can design OO firmware, as long as our style favours minimalism rather than the baroque. It’s desirable for our implementation language to offer native support for OO, but it isn’t essential. We can implement an OO firmware design in assembly language if necessary. OO is just a way of working.

The interesting thing about OO firmware is how we implement it. Our team chose not to use C++ because, at the time we were making these decisions, it was ten years since I’d written C++ professionally, and I was the only member of the team with any C++ experience at all.

C++ is the most complicated language I’ve ever encountered. It’s a high-ish level OO language that can still connect directly to the hardware, which is important in firmware. But it takes a lot of time and effort to learn to use C++ well. Read this book to see what I mean. [It’s a Very Good Book, by the way.]

We chose to stick with C, and I call our approach classes-in-C. Simplicity is a wonderful thing in firmware, and classes-in-C is nothing if not simple. The OO features we really need for firmware are easily implemented in C, so this simplicity comes quite cheaply.

Each .c file encodes a class, and usually the one and only instance too. Private class data is implemented as static class variables. [Static variables are persistent, so they continue to exist, and retain their values, in between invocation of the class task(s).] Private class methods are declared and defined within the .c file. The class API comprises the methods defined in the class header file, where we also include any necessary typedefs and symbolic constants.

Our RTOS used co-operative multi-tasking, which executes each task to completion before executing the next. This allows a task owned by one class to call the API methods of another class. Pre-emptive multi-tasking simulates continuous execution of all tasks, so a call to an API method could occur while a non-interruptible operation is taking place within the class/instance. This is not an insuperable problem, but it is a difficult one, and it can be completely avoided by using the simpler and quicker method of task-switching. We only need to ensure that no task runs for too long, closing out the others.

Identifying classes in firmware is often quite straightforward. We need a suite of peripheral driver classes, one for each device type. We made a rule that each peripheral device is owned by exactly one class. This has the happy side-effect of encapsulating interrupt processing inside the driver class, where the thread-safety aspects are handled, contained and hidden. [In the unusual event that two or more classes needed access to the same peripheral device, we just added a new node (class) to arbitrate incoming access requests, and share the peripheral device out as required.]

An API method and a table of function pointers serves to implement the Command pattern quite effectively. We have no real need of polymorphism. And so we found it for the other OO features that OO languages support natively. As simple as our implementation is, we found that we could use any software concept or convention that was truly appropriate in a firmware context. Using C did not seem to place any significant constraints on our designs, or on our choice to use an object-oriented design approach.

I have seen some quite clever schemes to emulate more native OO support in C than we had. They all seemed to rely heavily on macros, which I don’t like because a symbolic debugger can’t execute a macro. We all try to avoid debugging at this level, but sometimes we have to, and it isn’t good if you can’t follow the execution of the code properly. You can guarantee that the rogue write-operation you’re searching for will happen in the macro code, where we can’t see it! Have you had any success with schemes like this?

This post may be edited to correct it, or to expand on points made in comments. Please leave your own, and join our discussion!

Object-Oriented firmware

Donald’s Trap

In 1983, I was a hardware designer with a degree in Electronics, and four years practical experience. I thought this made me a firmware designer too. So, initially, firmware was hard work for me, and I produced one bug-ridden big ball of mud after another.

Because I knew what skills a firmware designer needed, and I had those skills, I tried to improve my work by improving my command of them. Of course, it didn’t work. I was caught in Donald’s Trap.

I’ve named this trap after Donald Rumsfeld, an American politician who famously referred to unknown unknowns — the ones we don’t know we don’t know. Donald’s Trap works because you don’t know you’re caught, so you don’t look for a way out. There were skills I needed to be a firmware designer, skills I didn’t know I needed. I didn’t know that I didn’t know, so I was stuck.

I’m not the only firmware designer to be caught like this. Some spend their careers there. I worked for one not too long ago.

Firmware designers are a solitary breed. We don’t seem to socialise, online or elsewhere, as much as our software peers do, so our expertise is less easily propagated. Some firmware designers are hardware designers lumbered with firmware as a side-line. It isn’t only designers who sometimes believe a hardware background is sufficient to write firmware. Employers often think so too.

I escaped by accident, because I love design. At first I thought I was learning all that prissy software stuff for my personal benefit. As I started to really appreciate software design, I also came to realise that this stuff applied to firmware too!

I learned from Cope and Uncle Bob (and many others) about software patterns, OO design, SOLID principles, TFD, DRY, and the like. Finally, my firmware improved. Adding new features became easy. There were less bugs, so I could spend more time doing design. Life was good.

 

Edited to add: would visitors leave a comment, please? I’d appreciate your feedback. Nice to see you; thanks for dropping by!

Donald’s Trap