2024-03-07 05:26 AM
Applies to: STM32L100xx, STM32L151xx, STM32L152xx, STM32L162xx.
Hi,
in order to write robust drivers, I'm kindly asking a few questions to developers who are into the silicon design. This is the 1st:
ref. RM0038 Reference manual Rev 18, paragraph 27.6.1 Status register (USART_SR) vaguely says that error flag(s):
"It is cleared by a software sequence (a read to the USART_SR register followed by a read to the USART_DR register)."
which of the following describes the real case:
0) "It is cleared by a read to USARTx_DR which happens AFTER a read to USARTx_SR, without any other access in between to the USARTx memory space*."
1) "It is cleared by a read to USARTx_DR which happens AFTER a read to USARTx_SR, without any other access in between to the APBx* linked to the USARTx."
2) ???
(*) see 2.3 Memory map - Table 5. Register boundary addresses
Thank you very much in advance.
Solved! Go to Solution.
2024-04-15 07:27 AM
Hello @elKarro ,
After checking I can confirm that scenario 2 is the right scenario:
2) It is cleared by a read to USARTx_DR which happens AFTER a read to USARTx_SR.
even if other accesses are done in between. it does not matter, and it doesn't matter either if it is done by CPU or DMA.
The state machine of UART needs to detect the sequence as described in RM (a read to the USART_SR register followed by a read to the USART_DR register) things can happen in between and access to the UART address space as well.
BR
2024-03-07 05:55 AM
2) It is cleared by a read to USARTx_DR which happens AFTER a read to USARTx_SR.
No caveats needed.
You'll always have a race condition where the flag can be set again after you clear it, no way around that. But if that happens, your driver should detect it (again) and clear it (again).
2024-03-07 06:16 AM
Hi TDK, so:
void fn1(USART_TypeDef *uart) // this will clear
{
uint32_t sr = uart->SR;
uint32_t dr = uart->DR;
...
}
void fn2(USART_TypeDef *uart) // this won't clear
{
uint32_t sr = uart->SR & (USART_SR_IDLE | USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE);
uint32_t dr = uart->DR;
...
}
is that so?
2024-03-07 06:23 AM
Those will both clear. In both of them, you're reading SR, then you're reading DR.
2024-03-07 06:23 AM
Logic operations to SR like in the second case above do not cause another AHB acces i.m.h.o.
void fn3(USART_TypeDef *uart) // this won't clear
{
uint32_t sr = uart->SR & (USART_SR_IDLE | USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE);
uint32_t cr = uart->CR;
uint32_t dr = uart->DR;
...
}
would probably cause a problem.
2024-03-07 06:59 AM - edited 2024-03-07 07:17 AM
Hi Uwe Bonnes, yours is the right direction to face the original question of the post.
The fn1() / fn2() is a tricky question:
fn1() does exactly what the RM0038 says to do, nothing more nothing less, but if we look at its asm we see that between the read of SR (ldr r3, [r0]) and the read of DR (ldr r3, [r0, #4]) other opcodes take place.
So, the point is: the sequence would be broken by an access to what? To the USART mem space? To AHB? To APBx?
I can do a try, I cause an ORE on purpose and I see which sequence works, but it wouldn't be as exhaustive as an answer from someone who knows the silicon.
fn1:
ldr r3, [r0] <- here SR is read
sub sp, sp, #8
str r3, [sp]
ldr r3, [r0, #4] <- here DR is read
str r3, [sp, #4]
ldr r3, [sp]
ldr r3, [sp, #4]
add sp, sp, #8
bx lr
2024-03-07 07:43 AM
Reading rm0038 again, I think anything can be done in between read SR and read DR, just as TDK explained.
2024-03-07 10:28 AM
> the sequence would be broken by an access to what?
Why do you think it can be broken by an access to something? Does something in the RM lead you to believe that, or does some actual testing indicate that is the case?
It is certainly doesn't expect that SR is read in one instruction and DR is read on the next. That would never work with any sensible code. HAL doesn't do that. You would have to go out of your way to code that in assembly.
2024-03-07 10:37 AM - edited 2024-03-07 10:41 AM
Personally I think this is a Schrödinger's Cat problem.
I think SR will clear simply by reading DR, you don't have to look at it
On subsequent STM32 the U(S)ARTs can have sticky error bits, that must be cleared explicitly.
Just be conscious that any read of DR has the potential to change SR's state, which is why floating a debug view window over the peripheral registers is so invasive/dangerous.
Causes headaches on peripherals with FIFO's that present in the address space, potentially decoding over a broad range of addresses.
SR can also change independently of the APB bus accesses, and faster too.
This can be particularly problematic when using the TIM->SR &= ~1 RMW forms, the HW/IC is designed not to use those forms, but just a write which performs an atomic AND operation
2024-03-07 11:57 AM - edited 2024-03-07 12:11 PM
Why do you think it can be broken by an access to something?
I could rephrase your question as: "why do you think that a read to SR followed by a read to DR will clear the flags?".
The answer is that the sequence clears those flags because inside the USART module there's a Finite State Machine which switches its state depending on the access to USART. The access to USART occurs through APBx and the FMS presumably perform this (pseudo C):
void USART_FSM()
{
enum {
idle_state,
state_1
} fms_state = idle_state;
while (1)
{
if (read access to USART module)
{
switch (fms_state)
{
case idle_state:
{
if (read access address == USART.SR)
fms_state = state_1;
break;
}
case state_1:
{
if (read access address == USART.DR)
clear(USART.SR);
fms_state = idle_state;
break;
}
}
}
}
}
Does something in the RM lead you to believe that, or does some actual testing indicate that is the case?
Is the lacking of documentation in the RM which worries me, because I'm about to write USART+DMA drivers. I explain:
USART doesn't know if it's accessed by DMA or by CPU, it only sees that something is accessing through APB. What happens if DMA reads USART.DR after I read USART.SR? I guess that all error flags would be cleared, so every time I read SR I must manage ALL the flags. HAL cuts short: if flags are set, it resets everything, and that induce me to think that the problem I'm supposing is real.
It is certainly doesn't expect that SR is read in one instruction and DR is read on the next. That would never work with any sensible code. HAL doesn't do that. You would have to go out of your way to code that in assembly.
My way is deeper than assembly, I'm focused on what happens inside the silicon, that is the only way to have full control over things. HAL is just a way to use the FSM implemented inside STM32, and a very resource-draining way.
PS I'm gonna run tests, I will share results and sources.