Guidelines for using C++ as an alternative to C in embedded designs: Part 2
Encapsulation of Expertise Using C++ Objects
By Colin Walls
Embedded.com
(04/12/08, 01:02:00 AM EDT)
Click here to see Part 1.

An object oriented language such as C++ enables data and code to be encapsulated into objects, which can then be utilized without any knowledge of their internal workings.

This approach is particularly attractive when a large software team is used for a project. Different members of the team will have different knowledge and capabilities and it is the encapsulation of that expertise which is so useful.

This perspective is strongly emphasized in embedded software development, where there may be a wide spread of expertise from the "embedded expert", who is used to working close to the hardware with drivers etc., to the applications specialist, who knows little about the ways that embedded systems differ from desktop computers.

If the expertise of the embedded expert can be encapsulated into C++ objects, the applications specialists can proceed efficiently and safely. In the case study that follows, a classic embedded system problem will be addressed: the handling of a write-only port.

Case Study: A Write-only Port
It is not uncommon for an embedded system to have a device (a port) to which a data value may be written, but from which no data may be read " a write-only port.

Although this sounds like a joke played upon software engineers by the hardware designers, it is really just a design which makes economic use of hardware design elements. Handling a write-only port in software is reasonably straightforward " it is just a matter of keeping a "shadow" copy of the last data written.

The matter becomes more complex when, as is often the case, the bits in the port have a selection of functionally unconnected purposes. This means that a number of parts of the software need to use the port correctly, which presents some challenges.

A Solution in C It is entirely possible to implement a solution for write-only ports in C. Here is some possible code:

extern int ports[10];
int shadow[10];

void wop_set(int port, int bit)
{
        int     val;
        val = 1 << bit;
        shadow[port] |= val;
        ports[port] = shadow[port];
}

void wop_clear(int port, int bit)
{
        int     val;
        val = ~(1 << bit);
        shadow[port]     &= val;
        ports[port] = shadow[port];
}

Although this code will do the job, it suffers a number of shortcomings:

1) There is no provision for initializing the data. Something (the linker probably) would need to map ports[] onto a sequence of write-only ports. At some point, an initial values need to be written to the ports and to shadow[].

2) There is no mechanism compelling programmers to use these functions " accessing ports[] and shadow[] directly is a possibility, even if it is ill-advised.

3) The code is not reentrant, which would be a problem if a real-time operating system (RTOS) is in use or even if interrupt service routines might need to access the ports. Reentrancy could be implemented, but would be very application specific.

4) This bundle of code and data is not encapsulated, so distribution is not readily controllable.

An Initial C++ Solution
There is no single solution to a problem such as this, so a series of approaches will be investigated. Here is a first attempt at a write-only port (wop) class:

class wop
{
        int shadow;              // not accessible
        int* address;           // to user
public:
        wop(long);               // constructor
        ~wop();                    // destructor
        void or(int);             // "operator"
        void and(int);          // functions
};

This hides the address of the port itself (address) and the shadow copy of the data (shadow) in the private part of the class, which can only be accessed by member functions.

A constructor initializes the member shadow value and the port itself with an arbitrary initial value (of zero):

wop::wop(long port)
{
        address = (int*) port;
        shadow = 0;             // initial value
        *address = 0;
}

The destructor would probably simply write zero to the port to return it to its initial state.

Two further member functions provide the user with a means of performing OR and AND operations on the port, which facilitates setting and clearing of individual bits:

void wop::or(int val)
{
        shadow |= val;                  // set bit(s) in copy
        *address = shadow;         // update port
}

void wop::and(int val)
{
        shadow &= val;                 // clear bit(s) in copy
        *address = shadow;           // update port
}

In both cases, the shadow data is maintained automatically. The applications programmer can create wop objects and use them thus:

main()
{
        wop out(0x10000);

        out.or(0x30);                     // set bits 4 and 5
out.and(~7);                              // clear bits 0, 1 and 2
};

Overloaded Operators
This C++ solution may be improved in a variety of ways. To start with, the use of the two member functions to perform OR and AND operations is not "natural". It would be much better if intuitive operators where available. This can be accommodated by overloading the |= and &= operators thus:

class wop
{
        int shadow;
        int* address;
public:
        wop(long);                         // constructor
        ~wop();                              // destructor
        void operator|=(int);         // overloaded
        void operator&=(int);        // operators
};

The way these operators perform should be exactly as any C/C++ programmer would expect. They may be implemented like this:

void wop::operator|=(int val)
{
            shadow |= val;              // set bit(s) in copy
            *address = shadow;     // update port
}

