Bug in delayMicroseconds: 25% too short for const delay < 64us

gwideman

Well-known member
Function delayMicroseconds(delay) for AVR Teensies has a significant bug that causes delays to execute in only 75% of the requested time, when both of the following are true:

-- The requested delay is known to be a constant at compile time, AND
-- The requested delay is < 64usec

The bug is in:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy\core_pins.h,

Code:
if (__builtin_constant_p(usec)) {
[...]
  if (tmp > 0) {
    if (tmp < 256) {
      uint8_t tmp2 = tmp;
      asm volatile(
        "L_%=_loop:"               // 1 to load
          "subi %0, 1"  "\n\t"     // 2
           // "nop\n\t"            // Uncomment to fix the problem. 
           "brne L_%=_loop" "\n\t" // 2 (1 on last)
           : "=d" (tmp2)
           : "0" (tmp2)
      );
    } else { [loop with correct timing] }
  } else { another different loop with correct timing }

The problem is revealed by the comment for subi, which indicates 2 cycles, when the Atmel doc says only 1 cycle. If you add the nop I show, then the timing works properly.

For testing, I used:

Code:
const int ledPin = 6;
[...]
const long delayus = 50;

//----------------
void loop() {
//----------------
  while (true) {
    digitalWriteFast(ledPin, HIGH);   // set the LED on
    delayMicroseconds(delayus);                 
    digitalWriteFast(ledPin, LOW);    // set the LED off
    delayMicroseconds(delayus);                 
  }  
}

I observe the results on a digital scope, and it shows the period is only 75.4usec, when it should be 100usec (plus while loop overhead). Adding the NOP as described above fixes the problem. Doing either of the following also fixed the problem:
-- requesting a delay >= 64 ((so that (tmp < 256) is false)
-- deleting the "const" before "long delayus = 50"

Further details: I used Arduino 1.8.4 and Teensyduino 1.38, both downloaded two days ago.

As a side note, there may be libraries which use delayMicroseconds for bit-banging and so on, and may have adjusted to the incorrect timing provided by this function.

-- Graham
 
I've put this on my list of bugs to fix. It's probably too late to get into 1.39, but it's on the list for 1.40.
 
Back
Top