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.
__DMB() is required between the payload stores and the flag store on the producer (M7) side, and a matching __DMB() between the flag read and the payload reads on the consumer (M4) side.volatile so the compiler doesn't hoist or coalesce the store.__DSB() (not just __DMB()) is safer before writing the mailbox register, because the interrupt on the other core must not arrive before the payload writes are globally visible.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.
