From ec55b82acfa9c005e35d274f26050a54d50bd734 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 13 Sep 2017 18:31:38 -0500 Subject: [PATCH 1/4] Update ticker to map closely to hardware Allow tickers to specify their native frequency and number of bits. This allows the conversion to happen in common code rather than in each vendor's implementation. --- TESTS/mbed_hal/ticker/main.cpp | 242 ++++++++++++++++++++++++++++++++- hal/lp_ticker_api.h | 5 + hal/mbed_lp_ticker_api.c | 1 + hal/mbed_ticker_api.c | 123 +++++++++++++---- hal/mbed_us_ticker_api.c | 1 + hal/ticker_api.h | 33 ++++- hal/us_ticker_api.h | 5 + 7 files changed, 375 insertions(+), 35 deletions(-) diff --git a/TESTS/mbed_hal/ticker/main.cpp b/TESTS/mbed_hal/ticker/main.cpp index 334a1ae404e..acb69474b1d 100644 --- a/TESTS/mbed_hal/ticker/main.cpp +++ b/TESTS/mbed_hal/ticker/main.cpp @@ -29,7 +29,8 @@ using namespace utest::v1; #define MBED_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) -#define TIMESTAMP_MAX_DELTA MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA +#define TIMESTAMP_MAX_DELTA_BITS(bits) ((uint64_t)(0x7 << ((bits) - 4))) +#define TIMESTAMP_MAX_DELTA TIMESTAMP_MAX_DELTA_BITS(32) struct ticker_interface_stub_t { ticker_interface_t interface; @@ -43,9 +44,11 @@ struct ticker_interface_stub_t { unsigned int clear_interrupt_call; unsigned int set_interrupt_call; unsigned int fire_interrupt_call; + unsigned int get_info_call; }; static ticker_interface_stub_t interface_stub = { 0 }; +static ticker_info_t interface_info_stub = { 0 }; static void ticker_interface_stub_init() { @@ -81,6 +84,12 @@ static void ticker_interface_stub_fire_interrupt() ++interface_stub.fire_interrupt_call; } +static const ticker_info_t *ticker_interface_stub_get_info() +{ + ++interface_stub.get_info_call; + return &interface_info_stub; +} + static void reset_ticker_interface_stub() { interface_stub.interface.init = ticker_interface_stub_init; @@ -91,6 +100,7 @@ static void reset_ticker_interface_stub() ticker_interface_stub_clear_interrupt; interface_stub.interface.set_interrupt =ticker_interface_stub_set_interrupt; interface_stub.interface.fire_interrupt = ticker_interface_stub_fire_interrupt; + interface_stub.interface.get_info = ticker_interface_stub_get_info; interface_stub.initialized = false; interface_stub.interrupt_flag = false; interface_stub.timestamp = 0; @@ -101,6 +111,9 @@ static void reset_ticker_interface_stub() interface_stub.clear_interrupt_call = 0; interface_stub.set_interrupt_call = 0; interface_stub.fire_interrupt_call = 0; + + interface_info_stub.frequency = 1000000; + interface_info_stub.bits = 32; } // stub of the event queue @@ -115,6 +128,12 @@ 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; } @@ -131,6 +150,34 @@ static void reset_ticker_stub() reset_ticker_interface_stub(); } +const uint32_t test_frequencies[] = { + 1, + 32768, // 2^15 + 1000000, + 0xFFFFFFFF // 2^32 - 1 +}; + +const uint32_t test_bitwidths[] = { + 32, + 31, + 16, + 8 +}; + +template < void (F)(uint32_t a, uint32_t b)> +static void test_over_frequency_and_width(void) +{ + for (unsigned int i = 0; i < MBED_ARRAY_SIZE(test_frequencies); i++) { + for (unsigned int j = 0; j < MBED_ARRAY_SIZE(test_bitwidths); j++) { + reset_ticker_stub(); + interface_info_stub.frequency = test_frequencies[i]; + interface_info_stub.bits = test_bitwidths[j]; + + F(test_frequencies[i], test_bitwidths[j]); + } + } +} + static utest::v1::status_t case_setup_handler( const Case *const source, const size_t index_of_case ) { @@ -175,8 +222,7 @@ static utest::v1::status_t greentea_failure_handler( * Then: * - The ticker interface should be initialized * - The queue handler should be set to the handler provided in parameter - * - The internal ticker timestamp should be synced with the counter in the - * interface counter. + * - The internal ticker timestamp should be zero * - interrupt should be scheduled in current timestamp + * TIMESTAMP_MAX_DELTA * - The queue should not contains any event @@ -192,7 +238,7 @@ static void test_ticker_initialization() TEST_ASSERT_TRUE(interface_stub.initialized); TEST_ASSERT_EQUAL_PTR(dummy_handler, queue_stub.event_handler); - TEST_ASSERT_EQUAL_UINT64(interface_stub.timestamp, queue_stub.present_time); + TEST_ASSERT_EQUAL_UINT64(0, queue_stub.present_time); TEST_ASSERT_EQUAL(1, interface_stub.set_interrupt_call); TEST_ASSERT_EQUAL_UINT32( interface_stub.timestamp + TIMESTAMP_MAX_DELTA, @@ -347,7 +393,7 @@ static void test_legacy_insert_event_outside_overflow_range() // test the beginning of the range ticker_event_t first_event = { 0 }; - const timestamp_t timestamp_first_event = interface_stub.timestamp + 1; + const timestamp_t timestamp_first_event = interface_stub.timestamp + 1; const uint32_t id_first_event = 0xAAAAAAAA; ticker_insert_event( @@ -820,6 +866,7 @@ static void test_insert_event_us_outside_overflow_range() ticker_set_handler(&ticker_stub, NULL); interface_stub.set_interrupt_call = 0; interface_stub.timestamp = 0xAAAAAAAA; + queue_stub.tick_last_read = interface_stub.timestamp; queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp; // test the end of the range @@ -881,6 +928,7 @@ static void test_insert_event_us_in_overflow_range() ticker_set_handler(&ticker_stub, NULL); interface_stub.set_interrupt_call = 0; interface_stub.timestamp = 0xAAAAAAAA; + queue_stub.tick_last_read = interface_stub.timestamp; queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp; // test the end of the range @@ -944,6 +992,7 @@ static void test_insert_event_us_underflow() interface_stub.set_interrupt_call = 0; interface_stub.timestamp = 0xAAAAAAAA; + queue_stub.tick_last_read = interface_stub.timestamp; queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp; // test the end of the range @@ -979,6 +1028,7 @@ static void test_insert_event_us_head() ticker_set_handler(&ticker_stub, NULL); interface_stub.set_interrupt_call = 0; interface_stub.timestamp = 0xAAAAAAAA; + queue_stub.tick_last_read = interface_stub.timestamp; queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp; const us_timestamp_t timestamps[] = { @@ -2003,6 +2053,8 @@ static uint32_t ticker_interface_stub_read_interrupt_time() */ static void test_set_interrupt_past_time() { + ticker_set_handler(&ticker_stub, NULL); + interface_stub.set_interrupt_call = 0; interface_stub.fire_interrupt_call = 0; interface_stub.timestamp = 0xFF; @@ -2023,6 +2075,8 @@ static void test_set_interrupt_past_time() */ static void test_set_interrupt_past_time_with_delay() { + ticker_set_handler(&ticker_stub, NULL); + interface_stub.set_interrupt_call = 0; interface_stub.fire_interrupt_call = 0; interface_stub.timestamp = 0xFF; @@ -2038,6 +2092,168 @@ static void test_set_interrupt_past_time_with_delay() TEST_ASSERT_EQUAL(1, interface_stub.fire_interrupt_call); } +/** + * Convert ticks at a given frequency to time in microseconds + * + * Assert if there is a 64-bit overflow + */ +static uint64_t convert_to_us(uint64_t ticks, uint32_t frequency) +{ + uint64_t scaled_ticks = ticks * 1000000; + // Assert that there was not an overflow + TEST_ASSERT_EQUAL(ticks, scaled_ticks / 1000000); + return scaled_ticks / frequency; +} + +/** + * Given an uninitialized ticker instance and an interface of a + * certain frequency and bit width. + * Then the time returned the ticker should match the cumulative time. + */ +void test_frequencies_and_masks(uint32_t frequency, uint32_t bits) +{ + const uint32_t bitmask = ((uint64_t)1 << bits) - 1; + + ticker_set_handler(&ticker_stub, NULL); + uint64_t ticks = 0; + + // Single tick + ticks += 1; + interface_stub.timestamp = ticks & bitmask; + TEST_ASSERT_EQUAL_UINT32(convert_to_us(ticks, frequency), ticker_read(&ticker_stub)); + TEST_ASSERT_EQUAL_UINT64(convert_to_us(ticks, frequency), ticker_read_us(&ticker_stub)); + + // Run until the loop before 64-bit overflow (worst case with frequency=1hz, bits=32) + for (unsigned int k = 0; k < 4294; k++) { + + // Largest value possible tick + ticks += ((uint64_t)1 << bits) - 1; + interface_stub.timestamp = ticks & bitmask; + TEST_ASSERT_EQUAL_UINT32(convert_to_us(ticks, frequency), ticker_read(&ticker_stub)); + TEST_ASSERT_EQUAL_UINT64(convert_to_us(ticks, frequency), ticker_read_us(&ticker_stub)); + } +} + +/** + * Given an uninitialized ticker_data instance. + * When the ticker is initialized + * Then: + * - The internal ticker timestamp should be zero + * - interrupt should be scheduled in current (timestamp + + * TIMESTAMP_MAX_DELTA_BITS(bitwidth)) % modval + * - The queue should not contains any event + */ +static void test_ticker_max_value() +{ + for (int bitwidth = 8; bitwidth <= 32; bitwidth++) { + const uint64_t modval = 1ULL << bitwidth; + + // setup of the stub + reset_ticker_stub(); + interface_info_stub.bits = bitwidth; + interface_stub.timestamp = 0xBA; + + ticker_set_handler(&ticker_stub, NULL); + + TEST_ASSERT_EQUAL_UINT64(0, queue_stub.present_time); + TEST_ASSERT_EQUAL(1, interface_stub.set_interrupt_call); + TEST_ASSERT_EQUAL_UINT32( + (interface_stub.timestamp + TIMESTAMP_MAX_DELTA_BITS(bitwidth)) % modval, + interface_stub.interrupt_timestamp + ); + TEST_ASSERT_EQUAL_PTR(NULL, queue_stub.head); + TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call); + } +} + +/** + * Check that _ticker_match_interval_passed correctly detects matches + * + * Brute force test that _ticker_match_interval_passed returns the correct match value + * for all cominations of values within a small range. + */ +static void test_match_interval_passed() +{ + + for (int modval = 1; modval <= 5; modval++) { + for (int prev = 0; prev < modval; prev++) { + for (int cur = 0; cur < modval; cur++) { + for (int match = 0; match < modval; match++) { + uint32_t delta = (cur - prev) % modval; + uint32_t delta_to_match = (match - prev) % modval; + bool match_expected = false; + if (delta_to_match) { + match_expected = delta >= delta_to_match; + } + + // Sanity checks + if (prev == cur) { + // No time has passed + TEST_ASSERT_EQUAL(false, match_expected); + } else if (match == prev) { + // Match can't occur without an overflow occurring + TEST_ASSERT_EQUAL(false, match_expected); + } else if (cur == match) { + // All other cases where cur == match a match should be expected + TEST_ASSERT_EQUAL(true, match_expected); + } + + // Actual test + TEST_ASSERT_EQUAL(match_expected, _ticker_match_interval_passed(prev, cur, match)); + } + } + } + } +} + +typedef struct { + timestamp_t prev; + timestamp_t cur; + timestamp_t match; + bool result; +} match_interval_entry_t; + +/** + * Check that _ticker_match_interval_passed correctly detects matches + * + * Use a table of pre-computed values to check that _ticker_match_interval_passed + * returns the correct match value. + */ +static void test_match_interval_passed_table() +{ + static const match_interval_entry_t test_values[] = { + /* prev, cur, match, result */ + {0x00000000, 0x00000000, 0x00000000, false}, + {0x00000000, 0x00000000, 0xffffffff, false}, + {0x00000000, 0x00000000, 0x00000001, false}, + {0x00000000, 0xffffffff, 0x00000000, false}, + {0x00000000, 0x00000001, 0x00000000, false}, + {0xffffffff, 0x00000000, 0x00000000, true}, + {0x00000001, 0x00000000, 0x00000000, true}, + {0x00005555, 0x00005555, 0x00005555, false}, + {0x00005555, 0x00005555, 0x00005554, false}, + {0x00005555, 0x00005555, 0x00005556, false}, + {0x00005555, 0x00005554, 0x00005555, false}, + {0x00005555, 0x00005556, 0x00005555, false}, + {0x00005554, 0x00005555, 0x00005555, true}, + {0x00005556, 0x00005555, 0x00005555, true}, + {0xffffffff, 0xffffffff, 0xffffffff, false}, + {0xffffffff, 0xffffffff, 0xfffffffe, false}, + {0xffffffff, 0xffffffff, 0x00000000, false}, + {0xffffffff, 0xfffffffe, 0xffffffff, false}, + {0xffffffff, 0x00000000, 0xffffffff, false}, + {0xfffffffe, 0xffffffff, 0xffffffff, true}, + {0x00000000, 0xffffffff, 0xffffffff, true}, + }; + for (int i = 0; i < MBED_ARRAY_SIZE(test_values); i++) { + const uint32_t prev = test_values[i].prev; + const uint32_t cur = test_values[i].cur; + const uint32_t match = test_values[i].match; + const uint32_t result = test_values[i].result; + TEST_ASSERT_EQUAL(result, _ticker_match_interval_passed(prev, cur, match)); + } +} + static const case_t cases[] = { MAKE_TEST_CASE("ticker initialization", test_ticker_initialization), MAKE_TEST_CASE( @@ -2130,6 +2346,22 @@ static const case_t cases[] = { MAKE_TEST_CASE( "test_set_interrupt_past_time_with_delay", test_set_interrupt_past_time_with_delay + ), + MAKE_TEST_CASE( + "test_frequencies_and_masks", + test_over_frequency_and_width + ), + MAKE_TEST_CASE( + "test_ticker_max_value", + test_ticker_max_value + ), + MAKE_TEST_CASE( + "test_match_interval_passed", + test_match_interval_passed + ), + MAKE_TEST_CASE( + "test_match_interval_passed_table", + test_match_interval_passed_table ) }; diff --git a/hal/lp_ticker_api.h b/hal/lp_ticker_api.h index 73d8eb5a07d..39d396e5b93 100644 --- a/hal/lp_ticker_api.h +++ b/hal/lp_ticker_api.h @@ -80,6 +80,11 @@ void lp_ticker_clear_interrupt(void); */ void lp_ticker_fire_interrupt(void); +/** Get frequency and counter bits of this ticker. + * + */ +const ticker_info_t* lp_ticker_get_info(void); + /**@}*/ #ifdef __cplusplus diff --git a/hal/mbed_lp_ticker_api.c b/hal/mbed_lp_ticker_api.c index 501f7f9487f..0809f6fa5f9 100644 --- a/hal/mbed_lp_ticker_api.c +++ b/hal/mbed_lp_ticker_api.c @@ -26,6 +26,7 @@ static const ticker_interface_t lp_interface = { .clear_interrupt = lp_ticker_clear_interrupt, .set_interrupt = lp_ticker_set_interrupt, .fire_interrupt = lp_ticker_fire_interrupt, + .get_info = lp_ticker_get_info, }; static const ticker_data_t lp_data = { diff --git a/hal/mbed_ticker_api.c b/hal/mbed_ticker_api.c index 18ee36d9862..e6b4d712ad9 100644 --- a/hal/mbed_ticker_api.c +++ b/hal/mbed_ticker_api.c @@ -17,6 +17,7 @@ #include #include "hal/ticker_api.h" #include "platform/mbed_critical.h" +#include "mbed_assert.h" static void schedule_interrupt(const ticker_data_t *const ticker); static void update_present_time(const ticker_data_t *const ticker); @@ -33,9 +34,31 @@ static void initialize(const ticker_data_t *ticker) } ticker->interface->init(); - + + const ticker_info_t *info = ticker->interface->get_info(); + uint32_t frequency = info->frequency; + if (info->frequency == 0) { + MBED_ASSERT(0); + frequency = 1000000; + } + + uint32_t bits = info->bits; + if ((info->bits > 32) || (info->bits < 4)) { + MBED_ASSERT(0); + bits = 32; + } + uint32_t max_delta = 0x7 << (bits - 4); // 7/16th + uint64_t max_delta_us = + ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency; + 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->bitmask = ((uint64_t)1 << bits) - 1; + ticker->queue->max_delta = max_delta; + ticker->queue->max_delta_us = max_delta_us; ticker->queue->present_time = 0; ticker->queue->initialized = true; @@ -86,53 +109,103 @@ static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestam * Update the present timestamp value of a ticker. */ static void update_present_time(const ticker_data_t *const ticker) -{ - ticker->queue->present_time = convert_timestamp( - ticker->queue->present_time, - ticker->interface->read() - ); +{ + + ticker_event_queue_t *queue = ticker->queue; + uint32_t ticker_time = ticker->interface->read(); + if (ticker_time == ticker->queue->tick_last_read) { + // No work to do + return; + } + + uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask; + queue->tick_last_read = ticker_time; + + uint64_t us_x_ticks = elapsed_ticks * 1000000; + uint64_t 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; + } + + // Update current time + queue->present_time += elapsed_us; +} + +/** + * Given the absolute timestamp compute the hal tick timestamp. + */ +static timestamp_t compute_tick(const ticker_data_t *const ticker, us_timestamp_t timestamp) +{ + 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) { + // Checking max_delta_us ensures the operation will not overflow + delta = delta_us * queue->frequency / 1000000; + if (delta > ticker->queue->max_delta) { + delta = ticker->queue->max_delta; + } + } + return (queue->tick_last_read + delta) & queue->bitmask; +} + +/** + * Return 1 if the tick has incremented to or past match_tick, otherwise 0. + */ +int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick) +{ + if (match_tick > prev_tick) { + return (cur_tick >= match_tick) || (cur_tick < prev_tick); + } else { + return (cur_tick < prev_tick) && (cur_tick >= match_tick); + } } /** * Compute the time when the interrupt has to be triggered and schedule it. * * If there is no event in the queue or the next event to execute is in more - * than MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us from now then the ticker - * irq will be scheduled in MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us. - * Otherwise the irq will be scheduled to happen when the running counter reach - * the timestamp of the first event in the queue. + * than ticker.queue.max_delta ticks from now then the ticker irq will be + * scheduled in ticker.queue.max_delta ticks. Otherwise the irq will be + * scheduled to happen when the running counter reach the timestamp of the + * first event in the queue. * * @note If there is no event in the queue then the interrupt is scheduled to - * in MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA. This is necessary to keep track + * in ticker.queue.max_delta. This is necessary to keep track * of the timer overflow. */ static void schedule_interrupt(const ticker_data_t *const ticker) { + ticker_event_queue_t *queue = ticker->queue; update_present_time(ticker); - uint32_t relative_timeout = MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA; if (ticker->queue->head) { us_timestamp_t present = ticker->queue->present_time; - us_timestamp_t next_event_timestamp = ticker->queue->head->timestamp; + us_timestamp_t match_time = ticker->queue->head->timestamp; // if the event at the head of the queue is in the past then schedule // it immediately. - if (next_event_timestamp <= present) { + if (match_time <= present) { ticker->interface->fire_interrupt(); return; - } else if ((next_event_timestamp - present) < MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA) { - relative_timeout = next_event_timestamp - present; } - } - us_timestamp_t new_match_time = ticker->queue->present_time + relative_timeout; - ticker->interface->set_interrupt(new_match_time); - // there could be a delay, reread the time, check if it was set in the past - // As result, if it is already in the past, we fire it immediately - update_present_time(ticker); - us_timestamp_t present = ticker->queue->present_time; - if (present >= new_match_time) { - ticker->interface->fire_interrupt(); + timestamp_t match_tick = compute_tick(ticker, match_time); + ticker->interface->set_interrupt(match_tick); + timestamp_t cur_tick = ticker->interface->read(); + + if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) { + ticker->interface->fire_interrupt(); + } + } else { + uint32_t match_tick = + (queue->tick_last_read + queue->max_delta) & queue->bitmask; + ticker->interface->set_interrupt(match_tick); } } diff --git a/hal/mbed_us_ticker_api.c b/hal/mbed_us_ticker_api.c index fb1663a5d38..17cf6f85c2b 100644 --- a/hal/mbed_us_ticker_api.c +++ b/hal/mbed_us_ticker_api.c @@ -24,6 +24,7 @@ static const ticker_interface_t us_interface = { .clear_interrupt = us_ticker_clear_interrupt, .set_interrupt = us_ticker_set_interrupt, .fire_interrupt = us_ticker_fire_interrupt, + .get_info = us_ticker_get_info, }; static const ticker_data_t us_data = { diff --git a/hal/ticker_api.h b/hal/ticker_api.h index fceba2db87e..334ca93aa87 100644 --- a/hal/ticker_api.h +++ b/hal/ticker_api.h @@ -23,11 +23,6 @@ #include #include "device.h" -/** - * Maximum delta (in us) between too interrupts. - */ -#define MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA 0x70000000ULL - /** * Legacy format representing a timestamp in us. * Given it is modeled as a 32 bit integer, this type can represent timestamp @@ -52,6 +47,14 @@ typedef struct ticker_event_s { typedef void (*ticker_event_handler)(uint32_t id); +/** Information about the ticker implementation + */ +typedef struct { + uint32_t frequency; /**< Frequency in Hz this ticker runs at */ + uint32_t bits; /**< Number of bits this ticker supports */ +} ticker_info_t; + + /** Ticker's interface structure - required API for a ticker */ typedef struct { @@ -61,6 +64,7 @@ typedef struct { void (*clear_interrupt)(void); /**< Clear interrupt function */ void (*set_interrupt)(timestamp_t timestamp); /**< Set interrupt function */ void (*fire_interrupt)(void); /**< Fire interrupt right-away */ + const ticker_info_t *(*get_info)(void); /**< Return info about this ticker's implementation */ } ticker_interface_t; /** Ticker's event queue structure @@ -68,6 +72,12 @@ typedef struct { 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 */ + 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 */ + uint64_t max_delta_us; /**< Largest delta in us that can be used when scheduling */ + uint32_t tick_last_read; /**< Last tick read */ + uint64_t tick_remainder; /**< Ticks that have not been added to base_time */ us_timestamp_t present_time; /**< Store the timestamp used for present time */ bool initialized; /**< Indicate if the instance is initialized */ } ticker_event_queue_t; @@ -170,6 +180,19 @@ us_timestamp_t ticker_read_us(const ticker_data_t *const ticker); */ int ticker_get_next_timestamp(const ticker_data_t *const ticker, timestamp_t *timestamp); +/* Private functions + * + * @cond PRIVATE + * + */ + +int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick); + +/* + * @endcond PRIVATE + * + */ + /**@}*/ #ifdef __cplusplus diff --git a/hal/us_ticker_api.h b/hal/us_ticker_api.h index b22fcc1d7bb..07288d2ee9e 100644 --- a/hal/us_ticker_api.h +++ b/hal/us_ticker_api.h @@ -78,6 +78,11 @@ void us_ticker_clear_interrupt(void); */ void us_ticker_fire_interrupt(void); +/** Get frequency and counter bits of this ticker. + * + */ +const ticker_info_t* us_ticker_get_info(void); + /**@}*/ #ifdef __cplusplus From 0d3714e9b715a32130a3f4d5f65aa2af6b1cbc74 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Mon, 18 Sep 2017 17:52:57 -0500 Subject: [PATCH 2/4] Optimize 1MHz and 32KHz use cases Add optimizations for the most common use cases of the us ticker, 1MHz, and the lp ticker, 32KHz. --- hal/mbed_ticker_api.c | 62 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/hal/mbed_ticker_api.c b/hal/mbed_ticker_api.c index e6b4d712ad9..114a5a46289 100644 --- a/hal/mbed_ticker_api.c +++ b/hal/mbed_ticker_api.c @@ -121,14 +121,35 @@ static void update_present_time(const ticker_data_t *const ticker) uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask; queue->tick_last_read = ticker_time; - uint64_t us_x_ticks = elapsed_ticks * 1000000; - uint64_t 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; + uint64_t elapsed_us; + if (1000000 == queue->frequency) { + // Optimized for 1MHz + + elapsed_us = elapsed_ticks; + } else if (32768 == queue->frequency) { + // Optimized for 32KHz + + uint64_t us_x_ticks = elapsed_ticks * 1000000; + elapsed_us = us_x_ticks >> 15; + + // Update remainder + queue->tick_remainder += us_x_ticks - (elapsed_us << 15); + 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 (queue->tick_remainder >= queue->frequency) { + elapsed_us += 1; + queue->tick_remainder -= queue->frequency; + } } // Update current time @@ -146,9 +167,28 @@ static timestamp_t compute_tick(const ticker_data_t *const ticker, us_timestamp_ timestamp_t delta = ticker->queue->max_delta; if (delta_us <= ticker->queue->max_delta_us) { // Checking max_delta_us ensures the operation will not overflow - delta = delta_us * queue->frequency / 1000000; - if (delta > ticker->queue->max_delta) { - delta = ticker->queue->max_delta; + + if (1000000 == queue->frequency) { + // Optimized for 1MHz + + delta = delta_us; + if (delta > ticker->queue->max_delta) { + delta = ticker->queue->max_delta; + } + } else if (32768 == queue->frequency) { + // Optimized for 32KHz + + delta = (delta_us << 15) / 1000000; + if (delta > ticker->queue->max_delta) { + delta = ticker->queue->max_delta; + } + } else { + // General case + + delta = delta_us * queue->frequency / 1000000; + if (delta > ticker->queue->max_delta) { + delta = ticker->queue->max_delta; + } } } return (queue->tick_last_read + delta) & queue->bitmask; From 6452821e2e83a6f55e3df7f05e41a622a2530de1 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 19 Sep 2017 13:58:34 -0500 Subject: [PATCH 3/4] Add default implementation of timer info Add weak implementations of *_ticker_get_info which returns 1MHz and a width of 32 bits. This allows the updated Ticker API to work with existing devices. Note - in the future when all targets have implemented *_ticker_get_info these weak functions will be removed. --- platform/mbed_retarget.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/platform/mbed_retarget.cpp b/platform/mbed_retarget.cpp index 757ac21c1f5..6c5f12031ec 100644 --- a/platform/mbed_retarget.cpp +++ b/platform/mbed_retarget.cpp @@ -27,6 +27,8 @@ #include "platform/mbed_stats.h" #include "platform/mbed_critical.h" #include "platform/PlatformMutex.h" +#include "us_ticker_api.h" +#include "lp_ticker_api.h" #include #include #include @@ -1063,3 +1065,23 @@ extern "C" clock_t clock() _mutex->unlock(); return t; } + +// temporary - Default to 1MHz at 32 bits if target does not have us_ticker_get_info +MBED_WEAK const ticker_info_t* us_ticker_get_info() +{ + static const ticker_info_t info = { + 1000000, + 32 + }; + return &info; +} + +// temporary - Default to 1MHz at 32 bits if target does not have lp_ticker_get_info +MBED_WEAK const ticker_info_t* lp_ticker_get_info() +{ + static const ticker_info_t info = { + 1000000, + 32 + }; + return &info; +} From 77dd4205095f09b54565bc50adacd9e8416c5d3f Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 3 Oct 2017 16:09:15 -0500 Subject: [PATCH 4/4] Increase ticker test time for slower devices Increase the hal ticker test time from 30s to 60s to prevent a timeout from occurring on slower devices, such as the nrf51. --- TESTS/mbed_hal/ticker/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TESTS/mbed_hal/ticker/main.cpp b/TESTS/mbed_hal/ticker/main.cpp index acb69474b1d..93ab4f4a94c 100644 --- a/TESTS/mbed_hal/ticker/main.cpp +++ b/TESTS/mbed_hal/ticker/main.cpp @@ -2367,7 +2367,7 @@ static const case_t cases[] = { static utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(30, "default_auto"); + GREENTEA_SETUP(60, "default_auto"); return verbose_test_setup_handler(number_of_cases); }