From f993e512504fda839d6f04d5dcc86f8b151698e0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Sat, 28 Nov 2015 16:17:31 +1100 Subject: [PATCH] timers.h: Remove compile-time-auto-inlining complexity Fixes bug mentioned in #72 (oops!), also progress towards #57. --- core/esp_timer.c | 118 +++++++- core/include/esp/timer.h | 108 +++++-- core/include/esp/timer_private.h | 266 ------------------ examples/terminal/terminal.c | 2 + .../stdin_uart_interrupt.c | 1 + 5 files changed, 186 insertions(+), 309 deletions(-) delete mode 100644 core/include/esp/timer_private.h diff --git a/core/esp_timer.c b/core/esp_timer.c index bb23f51..c2b45a0 100644 --- a/core/esp_timer.c +++ b/core/esp_timer.c @@ -6,30 +6,122 @@ * BSD Licensed as described in the file LICENSE */ #include +#include #include #include -/* - * These are the runtime implementations for functions that are linked in if any of - * the arguments aren't known at compile time (values are evaluated at - * compile time otherwise.) - */ -uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div) +/* Timer divisor number to maximum frequency */ +#define _FREQ_DIV1 (80*1000*1000) +#define _FREQ_DIV16 (5*1000*1000) +#define _FREQ_DIV256 312500 + +const static uint32_t IROM _TIMER_FREQS[] = { _FREQ_DIV1, _FREQ_DIV16, _FREQ_DIV256 }; + +/* Timer divisor index to divisor value */ +const static uint32_t IROM _TIMER_DIV_VAL[] = { 1, 16, 256 }; + +void timer_set_interrupts(const timer_frc_t frc, bool enable) { - return _timer_freq_to_count_impl(frc, freq, div); + const uint32_t dp_bit = (frc == FRC1) ? DPORT_INT_ENABLE_FRC1 : DPORT_INT_ENABLE_FRC2; + const uint32_t int_mask = BIT((frc == FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2); + if(enable) { + DPORT.INT_ENABLE |= dp_bit; + _xt_isr_unmask(int_mask); + } else { + DPORT.INT_ENABLE &= ~dp_bit; + _xt_isr_mask(int_mask); + } } -uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div) +uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div) { - return _timer_time_to_count_runtime(frc, us, div); + if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256) + return 0; /* invalid divider */ + + if(freq > _TIMER_FREQS[div]) + return 0; /* out of range for given divisor */ + + uint64_t counts = _TIMER_FREQS[div]/freq; + return counts; } -bool _timer_set_frequency_runtime(const timer_frc_t frc, uint32_t freq) +uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div) { - return _timer_set_frequency_runtime(frc, freq); + if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256) + return 0; /* invalid divider */ + + const uint32_t TIMER_MAX = timer_max_load(frc); + + if(div != TIMER_CLKDIV_256) /* timer tick in MHz */ + { + /* timer is either 80MHz or 5MHz, so either 80 or 5 MHz counts per us */ + const uint32_t counts_per_us = ((div == TIMER_CLKDIV_1) ? _FREQ_DIV1 : _FREQ_DIV16)/1000/1000; + if(us > TIMER_MAX/counts_per_us) + return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */ + return us*counts_per_us; + } + else /* /256 divider, 312.5kHz freq so need to scale up */ + { + /* derived from naive floating point equation that we can't use: + counts = (us/1000/1000)*_FREQ_DIV256; + counts = (us/2000)*(_FREQ_DIV256/500); + counts = us*(_FREQ_DIV256/500)/2000; + */ + const uint32_t scalar = _FREQ_DIV256/500; + if(us > 1+UINT32_MAX/scalar) + return 0; /* Multiplying us by _FREQ_DIV256/500 will overflow uint32_t */ + + uint32_t counts = (us*scalar)/2000; + if(counts > TIMER_MAX) + return 0; /* counts value too high for timer type */ + return counts; + } } -bool _timer_set_timeout_runtime(const timer_frc_t frc, uint32_t us) +bool timer_set_frequency(const timer_frc_t frc, uint32_t freq) { - return _timer_set_timeout_impl(frc, us); + uint32_t counts = 0; + timer_clkdiv_t div = timer_freq_to_div(freq); + + counts = timer_freq_to_count(frc, freq, div); + if(counts == 0) + { + printf("ABORT: No counter for timer %u frequency %u\r\n", frc, freq); + abort(); + } + + timer_set_divider(frc, div); + if(frc == FRC1) + { + timer_set_load(frc, counts); + timer_set_reload(frc, true); + } + else /* FRC2 */ + { + /* assume that if this overflows it'll wrap, so we'll get desired behaviour */ + TIMER(1).ALARM = counts + TIMER(1).COUNT; + } + return true; +} + +bool timer_set_timeout(const timer_frc_t frc, uint32_t us) +{ + uint32_t counts = 0; + timer_clkdiv_t div = timer_time_to_div(us); + + counts = timer_time_to_count(frc, us, div); + if(counts == 0) + return false; /* can't set frequency */ + + timer_set_divider(frc, div); + if(frc == FRC1) + { + timer_set_load(frc, counts); + } + else /* FRC2 */ + { + TIMER(1).ALARM = counts + TIMER(1).COUNT; + } + + return true; } diff --git a/core/include/esp/timer.h b/core/include/esp/timer.h index 49559e4..fc4e6f8 100644 --- a/core/include/esp/timer.h +++ b/core/include/esp/timer.h @@ -23,43 +23,73 @@ typedef enum { } timer_frc_t; /* Return current count value for timer. */ -INLINED uint32_t timer_get_count(const timer_frc_t frc); +static inline uint32_t timer_get_count(const timer_frc_t frc) +{ + return TIMER(frc).COUNT; +} /* Return current load value for timer. */ -INLINED uint32_t timer_get_load(const timer_frc_t frc); +static inline uint32_t timer_get_load(const timer_frc_t frc) +{ + return TIMER(frc).LOAD; +} /* Write load value for timer. */ -INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load); +static inline void timer_set_load(const timer_frc_t frc, const uint32_t load) +{ + TIMER(frc).LOAD = load; +} /* Returns maximum load value for timer. */ -INLINED uint32_t timer_max_load(const timer_frc_t frc); +static inline uint32_t timer_max_load(const timer_frc_t frc) +{ + return (frc == FRC1) ? TIMER_FRC1_MAX_LOAD : UINT32_MAX; +} /* Set the timer divider value */ -INLINED void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div); +static inline void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div) +{ + if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256) + return; + TIMER(frc).CTRL = SET_FIELD(TIMER(frc).CTRL, TIMER_CTRL_CLKDIV, div); +} /* Enable or disable timer interrupts This both sets the xtensa interrupt mask and writes to the DPORT register that allows timer interrupts. */ -INLINED void timer_set_interrupts(const timer_frc_t frc, bool enable); +void timer_set_interrupts(const timer_frc_t frc, bool enable); /* Turn the timer on or off */ -INLINED void timer_set_run(const timer_frc_t frc, const bool run); +static inline void timer_set_run(const timer_frc_t frc, const bool run) +{ + if (run) + TIMER(frc).CTRL |= TIMER_CTRL_RUN; + else + TIMER(frc).CTRL &= ~TIMER_CTRL_RUN; +} /* Get the run state of the timer (on or off) */ -INLINED bool timer_get_run(const timer_frc_t frc); +static inline bool timer_get_run(const timer_frc_t frc) +{ + return TIMER(frc).CTRL & TIMER_CTRL_RUN; +} /* Set timer auto-reload on or off */ -INLINED void timer_set_reload(const timer_frc_t frc, const bool reload); +static inline void timer_set_reload(const timer_frc_t frc, const bool reload) +{ + if (reload) + TIMER(frc).CTRL |= TIMER_CTRL_RELOAD; + else + TIMER(frc).CTRL &= ~TIMER_CTRL_RELOAD; +} /* Get the auto-reload state of the timer (on or off) */ -INLINED bool timer_get_reload(const timer_frc_t frc); - -/* Return a suitable timer divider for the specified frequency, - or -1 if none is found. - */ -INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq); +static inline bool timer_get_reload(const timer_frc_t frc) +{ + return TIMER(frc).CTRL & TIMER_CTRL_RELOAD; +} /* Return the number of timer counts to achieve the specified * frequency with the specified divisor. @@ -68,14 +98,41 @@ INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq); * * Returns 0 if the given freq/divisor combo cannot be achieved. * - * Compile-time evaluates if all arguments are available at compile time. */ -INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, uint32_t freq, const timer_clkdiv_t div); +uint32_t timer_freq_to_count(const timer_frc_t frc, uint32_t freq, const timer_clkdiv_t div); + +/* Return a suitable timer divider for the specified frequency, + or -1 if none is found. + */ +static inline timer_clkdiv_t timer_freq_to_div(uint32_t freq) +{ + /* + try to maintain resolution without risking overflows. + these values are a bit arbitrary at the moment! */ + if(freq > 100*1000) + return TIMER_CLKDIV_1; + else if(freq > 100) + return TIMER_CLKDIV_16; + else + return TIMER_CLKDIV_256; +} /* Return a suitable timer divider for the specified duration in microseconds or -1 if none is found. */ -INLINED timer_clkdiv_t timer_time_to_div(uint32_t us); +static inline timer_clkdiv_t timer_time_to_div(uint32_t us) +{ + /* + try to maintain resolution without risking overflows. Similar to + timer_freq_to_div, these values are a bit arbitrary at the + moment! */ + if(us < 1000) + return TIMER_CLKDIV_1; + else if(us < 10*1000) + return TIMER_CLKDIV_16; + else + return TIMER_CLKDIV_256; +} /* Return the number of timer counts for the specified timer duration * in microseconds, when using the specified divisor. @@ -84,9 +141,8 @@ INLINED timer_clkdiv_t timer_time_to_div(uint32_t us); * * Returns 0 if the given time/divisor combo cannot be achieved. * - * Compile-time evaluates if all arguments are available at compile time. */ -INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div); +uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div); /* Set a target timer interrupt frequency in Hz. @@ -104,11 +160,8 @@ INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const t timer_set_run. Returns true on success, false if given frequency could not be set. - - Compile-time evaluates to simple register writes if all arguments - are available at compile time. */ -INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq); +bool timer_set_frequency(const timer_frc_t frc, uint32_t freq); /* Sets the timer for a oneshot interrupt in 'us' microseconds. @@ -124,13 +177,8 @@ INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq); timer_set_run(TIMER_FRC1, false); Returns true if the timeout was successfully set. - - Compile-time evaluates to simple register writes if all arguments - are available at compile time. */ -INLINED bool timer_set_timeout(const timer_frc_t frc, uint32_t us); - -#include "timer_private.h" +bool timer_set_timeout(const timer_frc_t frc, uint32_t us); #ifdef __cplusplus } diff --git a/core/include/esp/timer_private.h b/core/include/esp/timer_private.h deleted file mode 100644 index e57fd24..0000000 --- a/core/include/esp/timer_private.h +++ /dev/null @@ -1,266 +0,0 @@ -/* Private header parts of the timer API implementation - * - * Part of esp-open-rtos - * Copyright (C) 2015 Superhouse Automation Pty Ltd - * BSD Licensed as described in the file LICENSE - */ -#ifndef _ESP_TIMER_PRIVATE_H -#define _ESP_TIMER_PRIVATE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include "esp/dport_regs.h" - -/* Timer divisor index to max frequency */ -#define _FREQ_DIV1 (80*1000*1000) -#define _FREQ_DIV16 (5*1000*1000) -#define _FREQ_DIV256 312500 -const static uint32_t IROM _TIMER_FREQS[] = { _FREQ_DIV1, _FREQ_DIV16, _FREQ_DIV256 }; - -/* Timer divisor index to divisor value */ -const static uint32_t IROM _TIMER_DIV_VAL[] = { 1, 16, 256 }; - -INLINED uint32_t timer_get_count(const timer_frc_t frc) -{ - return TIMER(frc).COUNT; -} - -INLINED uint32_t timer_get_load(const timer_frc_t frc) -{ - return TIMER(frc).LOAD; -} - -INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load) -{ - TIMER(frc).LOAD = load; -} - -INLINED uint32_t timer_max_load(const timer_frc_t frc) -{ - return (frc == FRC1) ? TIMER_FRC1_MAX_LOAD : UINT32_MAX; -} - -INLINED void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div) -{ - if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256) - return; - TIMER(frc).CTRL = SET_FIELD(TIMER(frc).CTRL, TIMER_CTRL_CLKDIV, div); -} - -INLINED void timer_set_interrupts(const timer_frc_t frc, bool enable) -{ - const uint32_t dp_bit = (frc == FRC1) ? DPORT_INT_ENABLE_FRC1 : DPORT_INT_ENABLE_FRC2; - const uint32_t int_mask = BIT((frc == FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2); - if(enable) { - DPORT.INT_ENABLE |= dp_bit; - _xt_isr_unmask(int_mask); - } else { - DPORT.INT_ENABLE &= ~dp_bit; - _xt_isr_mask(int_mask); - } -} - -INLINED void timer_set_run(const timer_frc_t frc, const bool run) -{ - if (run) - TIMER(frc).CTRL |= TIMER_CTRL_RUN; - else - TIMER(frc).CTRL &= ~TIMER_CTRL_RUN; -} - -INLINED bool timer_get_run(const timer_frc_t frc) -{ - return TIMER(frc).CTRL & TIMER_CTRL_RUN; -} - -INLINED void timer_set_reload(const timer_frc_t frc, const bool reload) -{ - if (reload) - TIMER(frc).CTRL |= TIMER_CTRL_RELOAD; - else - TIMER(frc).CTRL &= ~TIMER_CTRL_RELOAD; -} - -INLINED bool timer_get_reload(const timer_frc_t frc) -{ - return TIMER(frc).CTRL & TIMER_CTRL_RELOAD; -} - -INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq) -{ - /* - try to maintain resolution without risking overflows. - these values are a bit arbitrary at the moment! */ - if(freq > 100*1000) - return TIMER_CLKDIV_1; - else if(freq > 100) - return TIMER_CLKDIV_16; - else - return TIMER_CLKDIV_256; -} - -/* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */ - -INLINED uint32_t _timer_freq_to_count_impl(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div) -{ - if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256) - return 0; /* invalid divider */ - - if(freq > _TIMER_FREQS[div]) - return 0; /* out of range for given divisor */ - - uint64_t counts = _TIMER_FREQS[div]/freq; - return counts; -} - -uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div); - -INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div) -{ - if(__builtin_constant_p(frc) && __builtin_constant_p(freq) && __builtin_constant_p(div)) - return _timer_freq_to_count_impl(frc, freq, div); - else - return _timer_freq_to_count_runtime(frc, freq, div); -} - -INLINED timer_clkdiv_t timer_time_to_div(uint32_t us) -{ - /* - try to maintain resolution without risking overflows. Similar to - timer_freq_to_div, these values are a bit arbitrary at the - moment! */ - if(us < 1000) - return TIMER_CLKDIV_1; - else if(us < 10*1000) - return TIMER_CLKDIV_16; - else - return TIMER_CLKDIV_256; -} - -/* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */ - -INLINED uint32_t _timer_time_to_count_impl(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div) -{ - if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256) - return 0; /* invalid divider */ - - const uint32_t TIMER_MAX = timer_max_load(frc); - - if(div != TIMER_CLKDIV_256) /* timer tick in MHz */ - { - /* timer is either 80MHz or 5MHz, so either 80 or 5 MHz counts per us */ - const uint32_t counts_per_us = ((div == TIMER_CLKDIV_1) ? _FREQ_DIV1 : _FREQ_DIV16)/1000/1000; - if(us > TIMER_MAX/counts_per_us) - return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */ - return us*counts_per_us; - } - else /* /256 divider, 312.5kHz freq so need to scale up */ - { - /* derived from naive floating point equation that we can't use: - counts = (us/1000/1000)*_FREQ_DIV256; - counts = (us/2000)*(_FREQ_DIV256/500); - counts = us*(_FREQ_DIV256/500)/2000; - */ - const uint32_t scalar = _FREQ_DIV256/500; - if(us > 1+UINT32_MAX/scalar) - return 0; /* Multiplying us by _FREQ_DIV256/500 will overflow uint32_t */ - - uint32_t counts = (us*scalar)/2000; - if(counts > TIMER_MAX) - return 0; /* counts value too high for timer type */ - return counts; - } -} - -uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div); - -INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div) -{ - if(__builtin_constant_p(frc) && __builtin_constant_p(us) && __builtin_constant_p(div)) - return _timer_time_to_count_impl(frc, us, div); - else - return _timer_time_to_count_runtime(frc, us, div); -} - -/* timer_set_frequency implementation - inline if all args are constant, call normally otherwise */ - -INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq) -{ - uint32_t counts = 0; - timer_clkdiv_t div = timer_freq_to_div(freq); - - counts = timer_freq_to_count(frc, freq, div); - if(counts == 0) - { - printf("ABORT: No counter for timer %u frequency %u\r\n", frc, freq); - abort(); - } - - timer_set_divider(frc, div); - if(frc == FRC1) - { - timer_set_load(frc, counts); - timer_set_reload(frc, true); - } - else /* FRC2 */ - { - /* assume that if this overflows it'll wrap, so we'll get desired behaviour */ - TIMER(1).ALARM = counts + TIMER(1).COUNT; - } - return true; -} - -bool _timer_set_frequency_runtime(const timer_frc_t frc, uint32_t freq); - -INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq) -{ - if(__builtin_constant_p(frc) && __builtin_constant_p(freq)) - return _timer_set_frequency_impl(frc, freq); - else - return _timer_set_frequency_runtime(frc, freq); -} - -/* timer_set_timeout implementation - inline if all args are constant, call normally otherwise */ - -INLINED bool _timer_set_timeout_impl(const timer_frc_t frc, uint32_t us) -{ - uint32_t counts = 0; - timer_clkdiv_t div = timer_time_to_div(us); - - counts = timer_time_to_count(frc, us, div); - if(counts == 0) - return false; /* can't set frequency */ - - timer_set_divider(frc, div); - if(frc == FRC1) - { - timer_set_load(frc, counts); - } - else /* FRC2 */ - { - TIMER(1).ALARM = counts + TIMER(1).COUNT; - } - - return true; -} - -bool _timer_set_timeout_runtime(const timer_frc_t frc, uint32_t us); - -INLINED bool timer_set_timeout(const timer_frc_t frc, uint32_t us) -{ - if(__builtin_constant_p(frc) && __builtin_constant_p(us)) - return _timer_set_timeout_impl(frc, us); - else - return _timer_set_timeout_runtime(frc, us); -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/examples/terminal/terminal.c b/examples/terminal/terminal.c index 6da2590..83ea734 100644 --- a/examples/terminal/terminal.c +++ b/examples/terminal/terminal.c @@ -7,10 +7,12 @@ #include #include +#include #include #include #include #include +#include #include "FreeRTOS.h" #include "task.h" diff --git a/extras/stdin_uart_interrupt/stdin_uart_interrupt.c b/extras/stdin_uart_interrupt/stdin_uart_interrupt.c index 38d2264..e85c913 100644 --- a/extras/stdin_uart_interrupt/stdin_uart_interrupt.c +++ b/extras/stdin_uart_interrupt/stdin_uart_interrupt.c @@ -25,6 +25,7 @@ #include #include #include +#include #if (configUSE_COUNTING_SEMAPHORES == 0) #error "You need to define configUSE_COUNTING_SEMAPHORES in a local FreeRTOSConfig.h, see examples/terminal/FreeRTOSConfig.h"