Search code examples
timerstm32stm32f4stm32f0

STM32 blocking delays not consistent with interrupts disabled


I am running on an STM32F0xx micro, and I have the following code that simply toggles a pin using blocking delays (yes I know blocking delays are bad, not point here).

uint32_t ticks = 0;
// Disable interrupts
__disable_irq();
for (int bit = 0; bit < 10; bit++) {
  // Toggle pin high
  WritePin(GPIO_PIN_SET);
  ticks = 500;
  while (ticks--) {
    __NOP();
  }
  // Toggle pin low
  WritePin(GPIO_PIN_RESET);
  ticks = 500;
  while (ticks--) {
    __NOP();
  }
  // Repeat
  WritePin(GPIO_PIN_SET);
  ticks = 500;
  while (ticks--) {
    __NOP();
  }
  WritePin(GPIO_PIN_RESET);
  ticks = 500;
  while (ticks--) {
    __NOP();
  }
}
__enable_irq();

I disable interrupts to make sure nothing else is going on at this point. When I scope these waveforms, I see 10 clock periods; however, the periods of these waveforms do not all match. All even waveforms (0,2,4,6,8) have the same period, and all odd waveforms (1,3,5,7,9) have the same period, but the even and odd waveforms differ by a significant amount (%12). The even waveforms correlate to the first delay toggling, and odd to the second in the for loop. I do not have any idea why these would differ in period. Anyone have any insight here?


Solution

  • See this documentation about NOP on ARM Cortex-M0 core:

    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDJJGFB.html

    Operation

    NOP performs no operation and is not guaranteed to be time consuming. The processor might remove it from the pipeline before it reaches the execution stage.

    Use NOP for padding, for example to place the subsequent instructions on a 64-bit boundary.

    To that, you must add other varying factors like pipeline refills (due to branching) and flash wait-states (varying because the instruction blocks in different loops might not be aligned perfectly for the flash accelerator).

    The only reliable way to perform precise delays is to use timer.