Ferdinand Keil's

electronic notes

Feb 29, 2020

Atomic Test-and-Clear on the 8051

When running multiple tasks simultaneously on an embedded system using cooperative task scheduling you might run into situations (at least I did 😁) where these tasks might need to access the same hardware peripheral. Not all peripherals can be shared easily due to their configuration or some other kind of internal state. To coordinate access to the peripheral from multiple tasks one can use a Lock.

To ensure that the code managing the lock state shows deterministic behavior in a system with interrupts, special care has to be taken. The easy - but clumsy - implementation would just disable interrupts for critical code sections. This can lead to unintended dire consequences in terms of the real time behavior of the system, e.g. bytes get lost while communicating with an external system due to buffer overflow. The correct™ way to implement the lock would be to use an atomic operation to test-and-clear the flag variable used at the core of the lock. This however needs to be supported by the MCU core in hardware.

Luckily, the 8051-architecture as used in the Silicon Labs EFM8 comes with just the needed instruction: JBC, or jump if bit set with clear. This instruction gets executed atomically, thus cannot be interrupted by an interrupt. A basic example of a lock for the Keil compiler would then look something like this:

#include <intrins.h>

bit Lock_unlocked;

uint8_t Lock_getLock(void) {
    if (_testbit_(Lock_unlocked)) {
        // lock was unlocked, so we claimed it
        return 1;
    }
    // lock is still locked, try again later
    return 0;
}

void Lock_releaseLock(void) {
    // don't need lock anymore, so release it
    Lock_unlocked = 1;
}

Note: The Keil compiler comes with the intrinsic function _testbit_(bit b) that makes it very easy to use the JBC instruction.