Critical sections in ARM
Asked Answered
C

2

11

I am experienced in implementing critical sections on the AVR family of processors, where all you do is disable interrupts (with a memory barrier of course), do the critical operation, and then reenable interrupts:

void my_critical_function()
{
   cli();  //Disable interrupts
   // Mission critical code here
   sei();  //Enable interrupts
}

Now my question is this:

Does this simple method apply to the ARM architecture of processor as well? I have heard things about the processor doing lookahead on the instructions, and other black magic, and was wondering primarily if these types of things could be problematic to this implementation of critical sections.

Contextual answered 11/8, 2018 at 0:53 Comment(0)
L
17

Assuming you're on a Cortex-M processor, take a look at the LDREX and STREX instructions, which are available in C via the __LDREXW() and __STREXW() macros provided by CMSIS (the Cortex Microcontroller Software Interface Standard). They can be used to build extremely lightweight mutual exclusion mechanisms.

Basically,

data = __LDREXW(address)

works like data = *address except that it sets an 'exclusive access flag' in the CPU. When you've finished manipulating your data, write it back using

success = __STREXW(address, data)

which is like *address = data but will only succeed in writing if the exclusive access flag is still set. If it does succeed in writing then it also clears the flag. It returns 0 on success and 1 on failure. If the STREX fails, you have to go back to the LDREX and try again.

For simple exclusive access to a shared variable, nothing else is required. For example:

do {
  data = LDREX(address);
  data++;
} while (STREXW(address, data));

The interesting thing about this mechanism is that it's effectively 'last come, first served'; if this code is interrupted and the interrupt uses LDREX and STREX, the STREX interrupt will succeed and the (lower-priority) user code will have to retry.

If you're using an operating system, the same primitives can be used to build 'proper' semaphores and mutexes (see this application note, for example); but then again if you're using an OS you probably already have access to mutexes through its API!

Ladawnladd answered 11/8, 2018 at 7:45 Comment(3)
That's cool.....I am actually creating my own atomic data types in c++ (using template wrappers) and I can provide this as another backend, so that I can mix and match implementations.....Contextual
Cool is right. They replace the SWP instruction in the old 32-bit ARM instruction set, which did an atomic load/store (similar things are available on other platforms). But LDREX/STREX are far kinder to the memory bus, which is good anyway but starts to be really important if you have more than one processor sharing the bus. Neat bit of design, IMO.Ladawnladd
the above approach of using ldrex and strex instruction works, and it is the best way to guard a critical section. But if you want to guard the critical section by disabling and re-enabling interrupts, you should add a memory barrier instruction before disabling the interrupts. This instruction tells the CPU to stop out-of-order execution from the point of disabling interrupts. Without the barrier instruction, code may not behave as expected. This is applicable only to processors like cortex-M which reorders instructions for better performance.Stendhal
F
4

ARM architecture is very wide and as I understand you probably mean ARM Cortex M micro controllers.

You can use this technique, but many ARM uCs offer much more. As I do know what is the actual hardware I can only give you some examples:

  1. bitband area. In this memory regions you can set and reset bits atomic way.
  2. Hardware semaphores (STM32H7)
  3. Hardware MUTEX-es (some NXP uCs)

etc etc.

Freemason answered 11/8, 2018 at 1:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.