INTRODUCTION: I've designed an embedded system featuring an ATSAME54N20A 32-bit ARM® Cortex®-M4F MCU. The board will be assembled and ready for programming soon so I was setting up my programming environment. I went for a bare-bone solution where only the minimum C-written files necessary are present, because although it's a time consuming process, it helps me to understand the system workings. The compiler chosen is GCC with the following arguments:
"...\arm-none-eabi-gcc.exe" -x c -mthumb -O1 -ffunction-sections -mlong-calls -g3 -Wall -mcpu=cortex-m4 -c -std=gnu99 main.c -o main.o
...
"...\arm-none-eabi-gcc.exe" weak_handlers.o main.o SEGGER_RTT.o SEGGER_RTT_printf.o SEGGER_RTT_Syscalls_GCC.o -mthumb -Wl,-Map="app.map" -Wl,--start-group -lm -Wl,--end-group -Wl,--gc-sections -mcpu=cortex-m4 -T flash.ld -o app.elf
QUESTION: The reference programming project I'm using to compare my code against ( Atmel Studio LEDflasher example ) uses critical sections like the following: ( present on hri_nvmctrl_e54.h line 944 )
NVMCTRL_CRITICAL_SECTION_ENTER();
((Nvmctrl *)hw)->CTRLA.reg |= NVMCTRL_CTRLA_RWS(mask);
NVMCTRL_CRITICAL_SECTION_LEAVE();
Which I don't understand. I tried to follow those function implementations to see what they were doing and ended up with the following code:
// ==============================================================================================
// Enter critical section.
// ==============================================================================================
// Get primask
register uint32_t __regPriMask __asm__("primask");
uint32_t volatile *atomic = __regPriMask;
// Disable IRQ interrupts by setting the I-bit in the CPSR.
// Can only be executed in Privileged modes.
__asm__ volatile ("cpsid i" : : : "memory");
// Memory barrier
do {\
__asm__ volatile ("isb 0xF":::"memory");
__asm__ volatile ("dmb 0xF":::"memory");
__asm__ volatile ("isb 0xF":::"memory");
} while (0U);
// ==============================================================================================
// 25.8.1 Control A
// ==============================================================================================
// NVMCTRL-> offset: CTRLA
// 0x41004000U 0x00000000U
(*(volatile uint32_t*)0x41004000U) = 0x01000400U;
// ==============================================================================================
// Leave critical section.
// ==============================================================================================
// Memory barrier
do {\
__asm__ volatile ("isb 0xF":::"memory");
__asm__ volatile ("dmb 0xF":::"memory");
__asm__ volatile ("isb 0xF":::"memory");
} while (0U);
// Set primask
__regPriMask = &atomic;
Do any of this memory barriers make sense? Is wrapping a asm volatile ("dmb 0xF":::"memory"); between two asm volatile ("isb 0xF":::"memory"); a common useful implementation? What would those instructions mean? I'm not sure if the "GoTo Implementation" path was followed correctly to end up with these statements!
I'd like to thank everyone in advance for your time and hope this question helps others in the future !