mirror of https://github.com/ARMmbed/mbed-os.git
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.pull/5028/head
parent
0fd13b5288
commit
ec55b82acf
|
@ -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<test_frequencies_and_masks>
|
||||
),
|
||||
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
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <stddef.h>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -23,11 +23,6 @@
|
|||
#include <stdbool.h>
|
||||
#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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue