I often joke that my job puts me in a difficult position, as I am a software guy and a very large proportion of Mentor Graphics employees are hardware design specialists; I am consorting with the “the enemy”. Although I am making a light-hearted comment, there is, sadly, something of an “us and them” attitude between software and hardware teams in many companies. It is in everybody’s interest to make this a thing of the past.
Sometimes, it seems as if this view is reasonable, when hardware seems to be designed specifically to make life hard for software developers …
The issue of hardware being challenging to control from software is well known. The whole idea of device drivers was conceived, as much as anything, to hide the nastiness of such interfaces from non-experts. My favorite example of hardware that seems to have designed for intentional aggravation is a write-only port.
This type of interface has a register, to which data may be written, but from which the current value may not be read back. This may be problematic because the 8-, 16- or 32-bit port may consist of a number of single- or multi-bit registers with unrelated functionality. This means that different parts of the software may need to access the port and there are plenty of opportunities for unintentional interference. For example, to set the bottom 2 bits of the port without affecting the other bits, the following code would seem obvious:
WOPORT |= 0x03;
However, this construct will not work, as the |= operator generates code which will read the port [and get invalid data or trigger an exception] before writing an updated value. The solution is simple enough: keep a “shadow” copy of the port data and use that each time an update is required, like this:
woport_shadow |= 0x03;
WOPORT = woport_shadow;
This is quite straightforward, but there are a few things to take care of:
- The port and the shadow data must be correctly initialized.
- Code which accesses the port must always utilize the shadow.
- Re-entrancy may be an issue. If an interrupt occurred between the updating of the shadow and the writing to the port, another thread [task or ISR] might utilize the port and the shadow could get out of synch.
All of these issues may be addressed by careful code design. Probably the best approach is to encapsulate the access to a port inside a function, thus providing a common API. Another approach might be to use C++ to encapsulate the details of write-only port access. I will look at that approach on another occasion.
Incidentally, the code above might be an ideal application for my approach to binary constants, which I wrote about some time ago; instead of 0x03 you would use b00000011.