void wop::operator&=(int val)
{
            shadow &= val;             // clear bit(s) in copy
            *address = shadow;      // update port
}

Interestingly, this code is identical to the previous member functions, with just a change of function names. The new operators may be used thus:

main()
{
        wop             out(0x10000);

        out |= 0x30;                           // set bits 4 and 5
        out &= ~7;                             // clear bits 0, 1 and 2
}

<>Initialization
With the wop class above, there is a rational strategy for initialization " zero is written to the port (and saved in the shadow). However, it may be useful to allow the user to specify a custom value for each object. This requires a small change to the class definition and the constructor function, thus:

class wop
{
            int           shadow;
            int*         address;
            int           initval;                      // stored initial value
public:
            wop(long, int);                          // constructor
            ~wop();                                     // destructor
            void operator|=(int);                // overloaded
            void operator&=(int);             // operators
};

wop::wop(long port, int init=0)
{
            address = (int*) port;
            initval = init;
            shadow = initval; // initial value
            *address = initval;
}

The constructor takes an additional parameter, which specifies the initialization value, which may be applied like this:

main()
{
        wop     out(0x10000, 0x0f);

        out |= 0x30;                                 // set bits 4 and 5
        out &= ~7;                                  // clear bits 0, 1 and 2
}

Note that this second parameter has a default value of zero, which would be used if the constructor were called with a single parameter. This provides useful backwards compatibility with the previous version of the wop class.

The initial value is also retained in a private member variable (initval) which might be utilized by a destructor:

wop::~wop()
{
        *address = initval;                     // shutdown value
}

Reentrancy
As mentioned earlier, there is a possible issue with the reentrancy of code. The wop class, as it stands, is vulnerable if objects of this class are likely to be accessed by multiple program threads " i.e. tasks in a multi-tasking system or interrupt service routines as well as mainline code. If the interrupt occurred between updating of the shadow data and writing to the port, problems would result.

As is common in many real-time programming situations, imparting reentrancy is a matter of locking and unlocking a resource. To do this we need to extend the class slightly by adding two further (private) member functions:

class wop
{
            int shadow;
            int* address;
            int initval;
            void lock()                 { };     // dummy functions
            void unlock()             { };
public:
            wop(long, int);                     // constructor
            ~wop(); // destructor
            void operator|=(int);            // overloaded
            void operator&=(int);         //     operators
};

For the moment the two functions " lock() and unlock() " are dummies and do nothing. It is possible to imagine how they might really work. Perhaps they would simply disable and re-enable interrupts. Some possibilities will be discussed later.

These functions are used to make the two operator functions reentrant, thus:

void wop::operator|=(int val)
{
            lock();
            shadow |= val;                      // set bit(s) in copy
            *address = shadow;             // update port
            unlock();
}

void wop::operator&=(int val)
{
            lock();
            shadow &= val;                     // clear bit(s) in copy
            *address = shadow;               // update port
            unlock();
}

To implement these functions in the most flexible way, it is possible to take advantage of the object oriented capabilities of C++. If the functions are made replaceable (virtual), new variants may be deployed without further changes to the class:

class wop
{
        int shadow;
        int* address;
        int initval;
        virtual void lock();                   // replaceable functions
        virtual void unlock();
public:
        wop(long);                                 // constructor
        ~wop();                                      // destructor
        void operator|=(int);                 // overloaded
        void operator&=(int);              // operators
};

A new class - rwop - may be derived from the wop base class, which inherits all of its characteristics, but adds function locking functions:

class rwop : public wop
{
        int         flag;
        void lock()                 // replacement functions
        {
                while (flag)
                        ;
                flag = 1;
        };
        void unlock()
        {
                flag = 0; };
public:
        rwop(long, int);                 // constructor
};

The lock() and unlock() functions now use a private member variable (flag) to control access to the object. (This is not strictly reentrant, but serves to illustrate the point for the moment.) A new constructor is required:

rwop::rwop(long port,     int init=0)   :   wop(port,  init)
{
        unlock();
}

This simply executes the wop constructor code and calls unlock().Now the application programmer can use the rwop class in exactly the same way as wop was used and does not really need to know that anything has been changed:

main()
{
        rwop out(0x10000, 0x0f);

        out |= 0x30;                     // set bits 4 and 5
        out &= ~7;                     // clear bits 0, 1 and 2
}

Using an RTOS
If reentrancy is needed because an RTOS is in use, it is logical to use the services provided by the kernel to create a reentrant derivative of the wop class:

