Do I need a mutex to protect 8-bit variables on STM32L476 with ARMv7E-M architecture using FreeRTOS?

Good day @Middleware & OS , I am using an STM32L476 with CMSIS OS2, which implements FreeRTOS v10.3.1. The STM32L476 is based on a Cortex-M4 MCU that follows the ARMv7E-M architecture: https://en.wikipedia.org/wiki/ARM_Cortex-M. The key difference between ARMv7E-M and ARMv7-M is the inclusion of DSP extensions in ARMv7E-M, which I do not use in my application. Hence, functionally, my setup is akin to ARMv7-M: https://developer.arm.com/documentation/ddi0403/latest (Page A1-22). According to the ARM reference manual, ARMv7-M guarantees atomicity for single-byte (8-bit) operations: https://developer.arm.com/documentation/ddi0403/latest (Page A3-80). Given this, do I still need to use a mutex to protect 8-bit variables (e.g., uint8_t and int8_t) in my application? I have found discussions on the need for mutexes with 32-bit variables and for non-ARM Cortex M4 environments, but I seek clarity specifically for this scenario. My expectation is that a mutex is not required for 8-bit variable types, but I would like to confirm this understanding.
Solution:
Yes @Sterling , multi-byte variables like 16-bit and 32-bit types are not guaranteed to be atomic on ARM Cortex-M4. For these variables, you should use a mutex or other synchronization mechanism to protect access if they are shared between tasks. For example, you can use a FreeRTOS mutex: ```c uint32_t sharedVar; SemaphoreHandle_t xMutex;...
Jump to solution
4 Replies
UC GEE
UC GEE2mo ago
Good question @Sterling . You are correct 💯 that ARMv7-M, which includes the Cortex-M4, guarantees atomicity for single-byte (8-bit) operations. This means that read and write operations to 8-bit variables are atomic and do not require additional synchronization mechanisms such as mutexes. Therefore, in a single-core environment, you generally do not need to use a mutex to protect 8-bit variables. However, you should be cautious if the variable is accessed from both an interrupt service routine (ISR) and the main application code, as this might require disabling interrupts to ensure atomic access. For example:
uint8_t sharedVar;

void main(void) {
// ...
__disable_irq();
sharedVar = newValue; // Critical section
__enable_irq();
// ...
}

void ISR_Handler(void) {
sharedVar = isrValue;
}
uint8_t sharedVar;

void main(void) {
// ...
__disable_irq();
sharedVar = newValue; // Critical section
__enable_irq();
// ...
}

void ISR_Handler(void) {
sharedVar = isrValue;
}
In this case, you disable interrupts before accessing the variable and re-enable them afterward to prevent race conditions.
Sterling
Sterling2mo ago
That makes sense @UC GEE . So, if the 8-bit variable is only accessed from tasks running in the main application context and not from an ISR, no mutex is needed. But if it is accessed from both ISRs and tasks, then disabling and enabling interrupts is required. What about multi-byte variables like 16-bit or 32-bit? Do I need a mutex for those? 🤔
Solution
UC GEE
UC GEE2mo ago
Yes @Sterling , multi-byte variables like 16-bit and 32-bit types are not guaranteed to be atomic on ARM Cortex-M4. For these variables, you should use a mutex or other synchronization mechanism to protect access if they are shared between tasks. For example, you can use a FreeRTOS mutex:
uint32_t sharedVar;
SemaphoreHandle_t xMutex;

void main(void) {
xMutex = xSemaphoreCreateMutex();
if(xMutex != NULL) {
if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
sharedVar = newValue;
xSemaphoreGive(xMutex);
}
}
}

void ISR_Handler(void) {
// Ensure no preemption and data corruption
taskENTER_CRITICAL();
sharedVar = isrValue;
taskEXIT_CRITICAL();
}
uint32_t sharedVar;
SemaphoreHandle_t xMutex;

void main(void) {
xMutex = xSemaphoreCreateMutex();
if(xMutex != NULL) {
if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
sharedVar = newValue;
xSemaphoreGive(xMutex);
}
}
}

void ISR_Handler(void) {
// Ensure no preemption and data corruption
taskENTER_CRITICAL();
sharedVar = isrValue;
taskEXIT_CRITICAL();
}
In this case, the mutex ensures that only one task can access the variable at a time. For access from ISRs, you use taskENTER_CRITICAL() and taskEXIT_CRITICAL() to protect the critical section.
Sterling
Sterling2mo ago
Okayy, thanks for this @UC GEE 🙏
Want results from more Discord servers?
Add your server