SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Achieving Atomicity for Single Bit Writes in ARM Cortex M3
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm

Achieving Atomicity for Single Bit Writes in ARM Cortex M3

Eileen David
Last updated: October 5, 2023 9:24 am
Eileen David 6 Min Read
Share
SHARE

Atomic operations, where a single instruction executes in an uninterruptible sequence, are essential for many embedded systems and microcontroller applications. The ARM Cortex M3 processor provides atomic read-modify-write instructions for 32-bit and 16-bit data types. However, single bit access presents challenges for atomicity. This article examines techniques for achieving atomic single bit writes on the Cortex M3.

Contents
The Need for Atomic Single Bit WritesCortex M3 Limitations for Single Bit WritesApproach 1: Disable InterruptsApproach 2: Use LDREX/STREXApproach 3: Use Locking PrimitivesApproach 4: Use Atomic Peripheral AccessConclusion

The Need for Atomic Single Bit Writes

Single bit variables are commonly used for flags and status registers in embedded software. Setting or clearing bits in a control register often enables or disables critical hardware functions. These bit operations must execute atomically, without interruption, to avoid race conditions between interrupt handlers and main program flow.

For example, the main program may test a “transmit enable” bit, see it is clear, then get interrupted before setting it. The interrupt could then test the bit, also see it clear, and start transmitting. This leads to two simultaneous transaction attempts. Atomic test-and-set prevents such problems.

Cortex M3 Limitations for Single Bit Writes

The ARM Cortex M3 provides atomic read-modify-write instructions like LDREX/STREX for 32-bit data types, and LDREXH/STREXH for 16-bit data. However these operate on entire 32-bit or 16-bit words. Single bit atomicity is not directly supported by the architecture.

Single bit variables are typically implemented as bitfields within 32-bit CPU registers. A read-modify-write sequence on the entire 32-bit register is not atomic for the single bit. The processor may interrupt between reading and writing the register, allowing the bit to be changed by an interrupt handler.

Approach 1: Disable Interrupts

The simplest method is to disable interrupts before accessing the bit variable, and re-enable interrupts after. This prevents any interrupt handler code from running during the bit manipulation. For example: // Globally disable interrupts __disable_irq(); // Atomically clear bit 0 of control register CONTROL &= ~(1 << 0); // Re-enable interrupts __enable_irq();

Disabling interrupts is very heavy handed though, and can negatively impact real-time performance. Lengthy critical sections with interrupts disabled can lead to deadlock or data loss scenarios.

Approach 2: Use LDREX/STREX

We can leverage the Cortex M3’s native atomic primitives, LDREX and STREX, to implement single bit atomic writes. The process is:

  1. Use LDREX to load the 32-bit register containing the bit variable into a temporary variable.
  2. Manipulate the desired bit in the temporary variable.
  3. Use STREX to attempt to write the temporary variable back to the original register.
  4. Check if STREX succeeded, and loop back to LDREX again if not.

For example: uint32_t temp; do { temp = LDREX(CONTROL); temp |= (1 << 0); // Atomic bit set } while (STREX(temp, CONTROL));

The LDREX/STREX commands form an atomic read-modify-write sequence. The processor guarantees the CONTROL register value loaded by LDREX will not be modified before the STREX. The do-while loop handles any simultaneous access by retrying if STREX fails.

This method can be abstracted into a function for setting/clearing single bits: void atomic_bit_set(volatile uint32_t *reg, uint8_t bit) { uint32_t temp; do { temp = LDREX(reg); temp |= (1 << bit); } while (STREX(temp, reg)); }

The limitation of this technique is that interrupts are disabled for the duration of the LDREX/STREX loop. A series of repeated atomic accesses could delay interrupt handling. So care should be taken to minimize use of this method.

Approach 3: Use Locking Primitives

We can implement mutex locks or semaphores in Cortex M3 to provide synchronization for non-atomic code sections. A typical pattern is: // Globally define a mutex mutex_t bit_lock; // Lock mutex before bit manipulation mutex_lock(&bit_lock); CONTROL |= (1 << 0); // Set bit // Unlock mutex after mutex_unlock(&bit_lock);

If interrupts are also disabled during mutex lock/unlock, then this constructs a critical section preventing simultaneous access. The mutex ensures only one processor or context manipulates the bit at once.

Locking adds significant overhead however. A mutex implementation may disable interrupts internally, impacting real-time performance. Common problems like priority inversion must also be considered with locking.

Approach 4: Use Atomic Peripheral Access

For register bits controlling peripheral devices like GPIO, timers, communication buses etc., an alternative is leveraging the peripheral’s own atomic access mechanisms.

For example, the Cortex M3 SysTick timer has an atomic SET_ENABLE register to start/stop counting. The GPIO block has atomic BIT_SET and BIT_CLEAR registers for individual pin manipulation. Using theseatomic peripheral registers prevents external modification of bits during the atomic operation.

This keeps the processor interruptible, avoiding critical sections. But it depends on the peripheral having atomic access support, and is not generally applicable.

Conclusion

For single bit variables not mapped to a peripheral, using LDREX/STREX atomic primitives provides the best balance of robustness and real-time performance on Cortex M3. Lengthy critical sections should be avoided if possible. Atomic peripheral registers can also be leveraged when applicable.

Proper use of atomic access techniques is essential for robust embedded software on Cortex M3 and other ARM processors. Atomicity prevents race conditions and provides deterministic behavior for critical flag variables.

For additional information on implementing atomic operations in ARM Cortex-M3, consult…

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article Leveraging Bit Banding for Atomic Register Access in ARM Cortex M3
Next Article Interrupt Handling During Multi-Cycle Atomic Operations in ARM Cortex M3
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

Benefits of NVIC for Industrial Motion Controllers

Industrial motion control applications require precision, reliability, and flexibility. Nonvolatile-memory-based…

16 Min Read

Will The Arm Architecture Replace The X86/X64 Architecture?

The short answer is that while ARM is making inroads…

6 Min Read

ARM cross compiler toolchain

An Arm cross compiler toolchain allows developers to compile code…

6 Min Read

Application of ARM Processors

ARM processors are one of the most versatile and widely…

6 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account