Cortex M3 поддерживает полезную пару операций (также распространенную на многих других машинах), называемых «исключающими нагрузку» (LDREX) и «исключающими хранилище» (STREX). Концептуально операция LDREX выполняет загрузку, а также устанавливает некоторое специальное оборудование для наблюдения, может ли загруженное местоположение быть записано чем-то другим. Выполнение STREX по адресу, используемому последним LDREX, приведет к тому, что этот адрес будет записан только в том случае, если больше ничего не было написано первым . Инструкция STREX загрузит регистр с 0, если хранилище имело место, или с 1, если оно было прервано.
Обратите внимание, что STREX часто пессимистичен. Существуют различные ситуации, когда он может решить не работать с магазином, даже если это место не было затронуто. Например, прерывание между LDREX и STREX заставит STREX предположить, что наблюдаемое местоположение может быть повреждено. По этой причине обычно рекомендуется минимизировать объем кода между LDREX и STREX. Например, рассмотрим что-то вроде следующего:
inline void safe_increment (uint32_t * addr)
{
uint32_t new_value;
делать
{
new_value = __ldrex (addr) + 1;
} while (__strex (new_value, addr));
}
который компилируется во что-то вроде:
; Предположим, R0 содержит рассматриваемый адрес; R1 разгромили
LP:
ldrex r1, [r0]
добавить r1, r1, # 1
Strex R1, R1, [R0]
cmp r1, # 0; Проверьте, не ненулевой
бн лп
.. код продолжается
Подавляющее большинство времени выполнения кода, между LDREX и STREX ничего не случится, чтобы «потревожить» их, поэтому STREX преуспеет без дальнейших церемоний. Однако, если прерывание происходит сразу же после инструкции LDREX или ADD, STREX не будет выполнять сохранение, а вместо этого код вернется обратно, чтобы прочитать (возможно, обновленное) значение [r0] и вычислить новое увеличенное значение основываясь на этом.
Использование LDREX / STREX для формирования таких операций, как safe_increment, позволяет не только управлять критическими секциями, но и во многих случаях избегать необходимости в них.