cancel
Showing results for 
Search instead for 
Did you mean: 

Atomic register write from cmsis by stm32HAL, what's the point?

SeyyedMohammad
Senior III

On the code below:

/* Enable the DMA transfer for transmit request by setting the DMAT bit
		    in the UART CR3 register */
		    ATOMIC_SET_BIT((&huart1)->Instance->CR3, USART_CR3_DMAT);

The HAL used:

/* Use of CMSIS compiler intrinsics for register exclusive access */
/* Atomic 32-bit register access macro to set one or several bits */
#define ATOMIC_SET_BIT(REG, BIT)                             \
  do {                                                       \
    uint32_t val;                                            \
    do {                                                     \
      val = __LDREXW((__IO uint32_t *)&(REG)) | (BIT);       \
    } while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U); \
  } while(0)

But how it, and it's subassembly codes really works, and why such wierd instruction to modify a register?

1 ACCEPTED SOLUTION

Accepted Solutions

I may regret this, but....

We DO know exactly how this works. These are standard ARM/Cortex instructions, documented in the ST Cortex M4 programmer's manual (PM0214) and on the ARM site (I'm too lazy to find the URL).

This construct DOES presume that there will be a limited number of interrupts that also attempt to use that memory location. Therefore, the while() loop is presumed to only execute a small number of times (typically 1 or 2 times). However, it WILL keep looping as long as an interrupt happens (1) between the LDREX and STREX instructions AND (2) that interrupt accesses the same memory location as the LDREX instruction.

So as long as you don't have a flood of interrupts all trying to access that one memory location, this code will never "freeze".

View solution in original post

13 REPLIES 13

Cube/HAL anticipates, that UART CR3 will be accessed in both "main" and "interrupt" (or in different tasks in a multitasking environment), and this is one of the standard mechanisms to prevent collisions in this case.


_legacyfs_online_stmicro_images_0693W00000biJFQQA2.png 

JW

Hi @Community member​ 

Thanks for good referencing and simple description.

I'm courious in what scenario we need to have to use it? What scenario will procuces wrong behaviour?

Hard to say, look at the bits where it's used, and the specific MCU it's being applied on, say a multi-core H7..

Peripherals tend to be particularly difficult to work with atomically, as these are windows into complex state-machines, not memory cells.

Where atomic behaviour is critical, the IC designer has typically created a way to do a singular WRITE to the register to change it in a single cycle / action. ie clearing TIM->SR bits, or GPIO->BSRR

The classic RMW on TIM->SR for example can span many TIM CLK cycles, resulting in it clearing/missing interrupts

ie

TIM->SR &= ~1; // Has side-effect of clearing more than UIF if other sources signal between read and write phases.

TIM->SR = ~1; // This is the prescribed usage

This is also a critical failing of Bit-Banding

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Hi @Community member​ 

You mean this atomic is to enforce peripheral to accept the change of it's registers or show to us(CPU) it's register in a single clock of CPU, though CPU is not at the same clock frequency of peripheral? doesn't it? But isn' it a contradiction? since they are not typycally in the same clock frequency!

Best regards!

I'm struggling with the value of this ATOMIC macro.

I suspect ST is just using it across a number of STM32 MCU and MPU libraries for consistency sake, or to pass some validation suite.

It looks to be worthless and dangerous from a peripheral side perspective.

Jan's observation about two thread in a single machine seem like reason to use it on certain bits, ie where your main code, or one or more interrupts can modify USART->CR3 to set/clear bits, and without it there's a hazard / race-condition

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Then the CPU must be freezes in some cycle to pretend it doing it on single cycle, doesn't it?

> Then the CPU must be freezes in some cycle to pretend it doing it on single cycle, doesn't it?

No, the mechanism is different here: it does not prevent the "interrupting" process to change the register in question; rather, it is the "interrupted" process which learns that it has been interrupted and then can retry the whole RMW process (that's what the inner do-while cycle does).

JW

Since I'm newbie in this context I can't understand completely, can you explain more, pleasae? I only could understood RMW=ReadWriteModify.

Do you mean:

It doesn't prevent interrupt or thread2 access, it can understand the new interupt is in progress, it wait until thread1 finishes it's job and server interrupt/thread2.

In other word you mean it only freezes on collision, not everytime, doesn't it?

But since it's engaged in while(), then the CPU is actually freezed, but interrupt!!!!!!!! must not happened!?

> It looks to be worthless and dangerous from a peripheral side perspective.

This was already discussed here for STM32F4. IIRC, the ARM docum says that LDREX/STREX should be used only on "normal" memory. How it works on device registers is vendor specific. ST folks wrote this code and tested, so...

The alternative would be __disable__irq() around the RMW operation.