Do I need a __DMB() when writing a "ready" flag to non-cacheable shared SRAM (Cortex-M7 to M4)?

2026-08-18

Stack Overflow: View Question

Tags: arm, cortex-m, bare-metal, stm32h7, cortex-m7

Score: 1 | Views: 94

The asker is doing classic producer/consumer signaling between two heterogeneous cores on an STM32H7: the Cortex-M7 populates a struct in shared SRAM3 (configured non-cacheable) and then writes a "ready" flag that the Cortex-M4 polls. The question is whether a __DMB() barrier is required between the payload writes and the flag write to guarantee the M4 sees a consistent picture.

Why it's genuinely tricky. The intuition "non-cacheable memory means no reordering" is a common half-truth. Non-cacheable Normal memory does allow the memory system to reorder writes — cacheability and orderability are orthogonal attributes in the ARM memory model. The only attribute that removes reordering between accesses is Device memory (specifically Device-nGnRE or stricter), or configuring the region as Strongly-ordered / Normal Non-cacheable with the Shareable attribute plus explicit barriers.

Compounding this: the Cortex-M7 has a store buffer and is capable of merging and reordering writes to Normal memory even when caches are disabled. So writes to payload_1, payload_2, and the flag can retire to the SRAM controller in a different order than programmed. Additionally, the compiler can reorder the stores unless the flag is volatile — this is a separate concern from the CPU barrier.

Direction toward an answer.

Gotchas. The M7's store buffer is the real villain here — people often test without a barrier and it "works" because the two writes happen to drain in order under light load, then it fails intermittently under different timing or optimization levels. Also, if any part of the struct straddles a cacheable region (easy to do wrong with linker script placement), you'd additionally need SCB_CleanDCache_by_Addr() on the M7 and SCB_InvalidateDCache_by_Addr() on the M4. And remember: __DMB() orders memory accesses, but does not flush the store buffer to the point of global visibility for another master — __DSB() is the stronger guarantee, and is what you want before triggering the other core.

The challenge: "Non-cacheable" doesn't mean "ordered" in the ARM memory model — the M7's store buffer can still reorder writes to shared SRAM, so barriers are needed even without caches in the picture.

All newsletters