π§ ARM Cortex-M Memory Modelο
The ARM Cortex-M memory model governs how a CPU and other bus masters (DMA controllers, second cores) observe memory. Understanding it is essential for correct bare-metal and RTOS code on STM32 and similar devices β particularly when sharing memory between a CPU and a DMA peripheral, or between two cores on the STM32H7 (Cortex-M7 + Cortex-M4).
Volatileο
volatile is a compiler-only directive. It does two things:
Forces the compiler to re-read the variable from memory on every access, preventing it from holding the value in a CPU register across accesses.
Prevents the compiler from reordering accesses to that variable relative to other code, since every access is treated as a visible side effect.
volatile does not affect hardware. The CPUβs own pipeline, write buffer,
and cache can still reorder or delay memory accesses independently of what
the compiler emits. For hardware ordering guarantees, memory barrier instructions
are required.
Hardware Features That Affect Memory Orderingο
Several hardware mechanisms can cause a CPU to observe memory in a different order than the programβs source suggests.
Write Bufferο
When the CPU executes a store instruction, the value is placed into a small
hardware queue (the write buffer) and the CPU immediately continues executing.
The write propagates to RAM in the background. From the perspective of any other
bus master (DMA controller, second CPU core), the write may not yet be visible.
__DSB() drains the write buffer before proceeding.
Out-of-Order Executionο
The CPU pipeline may reorder independent instructions to keep execution units busy. On Cortex-M4 this is limited; on Cortex-M7 and Cortex-A it is more aggressive. Memory barriers prevent reordering across the barrier point.
D-Cacheο
A physical SRAM sitting between the CPU and main RAM. Present on Cortex-M7 (e.g. STM32F7, STM32H7) but not on Cortex-M4 (STM32F3, STM32F4). The CPU reads and writes to the cache; RAM is only updated when cache lines are flushed or evicted. Any other bus master that accesses RAM directly (DMA, second core) will not see writes that are still in cache, and vice versa. Cache maintenance operations are required to restore coherency.
Speculative Readsο
The CPU may read memory ahead of time in anticipation of future instructions. This can cause a load to happen before a preceding store has settled. Barriers prevent the CPU from acting on speculatively fetched data across the barrier.
Barrier Instructions (CMSIS)ο
Defined in core_cm4.h / core_cm7.h, provided by ARMβs CMSIS layer.
Each compiles to a single ARM instruction with a "memory" compiler clobber,
which simultaneously prevents hardware reordering and compiler reordering across
the barrier.
DSB β Data Synchronisation Barrierο
__DSB();
Stalls the CPU until all pending memory accesses and cache maintenance operations have fully completed and are visible to all observers of the memory system. The strongest of the three for data.
DMB β Data Memory Barrierο
__DMB();
Ensures that all memory accesses before the barrier are observed before any memory accesses after it. Does not wait for those accesses to complete β only enforces their relative ordering.
Reordering is a CPU-side phenomenon β it happens in the pipeline and execution units before writes ever reach the write buffer or RAM. Once a write is in the write buffer it will reach RAM in order; the danger is the CPU issuing writes to the write buffer in the wrong order in the first place. DMB prevents this by constraining the order in which the CPU commits accesses, without having to wait for them all to drain.
ISB β Instruction Synchronisation Barrierο
__ISB();
Flushes the CPU instruction pipeline, forcing all subsequent instructions to be re-fetched. Does not directly relate to data ordering β it ensures the CPU is executing with a clean view of any changes that affect instruction decode (vector table, MPU config, cache enable/disable).
Memory Types (ARM Memory Model)ο
The MPU assigns a memory type to each address region. The memory type determines what ordering and caching guarantees the hardware provides.
Normal Memoryο
RAM. The CPU may reorder accesses, buffer writes, and speculate reads. No ordering guarantees without explicit barriers. Cacheable or non-cacheable depending on MPU attributes.
Device Memoryο
Peripheral registers. No reordering, no speculation, no write merging. Accesses
complete before the next instruction proceeds. volatile (__IO) is
sufficient for peripheral registers because the hardware provides the ordering
guarantee that Normal memory does not.
Strongly Orderedο
Strictest. Every access is a full synchronisation point. Rarely used on Cortex-M.
MPU and Cache Relationshipο
The MPU and D-cache are separate hardware blocks inside the CPU core. The cache controller has a direct hardwired connection to the MPU and performs a lookup on every memory access to determine the attributes of that address. This happens in hardware with no software involvement at runtime.
Non-Cacheable MPU Regionο
The cleanest solution for any shared memory (DMA buffers, inter-core buffers) on H7/F7 is to permanently mark a dedicated RAM region as non-cacheable in the MPU at startup. No manual cache maintenance is needed at runtime.
Some chip families formalise this in their memory map. The STM32H7 has specific
SRAM regions (e.g. SRAM4 at 0x38000000) commonly reserved by convention for
DMA and inter-core shared buffers. Rather than scattering DMA buffers throughout
RAM and manually managing coherency for each one, all shared data is placed in
this dedicated region and the problem is solved structurally.
Place shared buffers at this address via the linker script. __DSB() is still
required when signalling between tasks or cores, as the write buffer still applies
even without a cache.