mirror of https://github.com/ARMmbed/mbed-os.git
commit
4e0d07d50f
|
@ -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.
|
||||
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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,41 +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;
|
||||
} 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;
|
||||
}
|
||||
// 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 {
|
||||
// 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 (queue->tick_remainder >= queue->frequency) {
|
||||
elapsed_us += 1;
|
||||
queue->tick_remainder -= queue->frequency;
|
||||
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 {
|
||||
// General case division
|
||||
elapsed_us = scaled_ticks / TICKER_PERIOD_DEN(queue);
|
||||
TICKER_SET_TICK_REMAINDER(queue, scaled_ticks - elapsed_us * TICKER_PERIOD_DEN(queue));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,34 +332,75 @@ 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;
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
if (delta > ticker->queue->max_delta) {
|
||||
delta = ticker->queue->max_delta;
|
||||
scaled_delta = delta_us * TICKER_PERIOD_DEN(queue);
|
||||
}
|
||||
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) & 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;
|
||||
|
||||
/* 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 = 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) {
|
||||
queue->head = obj;
|
||||
} else {
|
||||
prev->next = obj;
|
||||
}
|
||||
|
||||
if (prev == NULL || timestamp <= queue->present_time) {
|
||||
schedule_interrupt(ticker);
|
||||
}
|
||||
return (queue->tick_last_read + delta) & queue->bitmask;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -257,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;
|
||||
|
@ -265,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.
|
||||
|
@ -290,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);
|
||||
}
|
||||
}
|
||||
|
@ -307,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. */
|
||||
|
@ -338,13 +513,14 @@ void ticker_irq_handler(const ticker_data_t *const ticker)
|
|||
break;
|
||||
}
|
||||
}
|
||||
ticker->queue->dispatching = false;
|
||||
queue->dispatching = false;
|
||||
|
||||
schedule_interrupt(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 +532,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 +544,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();
|
||||
}
|
||||
|
@ -410,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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue