Atomic access to ARM peripheral registers

879 views Asked by At

I want to use the overflow, compare match and capture functionality of a general purpose timer on a ST2M32F103REY Cortex M3 at the same time. CC1 is configured as compare match and CC3 is configured as capture. The IRQ handler looks as follows:

void TIM3_IRQHandler(void) {
  if(TIM3->SR & TIM_SR_UIF){
      TIM3->SR &= ~TIM_SR_UIF;
      // do something on overflow
  }

  if(TIM3->SR & TIM_SR_CC1IF) {
     TIM3->SR &= ~TIM_SR_CC1IF;
     // do something on compare match
  }

  if(TIM3->SR & TIM_SR_CC3IF) {
     TIM3->SR &= ~TIM_SR_CC3IF;
     // do something on capture
  }
}

In principle, it works good, but it sometimes seems to skip a part. My theory is that this is because the operations of resetting the IRQ flags, e.g. TIM3->SR &= ~TIM_SR_UIF, is not atomic*, so it might happen that for example a TIM_SR_CC1IF occurring between load and store is overwritten.

* The disassembly of the instruction is as follows

8012e02:    8a13        ldrh    r3, [r2, #16]
8012e06:    f023 0301   bic.w   r3, r3, #1
8012e0a:    041b        lsls    r3, r3, #16
8012e0c:    0c1b        lsrs    r3, r3, #16
8012e0e:    8213        strh    r3, [r2, #16]
  • Is this plausible? Can the content of the TIM3->SR register change during the execution of the IRQ handler?
  • Is there a possibility to do an atomic read and write to the TIM3->SR register?
  • Is there another suitable solution?

By the way: There is similar question but that one is about protecting access by multiple processes or cores and not about protecting simultaneous access by software and hardware.

1

There are 1 answers

1
Maximilian Köstler On BEST ANSWER

Section 15.4.5 of the reference manual (CD00171190) states that all bits in TIMx->SR work in rc_w0 mode (or are reserved).

According to the programming manual (PM0056):

read/clear (rc_w0): Software can read as well as clear this bit by writing 0. Writing ‘1’ has no effect on the bit value.

This means that you can simplify your code to entirely avoid the read-modify-write cycle and instead just use TIM3->SR = ~TIM_SR_UIF instead.

Many application notes use a read-modify-write to clear interrupts, such as examples by Keil, but this is unnecessary and potentially dangerous, as you have experienced. In the ST application note DM00236305 (section 1.3.2), only a write operation is used.

Note, however, that when working with the NVIC, the register used for resetting is rc_w1.