From c1dd8b9acd39479dd957f9c1bc9157b7993ed516 Mon Sep 17 00:00:00 2001 From: Antti Kauppila Date: Mon, 27 Apr 2020 13:52:04 +0300 Subject: [PATCH 1/4] redundant code combined --- hal/source/mbed_ticker_api.c | 115 +++++++++++++++-------------------- 1 file changed, 50 insertions(+), 65 deletions(-) diff --git a/hal/source/mbed_ticker_api.c b/hal/source/mbed_ticker_api.c index 5f363651bb..07905b8e88 100644 --- a/hal/source/mbed_ticker_api.c +++ b/hal/source/mbed_ticker_api.c @@ -162,25 +162,16 @@ static void update_present_time(const ticker_data_t *const ticker) // Optimized for 1MHz elapsed_us = elapsed_ticks; - } else if (0 != queue->frequency_shifts) { - // Optimized for frequencies divisible by 2 - uint64_t us_x_ticks = elapsed_ticks * 1000000; - elapsed_us = us_x_ticks >> queue->frequency_shifts; - - // Update remainder - queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts); - if (queue->tick_remainder >= queue->frequency) { - elapsed_us += 1; - queue->tick_remainder -= queue->frequency; - } } else { - // General case - uint64_t us_x_ticks = elapsed_ticks * 1000000; - elapsed_us = us_x_ticks / queue->frequency; - - // Update remainder - queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency; + if (0 != queue->frequency_shifts) { + // Optimized for frequencies divisible by 2 + elapsed_us = us_x_ticks >> queue->frequency_shifts; + queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts); + } else { + elapsed_us = us_x_ticks / queue->frequency; + queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency; + } if (queue->tick_remainder >= queue->frequency) { elapsed_us += 1; queue->tick_remainder -= queue->frequency; @@ -205,30 +196,57 @@ static timestamp_t compute_tick_round_up(const ticker_data_t *const ticker, us_t if (1000000 == queue->frequency) { // Optimized for 1MHz - delta = delta_us; - if (delta > ticker->queue->max_delta) { - delta = ticker->queue->max_delta; - } } else if (0 != queue->frequency_shifts) { // Optimized frequencies divisible by 2 - delta = ((delta_us << ticker->queue->frequency_shifts) + 1000000 - 1) / 1000000; - if (delta > ticker->queue->max_delta) { - delta = ticker->queue->max_delta; - } } else { // General case - delta = (delta_us * queue->frequency + 1000000 - 1) / 1000000; - if (delta > ticker->queue->max_delta) { - delta = ticker->queue->max_delta; - } + } + if (delta > ticker->queue->max_delta) { + delta = ticker->queue->max_delta; } } return (queue->tick_last_read + delta) & queue->bitmask; } +//NOTE: Must be called from critical section! +static void insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id) +{ + // initialise our data + obj->timestamp = timestamp; + obj->id = id; + + /* Go through the list until we either reach the end, or find + an element this should come before (which is possibly the + head). */ + ticker_event_t *prev = NULL, *p = ticker->queue->head; + while (p != NULL) { + /* check if we come before p */ + if (timestamp < p->timestamp) { + break; + } + /* go to the next element */ + prev = p; + p = p->next; + } + + /* if we're at the end p will be NULL, which is correct */ + obj->next = p; + + /* if prev is NULL we're at the head */ + if (prev == NULL) { + ticker->queue->head = obj; + } else { + prev->next = obj; + } + + if (prev == NULL || timestamp <= ticker->queue->present_time) { + schedule_interrupt(ticker); + } +} + /** * Return 1 if the tick has incremented to or past match_tick, otherwise 0. */ @@ -345,6 +363,7 @@ void ticker_irq_handler(const ticker_data_t *const ticker) core_util_critical_section_exit(); } + void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id) { core_util_critical_section_enter(); @@ -356,11 +375,7 @@ void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp ); - // defer to ticker_insert_event_us - ticker_insert_event_us( - ticker, - obj, absolute_timestamp, id - ); + insert_event(ticker, obj, absolute_timestamp, id); core_util_critical_section_exit(); } @@ -372,37 +387,7 @@ void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *o // update the current timestamp update_present_time(ticker); - // initialise our data - obj->timestamp = timestamp; - obj->id = id; - - /* Go through the list until we either reach the end, or find - an element this should come before (which is possibly the - head). */ - ticker_event_t *prev = NULL, *p = ticker->queue->head; - while (p != NULL) { - /* check if we come before p */ - if (timestamp < p->timestamp) { - break; - } - /* go to the next element */ - prev = p; - p = p->next; - } - - /* if we're at the end p will be NULL, which is correct */ - obj->next = p; - - /* if prev is NULL we're at the head */ - if (prev == NULL) { - ticker->queue->head = obj; - } else { - prev->next = obj; - } - - if (prev == NULL || timestamp <= ticker->queue->present_time) { - schedule_interrupt(ticker); - } + insert_event(ticker, obj, timestamp, id); core_util_critical_section_exit(); } From 6423633122aa0df94fde41fcfd8bf609be354e98 Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Thu, 30 Apr 2020 10:51:27 +0300 Subject: [PATCH 2/4] Add documentation for LP_TICKER defines They're now potentially useful, so document them as per the US_TICKER defines. --- hal/include/hal/lp_ticker_api.h | 16 ++++++++++++++++ hal/include/hal/us_ticker_api.h | 2 +- hal/tests/TESTS/mbed_hal/lp_ticker/main.cpp | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hal/include/hal/lp_ticker_api.h b/hal/include/hal/lp_ticker_api.h index aecaf8ac4a..82a67b7dd1 100644 --- a/hal/include/hal/lp_ticker_api.h +++ b/hal/include/hal/lp_ticker_api.h @@ -49,6 +49,22 @@ extern "C" { * * @see hal_lp_ticker_tests * + * # Compile-time optimization macros + * + * To permit compile-time optimization, the following macros can be defined by a target's device.h: + * + * LP_TICKER_PERIOD_NUM, LP_TICKER_PERIOD_DEN: These denote the ratio (numerator, denominator) + * of the ticker period to a microsecond. For example, a 64kHz ticker would have NUM = 125, DEN = 8; + * a 4kHz ticker would have NUM = 250, DEN = 1; a 32.768kHz ticker would have NUM = 15625, DEN = 512. + * Both numerator and denominator must be 32 bits or less. They do not need to be fully simplified, + * so 32.768kHz could also be NUM = 1000000, DEN = 32768, but more simplification may be a minor + * speed optimisation, as can matching numerator or denominator with US_TICKER. + * + * LP_TICKER_MASK: The value mask for the ticker - eg 0x07FFFFFF for a 27-bit ticker. + * + * If any are defined, all 3 must be defined, and the macros are checked for consistency with + * lp_ticker_get_info by test ::lp_ticker_info_test. + * @{ */ diff --git a/hal/include/hal/us_ticker_api.h b/hal/include/hal/us_ticker_api.h index ecfa712bb9..f793e87858 100644 --- a/hal/include/hal/us_ticker_api.h +++ b/hal/include/hal/us_ticker_api.h @@ -50,7 +50,7 @@ extern "C" { * US_TICKER_PERIOD_NUM, US_TICKER_PERIOD_DEN: These denote the ratio (numerator, denominator) * of the ticker period to a microsecond. For example, an 8MHz ticker would have NUM = 1, DEN = 8; * a 1MHz ticker would have NUM = 1, DEN = 1; a 250kHz ticker would have NUM = 4, DEN = 1. - * Both numerator and denominator must be 16 bits or less. + * Both numerator and denominator must be 16 bits or less, but need not be fully simplified. * * US_TICKER_MASK: The value mask for the ticker - eg 0x07FFFFFF for a 27-bit ticker. * diff --git a/hal/tests/TESTS/mbed_hal/lp_ticker/main.cpp b/hal/tests/TESTS/mbed_hal/lp_ticker/main.cpp index 7331edf9e9..43f8e39cdd 100644 --- a/hal/tests/TESTS/mbed_hal/lp_ticker/main.cpp +++ b/hal/tests/TESTS/mbed_hal/lp_ticker/main.cpp @@ -110,6 +110,12 @@ void lp_ticker_info_test() TEST_ASSERT(p_ticker_info->frequency >= 4000); TEST_ASSERT(p_ticker_info->frequency <= 64000); TEST_ASSERT(p_ticker_info->bits >= 12); + +#ifdef LP_TICKER_PERIOD_NUM + TEST_ASSERT_UINT32_WITHIN(1, 1000000 * LP_TICKER_PERIOD_DEN / LP_TICKER_PERIOD_NUM, p_ticker_info->frequency); + TEST_ASSERT_EQUAL_UINT32(LP_TICKER_MASK, ((uint64_t)1 << p_ticker_info->bits) - 1); +#endif + } #if DEVICE_SLEEP From 65bc41a96b9cbacfbb3a772127859aa900bd8743 Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Thu, 30 Apr 2020 11:00:54 +0300 Subject: [PATCH 3/4] Optimise mbed_ticker_api.c The generic code in mbed_ticker_api.c uses run-time polymorphism to handle different tickers, and has generic run-time calculations for different ticker widths and frequencies, with a single special-case for 1MHz. Extend the run-time special casing to handle any conversion cases where either the multiply or divide can be done as a shift. This is a speed optimisation for certain platforms. Add a new option `target.custom-tickers`. If turned off, it promises that only USTICKER and LPTICKER devices will be used. This then permits elimination and/or simplification of runtime calculations, saving size and speed. If either both USTICKER and LPTICKER have the same width, or same period numerator or denominator, or only one of them exists, then operations can be hard-coded. This is a significant ROM space saving, and a minor speed and RAM saving. We get to optimise all the calculations, but the run-time polymorphism is retained even if there is only one ticker, as it doesn't significantly affect code size versus direct calls, and the existence of lp_ticker_wrapper and various us_ticker optimisations requires it, even if only LPTICKER is available. --- hal/include/hal/ticker_api.h | 57 ++++++- hal/source/mbed_ticker_api.c | 292 +++++++++++++++++++++++++++-------- targets/targets.json | 4 + 3 files changed, 283 insertions(+), 70 deletions(-) diff --git a/hal/include/hal/ticker_api.h b/hal/include/hal/ticker_api.h index 28017feea1..ade060fb9c 100644 --- a/hal/include/hal/ticker_api.h +++ b/hal/include/hal/ticker_api.h @@ -70,22 +70,73 @@ typedef struct { bool runs_in_deep_sleep; /**< Whether ticker operates in deep sleep */ } ticker_interface_t; +/* Optimizations to avoid run-time computation if custom ticker support is disabled and + * there is exactly one of USTICKER or LPTICKER available, or if they have the same + * parameter value(s). + */ +#define MBED_TICKER_JUST_US (!MBED_CONF_TARGET_CUSTOM_TICKERS && DEVICE_USTICKER && !DEVICE_LPTICKER) +#define MBED_TICKER_JUST_LP (!MBED_CONF_TARGET_CUSTOM_TICKERS && DEVICE_LPTICKER && !DEVICE_USTICKER) + +#if (MBED_TICKER_JUST_US && defined US_TICKER_PERIOD_NUM) || \ + (!MBED_CONF_TARGET_CUSTOM_TICKERS && defined US_TICKER_PERIOD_NUM && defined LP_TICKER_PERIOD_NUM && \ + US_TICKER_PERIOD_NUM == LP_TICKER_PERIOD_NUM) +#define MBED_TICKER_CONSTANT_PERIOD_NUM US_TICKER_PERIOD_NUM +#elif MBED_TICKER_JUST_LP && defined LP_TICKER_PERIOD_NUM +#define MBED_TICKER_CONSTANT_PERIOD_NUM LP_TICKER_PERIOD_NUM +#endif + +#if (MBED_TICKER_JUST_US && defined US_TICKER_PERIOD_DEN) || \ + (!MBED_CONF_TARGET_CUSTOM_TICKERS && defined US_TICKER_PERIOD_DEN && defined LP_TICKER_PERIOD_DEN && \ + US_TICKER_PERIOD_DEN == LP_TICKER_PERIOD_DEN) +#define MBED_TICKER_CONSTANT_PERIOD_DEN US_TICKER_PERIOD_DEN +#elif MBED_TICKER_JUST_LP && defined LP_TICKER_PERIOD_DEN +#define MBED_TICKER_CONSTANT_PERIOD_DEN LP_TICKER_PERIOD_DEN +#endif + +#if defined MBED_TICKER_CONSTANT_PERIOD_NUM && defined MBED_TICKER_CONSTANT_PERIOD_DEN +#define MBED_TICKER_CONSTANT_PERIOD +#endif + +#if (MBED_TICKER_JUST_US && defined US_TICKER_MASK) || \ + (!MBED_CONF_TARGET_CUSTOM_TICKERS && defined US_TICKER_MASK && defined LP_TICKER_MASK && \ + US_TICKER_MASK == LP_TICKER_MASK) +#define MBED_TICKER_CONSTANT_MASK US_TICKER_MASK +#elif MBED_TICKER_JUST_LP && defined LP_TICKER_MASK +#define MBED_TICKER_CONSTANT_MASK LP_TICKER_MASK +#endif + /** Ticker's event queue structure */ typedef struct { ticker_event_handler event_handler; /**< Event handler */ ticker_event_t *head; /**< A pointer to head */ - uint32_t frequency; /**< Frequency of the timer in Hz */ +#ifndef MBED_TICKER_CONSTANT_PERIOD_NUM + uint32_t period_num; /**< Ratio of period to 1us, numerator */ +#endif +#ifndef MBED_TICKER_CONSTANT_PERIOD_DEN + uint32_t period_den; /**< Ratio of period to 1us, denominator */ +#endif +#ifndef MBED_TICKER_CONSTANT_MASK uint32_t bitmask; /**< Mask to be applied to time values read */ uint32_t max_delta; /**< Largest delta in ticks that can be used when scheduling */ +#endif +#if !(defined MBED_TICKER_CONSTANT_PERIOD && defined MBED_TICKER_CONSTANT_MASK) uint64_t max_delta_us; /**< Largest delta in us that can be used when scheduling */ +#endif uint32_t tick_last_read; /**< Last tick read */ - uint64_t tick_remainder; /**< Ticks that have not been added to base_time */ +#if MBED_TICKER_CONSTANT_PERIOD_DEN != 1 + uint32_t tick_remainder; /**< Ticks that have not been added to base_time */ +#endif us_timestamp_t present_time; /**< Store the timestamp used for present time */ bool initialized; /**< Indicate if the instance is initialized */ bool dispatching; /**< The function ticker_irq_handler is dispatching */ bool suspended; /**< Indicate if the instance is suspended */ - uint8_t frequency_shifts; /**< If frequency is a value of 2^n, this is n, otherwise 0 */ +#ifndef MBED_TICKER_CONSTANT_PERIOD_NUM + int8_t period_num_shifts; /**< If numerator is a value of 2^n, this is n, otherwise -1 */ +#endif +#ifndef MBED_TICKER_CONSTANT_PERIOD_DEN + int8_t period_den_shifts; /**< If denominator is a value of 2^n, this is n, otherwise -1 */ +#endif } ticker_event_queue_t; /** Ticker's data structure diff --git a/hal/source/mbed_ticker_api.c b/hal/source/mbed_ticker_api.c index 07905b8e88..736b90dc08 100644 --- a/hal/source/mbed_ticker_api.c +++ b/hal/source/mbed_ticker_api.c @@ -21,9 +21,104 @@ #include "platform/mbed_assert.h" #include "platform/mbed_error.h" +#if !MBED_CONF_TARGET_CUSTOM_TICKERS +#include "us_ticker_api.h" +#include "lp_ticker_api.h" +#endif + +// It's almost always worth avoiding division, but only worth avoiding +// multiplication on some cores. +#if defined(__CORTEX_M0) || defined(__CORTEX_M0PLUS) || defined(__CORTEX_M23) +#define SLOW_MULTIPLY 1 +#else +#define SLOW_MULTIPLY 0 +#endif + +// Do we compute ratio from frequency, or can we always get it from defines? +#if MBED_CONF_TARGET_CUSTOM_TICKERS || (DEVICE_USTICKER && !defined US_TICKER_PERIOD_NUM) || (DEVICE_LPTICKER && !defined LP_TICKER_PERIOD_NUM) +#define COMPUTE_RATIO_FROM_FREQUENCY 1 +#else +#define COMPUTE_RATIO_FROM_FREQUENCY 0 +#endif + static void schedule_interrupt(const ticker_data_t *const ticker); static void update_present_time(const ticker_data_t *const ticker); +/* Macros that either look up the info from mbed_ticker_queue_t, or give a constant. + * Some constants are defined during the definition of initialize, to keep the + * compile-time and run-time calculations alongside each other. + */ +#ifdef MBED_TICKER_CONSTANT_PERIOD_NUM +#define TICKER_PERIOD_NUM(queue) MBED_TICKER_CONSTANT_PERIOD_NUM +// don't bother doing computing shift - rely on the compiler being able convert "/ 2^k" to ">> k", +// except that it's useful to note shift 0 for numerator 1, as that's special-cased +#define TICKER_PERIOD_NUM_SHIFTS(queue) (MBED_TICKER_CONSTANT_PERIOD_NUM == 1 ? 0 : -1) +#else +#define TICKER_PERIOD_NUM(queue) ((queue)->period_num) +#define TICKER_PERIOD_NUM_SHIFTS(queue) ((queue)->period_num_shifts) +#endif + +#ifdef MBED_TICKER_CONSTANT_PERIOD_DEN +#define TICKER_PERIOD_DEN(queue) MBED_TICKER_CONSTANT_PERIOD_DEN +#define TICKER_PERIOD_DEN_SHIFTS(queue) (MBED_TICKER_CONSTANT_PERIOD_DEN == 1 ? 0 : -1) +#else +#define TICKER_PERIOD_DEN(queue) ((queue)->period_den) +#define TICKER_PERIOD_DEN_SHIFTS(queue) ((queue)->period_den_shifts) +#endif + +#if MBED_TICKER_CONSTANT_PERIOD_DEN == 1 +#define TICKER_TICK_REMAINDER(queue) 0 +#define TICKER_SET_TICK_REMAINDER(queue, rem) ((void)(rem)) +#else +#define TICKER_TICK_REMAINDER(queue) ((queue)->tick_remainder) +#define TICKER_SET_TICK_REMAINDER(queue, rem) ((queue)->tick_remainder = (rem)) +#endif + +// But the above can generate compiler warnings from `if (-1 >= 0) { x >>= -1; }` +#if defined ( __CC_ARM ) +#pragma diag_suppress 62 // Shift count is negative +#elif defined ( __GNUC__ ) +#pragma GCC diagnostic ignored "-Wshift-count-negative" +#elif defined (__ICCARM__) +#pragma diag_suppress=Pe062 // Shift count is negative +#endif + +#ifdef MBED_TICKER_CONSTANT_MASK +#define TICKER_BITMASK(queue) MBED_TICKER_CONSTANT_MASK +#define TICKER_MAX_DELTA(queue) CONSTANT_MAX_DELTA +#else +#define TICKER_BITMASK(queue) ((queue)->bitmask) +#define TICKER_MAX_DELTA(queue) ((queue)->max_delta) +#endif + +#if defined MBED_TICKER_CONSTANT_PERIOD && defined MBED_TICKER_CONSTANT_MASK +#define TICKER_MAX_DELTA_US(queue) CONSTANT_MAX_DELTA_US +#else +#define TICKER_MAX_DELTA_US(queue) ((queue)->max_delta_us) +#endif + +#if COMPUTE_RATIO_FROM_FREQUENCY +static inline uint32_t gcd(uint32_t a, uint32_t b) +{ + do { + uint32_t r = a % b; + a = b; + b = r; + } while (b != 0); + return a; +} + +static int exact_log2(uint32_t n) +{ + for (int i = 31; i > 0; --i) { + if ((1U << i) == n) { + return i; + } + } + return -1; +} +#endif + /* * Initialize a ticker instance. */ @@ -40,9 +135,36 @@ static void initialize(const ticker_data_t *ticker) ticker->interface->init(); +#if MBED_TRAP_ERRORS_ENABLED || COMPUTE_RATIO_FROM_FREQUENCY || !defined MBED_TICKER_CONSTANT_MASK const ticker_info_t *info = ticker->interface->get_info(); +#endif + +#if !MBED_CONF_TARGET_CUSTOM_TICKERS && MBED_TRAP_ERRORS_ENABLED + /* They must be passing us one of the well-known tickers. Check info + * rather than the data, to cope with the lp_ticker_wrapper. It doesn't count + * as a "custom ticker" for the purpose of this optimization. + * + * This check has the downside of potentially pulling in code for an unused ticker. + * This is minimized by using direct xxx_ticker_get_info() calls rather than + * `get_us_ticker_data()->interface->get_info()` which would pull in the entire system, + * and we wrap it in `MBED_TRAP_ERRORS_ENABLED`. + */ +#if DEVICE_USTICKER && DEVICE_LPTICKER + MBED_ASSERT(info == us_ticker_get_info() || info == lp_ticker_get_info()); +#elif DEVICE_USTICKER + MBED_ASSERT(info == us_ticker_get_info()); +#elif DEVICE_LPTICKER + MBED_ASSERT(info == lp_ticker_get_info()); +#else + MBED_ASSERT(false); +#endif +#endif + +#if COMPUTE_RATIO_FROM_FREQUENCY + // Will need to use frequency computation for at least some cases, so always do it + // to minimise code size. uint32_t frequency = info->frequency; - if (info->frequency == 0) { + if (frequency == 0) { #if MBED_TRAP_ERRORS_ENABLED MBED_ERROR( MBED_MAKE_ERROR( @@ -56,16 +178,27 @@ static void initialize(const ticker_data_t *ticker) #endif // MBED_TRAP_ERRORS_ENABLED } - uint8_t frequency_shifts = 0; - for (uint8_t i = 31; i > 0; --i) { - if ((1U << i) == frequency) { - frequency_shifts = i; - break; - } - } + const uint32_t period_gcd = gcd(frequency, 1000000); + ticker->queue->period_num = 1000000 / period_gcd; + ticker->queue->period_num_shifts = exact_log2(ticker->queue->period_num); + ticker->queue->period_den = frequency / period_gcd; + ticker->queue->period_den_shifts = exact_log2(ticker->queue->period_den); +#elif !defined MBED_TICKER_CONSTANT_PERIOD + // Have ratio defines, but need to figure out which one applies. + // `runs_in_deep_sleep` is a viable proxy. (We have asserts above that + // check that they're only passing usticker or lpticker). + const bool is_usticker = !DEVICE_LPTICKER || !ticker->interface->runs_in_deep_sleep; +#ifndef MBED_TICKER_CONSTANT_PERIOD_NUM + ticker->queue->period_num = is_usticker ? US_TICKER_PERIOD_NUM : LP_TICKER_PERIOD_NUM; +#endif +#ifndef MBED_TICKER_CONSTANT_PERIOD_DEN + ticker->queue->period_den = is_usticker ? US_TICKER_PERIOD_DEN : LP_TICKER_PERIOD_DEN; +#endif +#endif // COMPUTE_RATIO_FROM_FREQUENCY / MBED_TICKER_CONSTANT_PERIOD +#ifndef MBED_TICKER_CONSTANT_MASK uint32_t bits = info->bits; - if ((info->bits > 32) || (info->bits < 4)) { + if ((bits > 32) || (bits < 4)) { #if MBED_TRAP_ERRORS_ENABLED MBED_ERROR( MBED_MAKE_ERROR( @@ -78,19 +211,24 @@ static void initialize(const ticker_data_t *ticker) bits = 32; #endif // MBED_TRAP_ERRORS_ENABLED } - uint32_t max_delta = 0x7 << (bits - 4); // 7/16th - uint64_t max_delta_us = - ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency; + ticker->queue->bitmask = bits == 32 ? 0xFFFFFFFF : (1U << bits) - 1; + ticker->queue->max_delta = 7 << (bits - 4); // 7/16th +#else // MBED_TICKER_CONSTANT_MASK +#define CONSTANT_MAX_DELTA (7 * ((MBED_TICKER_CONSTANT_MASK >> 4) + 1)) // 7/16th +#endif // MBED_TICKER_CONSTANT_MASK + +#if !(defined MBED_TICKER_CONSTANT_PERIOD && defined MBED_TICKER_CONSTANT_MASK) + ticker->queue->max_delta_us = + ((uint64_t)TICKER_MAX_DELTA(ticker->queue) * TICKER_PERIOD_NUM(ticker->queue) + TICKER_PERIOD_DEN(ticker->queue) - 1) / TICKER_PERIOD_DEN(ticker->queue); +#else +#define CONSTANT_MAX_DELTA_US \ + (((uint64_t)CONSTANT_MAX_DELTA * MBED_TICKER_CONSTANT_PERIOD_NUM + MBED_TICKER_CONSTANT_PERIOD_DEN - 1) / MBED_TICKER_CONSTANT_PERIOD_DEN) +#endif ticker->queue->event_handler = NULL; ticker->queue->head = NULL; ticker->queue->tick_last_read = ticker->interface->read(); - ticker->queue->tick_remainder = 0; - ticker->queue->frequency = frequency; - ticker->queue->frequency_shifts = frequency_shifts; - ticker->queue->bitmask = ((uint64_t)1 << bits) - 1; - ticker->queue->max_delta = max_delta; - ticker->queue->max_delta_us = max_delta_us; + TICKER_SET_TICK_REMAINDER(ticker->queue, 0); ticker->queue->present_time = 0; ticker->queue->dispatching = false; ticker->queue->suspended = false; @@ -149,32 +287,36 @@ static void update_present_time(const ticker_data_t *const ticker) return; } uint32_t ticker_time = ticker->interface->read(); - if (ticker_time == ticker->queue->tick_last_read) { + if (ticker_time == queue->tick_last_read) { // No work to do return; } - uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask; + uint32_t elapsed_ticks = (ticker_time - queue->tick_last_read) & TICKER_BITMASK(queue); queue->tick_last_read = ticker_time; - uint64_t elapsed_us; - if (1000000 == queue->frequency) { - // Optimized for 1MHz - - elapsed_us = elapsed_ticks; + // Convert elapsed_ticks to elapsed_us as (elapsed_ticks * period_num / period_den) + // adding in any remainder from the last division + uint64_t scaled_ticks; + if (SLOW_MULTIPLY && TICKER_PERIOD_NUM_SHIFTS(queue) >= 0) { + scaled_ticks = (uint64_t) elapsed_ticks << TICKER_PERIOD_NUM_SHIFTS(queue); } else { - uint64_t us_x_ticks = elapsed_ticks * 1000000; - if (0 != queue->frequency_shifts) { - // Optimized for frequencies divisible by 2 - elapsed_us = us_x_ticks >> queue->frequency_shifts; - queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts); + scaled_ticks = (uint64_t) elapsed_ticks * TICKER_PERIOD_NUM(queue); + } + uint64_t elapsed_us; + if (TICKER_PERIOD_DEN_SHIFTS(queue) == 0) { + // Optimized for cases that don't need division + elapsed_us = scaled_ticks; + } else { + scaled_ticks += TICKER_TICK_REMAINDER(queue); + if (TICKER_PERIOD_DEN_SHIFTS(queue) >= 0) { + // Speed-optimised for shifts + elapsed_us = scaled_ticks >> TICKER_PERIOD_DEN_SHIFTS(queue); + TICKER_SET_TICK_REMAINDER(queue, scaled_ticks - (elapsed_us << TICKER_PERIOD_DEN_SHIFTS(queue))); } else { - elapsed_us = us_x_ticks / queue->frequency; - queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency; - } - if (queue->tick_remainder >= queue->frequency) { - elapsed_us += 1; - queue->tick_remainder -= queue->frequency; + // General case division + elapsed_us = scaled_ticks / TICKER_PERIOD_DEN(queue); + TICKER_SET_TICK_REMAINDER(queue, scaled_ticks - elapsed_us * TICKER_PERIOD_DEN(queue)); } } @@ -190,30 +332,44 @@ static timestamp_t compute_tick_round_up(const ticker_data_t *const ticker, us_t ticker_event_queue_t *queue = ticker->queue; us_timestamp_t delta_us = timestamp - queue->present_time; - timestamp_t delta = ticker->queue->max_delta; - if (delta_us <= ticker->queue->max_delta_us) { + timestamp_t delta = TICKER_MAX_DELTA(queue); + if (delta_us <= TICKER_MAX_DELTA_US(queue)) { // Checking max_delta_us ensures the operation will not overflow - if (1000000 == queue->frequency) { - // Optimized for 1MHz - delta = delta_us; - } else if (0 != queue->frequency_shifts) { - // Optimized frequencies divisible by 2 - delta = ((delta_us << ticker->queue->frequency_shifts) + 1000000 - 1) / 1000000; + // Convert delta_us to delta (ticks) as (delta_us * period_den / period_num) + // taking care to round up if num != 1 + uint64_t scaled_delta; + if (SLOW_MULTIPLY && TICKER_PERIOD_DEN_SHIFTS(queue) >= 0) { + // Optimized denominators divisible by 2 + scaled_delta = delta_us << TICKER_PERIOD_DEN_SHIFTS(queue); } else { // General case - delta = (delta_us * queue->frequency + 1000000 - 1) / 1000000; + scaled_delta = delta_us * TICKER_PERIOD_DEN(queue); } - if (delta > ticker->queue->max_delta) { - delta = ticker->queue->max_delta; + if (TICKER_PERIOD_NUM_SHIFTS(queue) == 0) { + delta = scaled_delta; + } else { + scaled_delta += TICKER_PERIOD_NUM(queue) - 1; + if (TICKER_PERIOD_NUM_SHIFTS(queue) >= 0) { + // Optimized numerators divisible by 2 + delta = scaled_delta >> TICKER_PERIOD_NUM_SHIFTS(queue); + } else { + // General case + delta = scaled_delta / TICKER_PERIOD_NUM(queue); + } + } + if (delta > TICKER_MAX_DELTA(queue)) { + delta = TICKER_MAX_DELTA(queue); } } - return (queue->tick_last_read + delta) & queue->bitmask; + return (queue->tick_last_read + delta) & TICKER_BITMASK(queue); } //NOTE: Must be called from critical section! static void insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id) { + ticker_event_queue_t *queue = ticker->queue; + // initialise our data obj->timestamp = timestamp; obj->id = id; @@ -221,7 +377,7 @@ static void insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, /* Go through the list until we either reach the end, or find an element this should come before (which is possibly the head). */ - ticker_event_t *prev = NULL, *p = ticker->queue->head; + ticker_event_t *prev = NULL, *p = queue->head; while (p != NULL) { /* check if we come before p */ if (timestamp < p->timestamp) { @@ -237,12 +393,12 @@ static void insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, /* if prev is NULL we're at the head */ if (prev == NULL) { - ticker->queue->head = obj; + queue->head = obj; } else { prev->next = obj; } - if (prev == NULL || timestamp <= ticker->queue->present_time) { + if (prev == NULL || timestamp <= queue->present_time) { schedule_interrupt(ticker); } } @@ -275,7 +431,7 @@ int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, t static void schedule_interrupt(const ticker_data_t *const ticker) { ticker_event_queue_t *queue = ticker->queue; - if (queue->suspended || ticker->queue->dispatching) { + if (queue->suspended || queue->dispatching) { // Don't schedule the next interrupt until dispatching is // finished. This prevents repeated calls to interface->set_interrupt return; @@ -283,9 +439,9 @@ static void schedule_interrupt(const ticker_data_t *const ticker) update_present_time(ticker); - if (ticker->queue->head) { - us_timestamp_t present = ticker->queue->present_time; - us_timestamp_t match_time = ticker->queue->head->timestamp; + if (queue->head) { + us_timestamp_t present = queue->present_time; + us_timestamp_t match_time = queue->head->timestamp; // if the event at the head of the queue is in the past then schedule // it immediately. @@ -308,7 +464,7 @@ static void schedule_interrupt(const ticker_data_t *const ticker) } } else { uint32_t match_tick = - (queue->tick_last_read + queue->max_delta) & queue->bitmask; + (queue->tick_last_read + TICKER_MAX_DELTA(queue)) & TICKER_BITMASK(queue); ticker->interface->set_interrupt(match_tick); } } @@ -325,30 +481,31 @@ void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler void ticker_irq_handler(const ticker_data_t *const ticker) { core_util_critical_section_enter(); + ticker_event_queue_t *queue = ticker->queue; ticker->interface->clear_interrupt(); - if (ticker->queue->suspended) { + if (queue->suspended) { core_util_critical_section_exit(); return; } /* Go through all the pending TimerEvents */ - ticker->queue->dispatching = true; + queue->dispatching = true; while (1) { - if (ticker->queue->head == NULL) { + if (queue->head == NULL) { break; } // update the current timestamp used by the queue update_present_time(ticker); - if (ticker->queue->head->timestamp <= ticker->queue->present_time) { + if (queue->head->timestamp <= queue->present_time) { // This event was in the past: // point to the following one and execute its handler ticker_event_t *p = ticker->queue->head; - ticker->queue->head = ticker->queue->head->next; - if (ticker->queue->event_handler != NULL) { - (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events + queue->head = queue->head->next; + if (queue->event_handler != NULL) { + (*queue->event_handler)(p->id); // NOTE: the handler can set new events } /* Note: We continue back to examining the head because calling the * event handler may have altered the chain of pending events. */ @@ -356,7 +513,7 @@ void ticker_irq_handler(const ticker_data_t *const ticker) break; } } - ticker->queue->dispatching = false; + queue->dispatching = false; schedule_interrupt(ticker); @@ -395,15 +552,16 @@ void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *o void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj) { core_util_critical_section_enter(); + ticker_event_queue_t *queue = ticker->queue; // remove this object from the list - if (ticker->queue->head == obj) { + if (queue->head == obj) { // first in the list, so just drop me - ticker->queue->head = obj->next; + queue->head = obj->next; schedule_interrupt(ticker); } else { // find the object before me, then drop me - ticker_event_t *p = ticker->queue->head; + ticker_event_t *p = queue->head; while (p != NULL) { if (p->next == obj) { p->next = obj->next; diff --git a/targets/targets.json b/targets/targets.json index 38c51e4952..157feadd23 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -77,6 +77,10 @@ "help": "Initialize the microsecond ticker at boot rather than on first use, and leave it initialized. This speeds up wait_us in particular.", "value": false }, + "custom-tickers": { + "help": "Support custom tickers in addition to USTICKER and LPTICKER. Turning this off can permit some space and speed optimisations, if characteristics of USTICKER and LPTICKER are known at compile time.", + "value": true + }, "xip-enable": { "help": "Enable Execute In Place (XIP) on this target. Value is only significant if the board has executable external storage such as QSPIF. If this is enabled, customize the linker file to choose what text segments are placed on external storage", "value": false From c887290db62823f48b0a3010620710ba8431ead0 Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Thu, 7 May 2020 13:40:21 +0300 Subject: [PATCH 4/4] Adjust ticker tests --- hal/tests/TESTS/mbed_hal/ticker/main.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/hal/tests/TESTS/mbed_hal/ticker/main.cpp b/hal/tests/TESTS/mbed_hal/ticker/main.cpp index a8db5007ff..778b4e83fb 100644 --- a/hal/tests/TESTS/mbed_hal/ticker/main.cpp +++ b/hal/tests/TESTS/mbed_hal/ticker/main.cpp @@ -127,16 +127,7 @@ static ticker_event_queue_t queue_stub = { static void reset_queue_stub() { - queue_stub.event_handler = NULL; - queue_stub.head = NULL, - queue_stub.tick_last_read = 0; - queue_stub.tick_remainder = 0; - queue_stub.frequency = 0; - queue_stub.bitmask = 0; - queue_stub.max_delta = 0; - queue_stub.max_delta_us = 0; - queue_stub.present_time = 0; - queue_stub.initialized = false; + queue_stub = ticker_event_queue_t{}; } // stub of the ticker