armhub.dev
Architecture · 15 pages
Architecture

Memory model & barriers

How loads and stores are ordered on ARM — relaxed by default, with explicit barriers (DMB, DSB, ISB) where you need them. Plus the Cortex-M memory map.

2 min read · 467 wordsupdated · May 12, 2026

ARM is a weakly ordered architecture. The CPU and the bus are free to reorder loads and stores as long as single-threaded program semantics are preserved. Most of the time you don't notice. The moment you start touching memory-mapped peripherals or doing lock-free inter-core communication, you notice hard.

The Cortex-M address space

Every Cortex-M divides its 4 GB address space into eight 512 MB regions with fixed default attributes:

Cortex-M default memory map (ARMv7-M ARM §B3.1)
0xE010_0000
– 0xFFFF_FFFF
Vendor / system
Implementation-defined system region.
system
0xE000_0000
– 0xE00F_FFFF
Private Peripheral Bus
NVIC, SCB, SysTick, MPU, ITM, DWT, TPIU, FPB.
system
0xA000_0000
– 0xDFFF_FFFF
External device
Shareable, non-cacheable, ordered.
external
0x6000_0000
– 0x9FFF_FFFF
External RAM
FMC / FSMC / OSPI / QSPI — vendor wiring.
external
0x4000_0000
– 0x5FFF_FFFF
Peripheral
Device-type. GPIO, UART, SPI, I²C, timers, DMA, USB…
periph
0x2000_0000
– 0x3FFF_FFFF
SRAM
Normal memory. Bit-banded on M3/M4 (low 1 MB).
sram
0x0000_0000
– 0x1FFF_FFFF
Code
Flash, system memory, bootloader.
code

The regions default to specific memory types (Normal, Device, Strongly-ordered) which dictate what reordering and caching the bus is allowed to do. An MPU can override these.

TypeReordered?Cacheable?Example region
NormalyesyesFlash, SRAM
DevicenonoPeripherals
Strongly-orderednonoPPB, SO regions

The three barriers

ARM has exactly three memory barriers, and the names tell you what they do.

DMB — Data Memory Barrier

All explicit memory accesses before the DMB complete (from the observer's point of view) before any explicit memory accesses after the DMB are observed. It's the cheapest barrier — it doesn't stall the pipeline, it just constrains the bus.

Use DMB when:

  • Two cores or a core + DMA are sharing a buffer.
  • You're implementing a software mutex or producer/consumer queue.
  • You hand a pointer to a peripheral that will then read the memory it points to.
// Producer
buffer[head] = item;       // store data
__DMB();                   // ensure data is visible before head update
head_index = (head + 1);   // store updated index — consumer can now see item
Effect of DMB on store ordering
STR ASTR BDMBSTR CSTR Dobs A,Bobs C,DbarrierA,B visibleC,D visible
Without DMB the bus is free to reorder all four stores; with DMB inserted between B and C, any other observer is guaranteed to see A and B complete before C or D become visible.

DSB — Data Synchronisation Barrier

A DMB plus the processor stalls until the prior accesses have completed. Stronger and more expensive.

Use DSB when:

  • You wrote to a peripheral register and the next instruction depends on the write having taken effect (e.g. enabling an interrupt and immediately executing WFI, disabling an interrupt and immediately returning from a handler).
  • You need to flush the write buffer before sleeping.
NVIC_DisableIRQ(SOME_IRQ);
__DSB();                    // make sure the disable is observed
__ISB();                    // and that pending instructions are flushed
// safe to assume IRQ won't fire from here

ISB — Instruction Synchronisation Barrier

Flushes the instruction pipeline. The processor refetches all instructions after the ISB. Cheap from a bus perspective but expensive in pipeline terms (~5–10 cycles).

Use ISB when:

  • You just modified a system register that changes how subsequent instructions execute (e.g. CONTROL, VTOR, MPU region settings, FAULTMASK).
  • You self-modified code or remapped memory.
SCB->VTOR = new_table_address;
__DSB();   // make sure the write to VTOR completes
__ISB();   // flush prefetch — next IRQ uses new vector table

Putting it together

The classic three-barrier dance after enabling a peripheral clock or modifying the MPU:

MPU->RNR  = region;
MPU->RBAR = base;
MPU->RASR = attr | MPU_RASR_ENABLE_Msk;
__DSB();    // make sure the MPU writes have completed
__ISB();    // flush any prefetched instructions executed with old config

Atomics on Cortex-M

  • Cortex-M0/M0+/M23: no exclusive load/store. The standard pattern is to disable interrupts (__disable_irq() or a critical section macro) around the read-modify-write.
  • Cortex-M3 and up: have LDREX/STREX exclusive monitors. C11 <stdatomic.h> and C++11 <atomic> will use them when targeting ARMv7-M or later.
  • Multi-master systems (e.g. M0+ core + a DMA controller): the exclusive monitor only covers the CPU. You still need software protocols (or a vendor-provided hardware semaphore — STM32 HSEM, NXP SEMA42) to coordinate.

See also

  • Exception model — barriers in interrupt entry/exit.
  • NVIC deep dive — when you need DSB+ISB and when CMSIS does it for you.
  • DMA — the most common source of "I forgot a barrier" bugs.