Browse Source

🐛 Fix AVR DELAY_US int overflow (#22268)

vanilla_fb_2.0.x
Skruppy 4 years ago
committed by Scott Lahteine
parent
commit
7ae099f2be
  1. 76
      Marlin/src/HAL/shared/Delay.h

76
Marlin/src/HAL/shared/Delay.h

@ -97,43 +97,65 @@ void calibrate_delay_loop();
#define DELAY_US(x) DelayCycleFnc((x) * ((F_CPU) / 1000000UL)) #define DELAY_US(x) DelayCycleFnc((x) * ((F_CPU) / 1000000UL))
#elif defined(__AVR__) #elif defined(__AVR__)
FORCE_INLINE static void __delay_up_to_3c(uint8_t cycles) {
switch (cycles) {
case 3:
__asm__ __volatile__(A("RJMP .+0") A("NOP"));
break;
case 2:
__asm__ __volatile__(A("RJMP .+0"));
break;
case 1:
__asm__ __volatile__(A("NOP"));
break;
}
}
#define nop() __asm__ __volatile__("nop;\n\t":::) // Delay in cycles
FORCE_INLINE static void DELAY_CYCLES(uint16_t cycles) {
if (__builtin_constant_p(cycles)) {
if (cycles <= 3) {
__delay_up_to_3c(cycles);
}
else if (cycles == 4) {
__delay_up_to_3c(2);
__delay_up_to_3c(2);
}
else {
cycles -= 1 + 4; // Compensate for the first LDI (1) and the first round (4)
__delay_up_to_3c(cycles % 4);
FORCE_INLINE static void __delay_4cycles(uint8_t cy) { cycles /= 4;
// The following code burns [1 + 4 * (rounds+1)] cycles
uint16_t dummy;
__asm__ __volatile__( __asm__ __volatile__(
// "manually" load counter from constants, otherwise the compiler may optimize this part away
A("LDI %A[rounds], %[l]") // 1c
A("LDI %B[rounds], %[h]") // 1c (compensating the non branching BRCC)
L("1") L("1")
A("dec %[cnt]") A("SBIW %[rounds], 1") // 2c
A("nop") A("BRCC 1b") // 2c when branching, else 1c (end of loop)
A("brne 1b") : // Outputs ...
: [cnt] "+r"(cy) // output: +r means input+output [rounds] "=w" (dummy) // Restrict to a wo (=) 16 bit register pair (w)
: // input: : // Inputs ...
: "cc" // clobbers: [l] "M" (cycles%256), // Restrict to 0..255 constant (M)
[h] "M" (cycles/256) // Restrict to 0..255 constant (M)
:// Clobbers ...
"cc" // Indicate we are modifying flags like Carry (cc)
); );
} }
// Delay in cycles
FORCE_INLINE static void DELAY_CYCLES(uint16_t x) {
if (__builtin_constant_p(x)) {
#define MAXNOPS 4
if (x <= (MAXNOPS)) {
switch (x) { case 4: nop(); case 3: nop(); case 2: nop(); case 1: nop(); }
} }
else { else {
const uint32_t rem = (x) % (MAXNOPS); __asm__ __volatile__(
switch (rem) { case 3: nop(); case 2: nop(); case 1: nop(); } L("1")
if ((x = (x) / (MAXNOPS))) A("SBIW %[cycles], 4") // 2c
__delay_4cycles(x); // if need more then 4 nop loop is more optimal A("BRCC 1b") // 2c when branching, else 1c (end of loop)
} : [cycles] "+w" (cycles) // output: Restrict to a rw (+) 16 bit register pair (w)
: // input: -
#undef MAXNOPS : "cc" // clobbers: We are modifying flags like Carry (cc)
);
} }
else if ((x >>= 2))
__delay_4cycles(x);
} }
#undef nop
// Delay in microseconds // Delay in microseconds
#define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL)) #define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL))

Loading…
Cancel
Save