Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[patch #6418] Yet another delay routines #754

Open
avrs-admin opened this issue Jan 31, 2022 · 4 comments
Open

[patch #6418] Yet another delay routines #754

avrs-admin opened this issue Jan 31, 2022 · 4 comments
Labels
enhancement New feature or request

Comments

@avrs-admin
Copy link

Wed 13 Feb 2008 11:26:51 PM CET

This code is proposed to replace actual avr-libc delay routines. Current delay routines in <util/basic_delay.h> and <util/delay.h> present several problems:

  1. They require the optimization enabled to avoid floating point libraries code bloating the executable.

  2. The arguments passed must be constant but the compiler does not warn the user when a variable is passed. This can also bloat the code with floating point libraries and delay's accuracy loss.

  3. There is a lot of asymmetry between the functions used for microseconds and for milliseconds: the time is not continuous, the 1/10 millisecond precision is arbitrary (F_CPU value is not considered) and inaccurate, and delays are short for high frequency processors...

Solutions

Problem 1. Can be solved eliminating temporary intermediate variables and letting the compiler to do all the arithmetics. If the variables are not defined the intermediate integer and floating point constant values do not need to be stored, even when optimization level is -O0. To accomplish this it's mandatory a conversion from inline function (the argument is an automatic storage class variable) to macro.

Problem 2. The proposed solution is to check the constant parameter passed against an inline assembler constraint in an empty dummy asm() instruction. This also requires to transform the inline function in a macro. Passing a variable as the argument to the macro generates an error a bit obfuscated.

Problem 3. The compiler can choose the delay with the minimum penalty in size. Frequency and time should be taken as a whole, i.e. the number of CPU cycles that must elapse to obtain the delay. And big sized delay loops can produce accurate delays by increasing the counter word size from 16 bit maximum to 24 bit and 32 bit.

Attached files

The new files <util/delay_basic.h> and <util/delay.h> are downwards compatible with currently existing routines.

Another two delay loop functions have been defined: _delay_loop3() and _delay_loop4() to get 24 bit and 32 bit counter values. The code size of this loops is somewhat big including compiler overhead but they allow really, really long delays.

Additionaly, constant argument versions delay loop macros were defined to reduce the code generated and minimize errors when optimization is disabled.

The core of these header files are two delay macros named _delay_cycles() and _delay_exact_cycles(). They do not depend on F_CPU and as such they are placed in delay_basic.h. They wrap to the shortest loop  suitable to obtain a delay in CPU cycles (_delay_cycles() accuracy is +/- 0.5 loop step size and _delay_exact_cycles() is +/- 0.5 CPU cycles). Single instruction sequences without loops are used for very short delays (less than 7 CPU cycles). _delay_cycles_exact() inserts padding instructions after the loops at the expense of increasing code size to obtain the best delay accuracy.

Padding instructions used in the code are NOP (1 word, 1 CPU cycle) and RJMP (1 word, 2 CPU cycles). LPM (1 word, 3 cycles) was discarded due to lack of documentation about the behaviour accessing inexistent Flash addresses and other possible fuses and lock bits issues. Also, LPM is not available in all the AVR devices.

The constraint 'd' was preferred to 'w' in 32 bit data sizes to facilitate to the compiler the allocation of the loop variable, at the expense of increasing the code in one instruction (sbiw could be used instead of subi + sbci). Internal constant loop version macros for optimization enabled use a different approach splitting the big data counters (24 bit and 32 bit) in two parts, with 'w' constraint applied only to the less significant 16 bit of the data. For the remaining data part (8 bit or 16 bit) is up to the compiler (constraint 'r') to decide the registers to be used. Optimization disabled counterparts avoid the compiler overhead previous to the loop (not fixed time) moving the counter precharge to the first loop instructions and the compiler overhead after the loop storing data in the temporary and zero registers (complete for loops 1 and 2, partial for loop 3, nothing for loop 4)

The code philosophy remains the same: despite of the increased code complexity in the header files the compiler performs all the arithmetics and the resulting delay code remains short. The only incompatibility can arise with the imposition of using constant values.

Interface summary <util/delay_basic.h>

static inline void _delay_loop_1(uint8_t __count);
static inline void _delay_loop_2(uint16_t __count);
static inline void _delay_loop_3(uint32_t __count);
static inline void _delay_loop_4(uint32_t __count);

#define _delay_cycles(__cycles)
#define _delay_exact_cycles(__cycles)

#define _delay_f_us(__freq, __us)
#define _delay_f_ms(__freq, __ms)
#define _delay_f_s(__freq, __s)

#define _delay_exact_f_us(__freq, __us)
#define _delay_exact_f_ms(__freq, __ms)
#define _delay_exact_f_s(__freq, __s)

Interface summary <util/delay.h>

#define _delay_us(__us)
#define _delay_ms(__ms)
#define _delay_s(__s)

#define _delay_exact_us(__us)
#define _delay_exact_ms(__ms)
#define _delay_exact_s(__s)

Note. Doxygen comments haven't been compiled/tested.

file #15033: delay.tgz
file #15289: delay2.tgz

This issue was migrated from https://savannah.nongnu.org/patch/?6418

@avrs-admin
Copy link
Author

Carlos Lamas
Mon 17 Mar 2008 03:29:25 PM CET

Following David Brown's suggestion of using __builtin_constant_p() GCC function to test the macro arguments I have uploaded a new version of the headers. The main changes are:

  • Errors emitted are controlled and now error messages are clear. Extra lines of printed messages also help the IDEs to locate the source line of error in the C code.

  • Argument range checking is performed on the delay time intervals.

  • Comments were updated with the changes and some modifications were introduced for Doxygen compatibility.

  • Some internal use macros called only once from __SHORT_DELAY() in the headers have been eliminated (moving the code to the caller) to reduce the number of macro definitions.

Everything else remains the same.

(file #15289)

@avrs-admin
Copy link
Author

Clarence Risher
Mon 01 Dec 2008 10:40:31 PM CET

I have pursued alternative methods to improve the accuracy of the delay functions.  Your work here is impressive, and my testing indicates that it functions as advertised.  I will be using your patch personally, and look forward to it being integrated.

@avrs-admin
Copy link
Author

Timothy Baldwin
Sun 11 Oct 2009 04:31:31 PM CEST

Rather than generating an error when an integer non-constant is passed the non-exact macros could form a loop around a constant delay.

#define _delay_ms(__ms)                                                      \
do {                                                                         \
if (!__builtin_constant_p(F_CPU)) {                                      \
_DELAY_EMIT_ERROR(__STRINGIFY(F_CPU)                                 \
" is not a constant for _delay_ms");               \
} else if (__builtin_constant_p(__ms)) {                                 \
_delay_f_ms(F_CPU, __ms);                                            \
} else if ((__typeof__((_ms) + 0))0.25 == 0)  {                          \
__typeof__((__ms) + 0) __ms2 = __ms;                                 \
while (__ms2--) _delay_f_ms(F_CPU, 1);                               \
} else {                                                                 \
_DELAY_EMIT_ERROR(__STRINGIFY(__ms)                                  \
" is not a constant or integer for _delay_ms");    \
}                                                                        \
} while (0)

@sprintersb sprintersb added the enhancement New feature or request label Feb 8, 2024
@sprintersb
Copy link
Collaborator

This proposal is quite old, it dates back to 2008 and prior to the introduction of __builtin_avr_delay_cycles in 2011 with avr-gcc v4.7.

At least some problems are solved:

  • The compiler will complain when no compile-time constant is passed as ticks.
  • The generated code is exact to the cycle.
  • Code size is minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants