Several years ago I came across a register that, at first glance, seemed to be a typical register with several read/write bits. Upon closer inspection, I saw that one bit behaved differently, depending on its state and whether I was writing a 1 or a 0 to it. Since firmware had to handle that one bit differently from the other bits in that register, I saw that it would be difficult for firmware to safely handle both types in the same register. I was able to reduce the risk of mishandling that register by writing macros with generous comments. However, I could not ensure that everybody in the future would know that it was not a typical read/write register and treat it as such.
Although the engineer had valid reasons for designing the register that way, he or she had not anticipated the impact on firmware of mixing different types of bits in the same register. To avoid complexity and risk of firmware defects, different types of bits should be located in different registers. To see why, let’s examine how firmware manages two types of bits – read/write bits and interrupt bits.
With read/write bits, firmware sets and clears bits when needed. It typically first reads the register, modifies the desired bit, then writes the modified value back out. Here is a sample code fragment:
tmp = ReadReg (regA); /* Get the register contents */ tmp |= 0x01; /* Set bit 0 */ RegWrite (regA, tmp); /* Write it back out */
In the case of interrupt bits, firmware often writes a value with one bit set to acknowledge the desired interrupt while leaving any other pending interrupts untouched.
RegWrite (regB, 0x10); /* Ack bit 4 */
Mixing the two types of bits in the same register could cause problems. Using the read/write code on interrupt bits causes pending interrupts to inadvertently be acknowledged. Using the interrupt code on read/write bits clears all read/write bits that used to be set. Firmware must take special care to ensure that it does not inadvertently change the wrong bits.
Here is a code fragment that acknowledges bit 4 while taking care not to acknowledge other pending interrupts or modify any read/write bits. In this example, read/write bits are located in positions 0x0f and interrupt bits are located in positions 0x70.
tmp = RegRead (regC); /* Get the register contents */ tmp &= 0x0f; /* Keep R/W bits but zero any intr bits */ tmp |= 0x10; /* Set bit 4 to ack */ RegWrite (regC, tmp); /* Write it back out */
Acknowledging an interrupt changed from a one-step to a four-step operation. A similar code fragment is needed to modify desired read/write bits while leaving alone any pending interrupts.
While there is a way for firmware to safely handle this, it is out of the ordinary and prone to firmware defects. Combining different types of bits into the same register may save registers but it adds unnecessary burden and complexity to firmware. Looking ahead and anticipating the firmware impact can lead to a more reliable and robust solution of placing different types of bits in separate registers.
Best Practice: Segregate different types of bits (read/write, read-only, interrupt, etc.) into different registers.
If necessary, read-only bits could be combined with any one of the other types of bits. This is acceptable because no matter how the other bits are handled, firmware writes to the register will not affect the read-only bits.
Until the next bit…
Tags: bit types, mixing bit types

I always think of reading and writing to I/O registers as commands, which can do anything, not just move bits around. In your example the same command does two different things, which is bad design. In general it is good to have separate commands (i.e. addresses to write to) for setting or resetting a subset of the GPIO bits on a port. This avoids races from read-update-write, and is also faster.
Juergen,
Requiring firmware to do read-update-write is bad if different threads of execution is possible. Your GPIO port is an excellent example where read-update-write should be avoided because different parts of the firmware might need to access their respective bits. Unfortunately some GPIO ports are designed that way and firmware has to use that and other techniques to try to avoid problems. (I discussed those firmware challenges and hardware solutions, including your recommendation for having separate set and reset addresses, in my newsletters, #37, #38, and #39.)
In this post, I was referring to interrupt bits and read/write bits that, though I did not specifically so state, only one firmware module would access. Interrupt bits are set by the hardware when an event occurs and firmware must read it to see which interrupt occurs, then (typically) writes a one to clear (reset) it. Firmware does not set interrupt bits so having separate set and clear addresses is not needed there. Read/write bits are used to configure hardware operation and firmware needs to set some bits and clear others at the same time, so having separate set and clear addresses prevents firmware from making the necessary changes in one write. If firmware wants to speed things up, it can keep a shadow copy of the contents of the register so that it does not have to first read it before making changes.
Because firmware’s handling is different for interrupt bits versus read/write bits, my post was with regards to keeping those types of bits in separate registers at different addresses, in order to avoid confusion as to how the contents of a particular register should be handled.
If I have not made myself clear, let me know and I will try again.
It gets even more interesting when the very act of reading a register is sufficient to clear a bit! Sometimes this can make for very efficient code. Other times it can make for exactly the sort of problems you described here.
This and your previous post both allude to the issue of bit allocation to registers. It’s a tough problem, particularly on small processors with limited address space. Different companies have adopted different strategies on this, so I’d be interested to get your opinions on this topic in future posts.
What I’ve seen happen is that hardware engineers will focus on optimizing the hardware side, such as packing in and overloading unrelated bits in the same register but it comes with a high firmware price in code size and complexity. And likewise, firmware engineers request that hardware be designed a certain way which optimizes firmware but makes hardware complex. An increase in complexity leads to an increase in defects, debugability, maintainability, and future growth. When resources are limited, such as address space or firmware space, hardware and firmware engineers need to collaborate to make sure optimizing one side does not negatively impact the other.
in multi-threaded systems all accesses to hw registers no matter what their ‘mechanism’ is MUST be handled using mutexes.
ISRs have to access hw registers however, they cannot, while servicing an interrupt, be blocked on a mutex. So mutexes cannot be used there. Hw registers being accessed only by one thread and that thread only do not need mutexes.
Mutexes are needed when different threads need to access the same hw registers. However, mutexes still have their flaws. Each and every thread in all of their respective hw register accesses must use the same mutexing technique. If even one thread doesn’t, the system is exposed.
This is where the design of the hw can help, by assigning, where possible, bits into registers only needed by one thread. For registers that do need to be accessed by multiple threads (such as GPIO ports), atomic register access mechanisms (see here for examples) can be employed to eliminate any need for mutexes.