class vwop : public wop
{
        int* mbox;
        int err;
        void lock()             // replacement functions
        {
                sc_pend(&mbox, 0, &err);
        };
        void unlock()
        {
                sc_post(&mbox, (char*)1, &err);
}; public:
        vwop(long, int); // constructor
};

In this case a mailbox is used to store a token. A binary semaphore is another - perhaps more likely - way to implement the flagging. Again the application programmer can use the vwop class in an identical way to the earliest wop class.

The vwop class successfully encapsulates two separate, mutually exclusive fragments of expertise: knowledge of the write-only port hardware and RTOS programming experience. The application programmer needs no awareness of either.

Other Possibilities
The ideas presented here may be developed further to add additional capabilities or to address different, but similar, problems. Possibilities include:

* Add an exclusive-OR operator: ^=

* Provide a means of retrieving the shadow data (perhaps by overloading the int casting operator)

* Treat a port as an array of bits (overloading the [] operator)

* Accommodate arrays of ports (at consecutive addresses)

* Apply the same ideas to handle indexed ports, where the "address" of a device's internal register needs to be written first, followed by the actual data reading or writing

C++ and an RTOS
When an embedded software application is being developed using a real-time operating system, there are some distinct benefits to be gained from the careful use of C++.

The two clear advantages are: application programmers do not need to have detailed knowledge of the RTOS programming interface and the application code is likely to be portable to an alternate RTOS, if a change is necessitated.This topic is quite large and only a few aspects may be considered here:

Task/Object Creation. Most RTOSes have a conventional C function library which serves as an application program interface (API). This may be proprietary or may conform to a standard (like POSIX, for example). In either case, many API calls may be quite complex, with numerous parameters. This is an ideal opportunity for encapsulation of expertise.

Consider the creation of tasks. A base class could be designed which characterizes a generic task. The key components of the class would be a constructor (which creates the task), a destructor (which deletes the task) and an empty virtual (i.e. replaceable) main() function.

An application task could then be defined by deriving a new class, which replaces the main() function with the actual task code and adds in other requirements (like the task stack perhaps).

Instances of the task could then be created by simply instantiating objects from the class. Note the separation of task definition (creating the class) and the actual creation of tasks (object instantiation).

The same kind of approach may be taken to the handling of other RTOS objects like mailboxes, event flags, semaphores, timers, queues, pipes etc.

Thread Local Objects. C++, like C, has no in-built concept of a thread or a task " the language essentially assumes a single thread of execution. By creating a class that represents a task, this shortcoming is effectively overcome. This brings with it some useful benefits. A key one is the ability to have thread local objects (and variables).

When using an RTOS conventionally, it may be quite common to create a number of identical tasks, each of which operates on different data, while sharing code. This is a challenge as there is no straightforward way to have conventional variables that are specific to a particular task (i.e. thread local variables).

By adding member variables to the task definition class, each resulting object has its own copy of each variable (unless they need to be shared, in which case they need to be declared static).

An extension of this idea is thread local variants of other RTOS objects. If the task definition class includes a declaration of a semaphore (for example), that object will be created specifically for each task instance and be inaccessible elsewhere.

Paired Operations. It is a common requirement in real time applications (with or without an RTOS) to define paired operations: two actions that complement one another and are executed at the beginning and end of a code sequence.

For example, if a set of instructions must be executed without any possibility of interruption, it may be preceded by a disable interrupts and succeeded by an enable interrupts. It may be problematic to ensure that both operations are performed, particularly if the code logic is complex.

An object oriented "trick" can provide a simple way out. All that is required is a simple class which only contains a constructor and a destructor. The constructor disables interrupts and destructor enables them again.

<>Then it is simply a matter of instantiating an instance of this class (an object) at the head of a block. Interrupts will be turned off on entry to the block and turned on again when the object goes out of scope " i.e. when execution leaves the block by whatever means. The structure of the code looks like this:

{
object declaration
(constructor disables interrupts)

(destructor re-enables interrupts)
}

This approach has numerous applications. Others include: locking resources, turning on/off an RTOS scheduler and providing pre-amble and post-amble for an ISR.

Here is some possible code for a class to provide device locking using an RTOS semaphore:

class Device_Locked
{
private:
        static Semaphore device;         // NB static
public:
        Device_Locked()

        { device.acquire(); };
        ~ Device_Locked()
        { device.release(); };
};

To read Part 1, go to: Why is C++ not more widely used?

Colin Walls has over twenty-five years experience in the electronics industry, largely involved with embedded software. A frequent presenter at conferences and seminars including the Embedded Systems Conference he is a member of the marketing team of the Embedded Systems Division of Mentor Graphics.

Click here to see Part 1.