Skip to content

Commit

Permalink
Fix rollover handling in ticker frequency test
Browse files Browse the repository at this point in the history
To handle timer rollovers the test tests-mbed_hal-common_tickers_freq
calls intf->set_interrupt(0). For this to work correctly the ticker
implementation must fire an interrupt on every rollover event though
intf->set_interrupt(0) was called only once. Whether an interrupt will
fire only once or multiple times is undefined behavior which
cannot be relied upon.

To avoid this undefined behavior this patch continually schedules an
interrupt and performs overflow detection on every read. This also
removes the possibility of race conditions due to overflowCounter
incrementing at the wrong time.
  • Loading branch information
c1728p9 committed Aug 17, 2018
1 parent f68958d commit 00b8e24
Showing 1 changed file with 42 additions and 19 deletions.
61 changes: 42 additions & 19 deletions TESTS/mbed_hal/common_tickers_freq/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,51 @@
using namespace utest::v1;

const ticker_interface_t *intf;
uint32_t intf_mask;
uint32_t intf_last_tick;
uint32_t intf_elapsed_ticks;
ticker_irq_handler_type prev_handler;

static volatile unsigned int overflowCounter;

uint32_t ticks_to_us(uint32_t ticks, uint32_t freq)
{
return (uint32_t)((uint64_t)ticks * US_PER_S / freq);
}

void ticker_event_handler_stub(const ticker_data_t *const ticker)
void elapsed_ticks_reset()
{
if (ticker == get_us_ticker_data()) {
us_ticker_clear_interrupt();
} else {
#if DEVICE_LPTICKER
lp_ticker_clear_interrupt();
#endif
}
core_util_critical_section_enter();

const uint32_t ticker_bits = intf->get_info()->bits;

overflowCounter++;
intf_mask = (1 << ticker_bits) - 1;
intf_last_tick = intf->read();
intf_elapsed_ticks = 0;

core_util_critical_section_exit();
}

uint32_t elapsed_ticks_update()
{
core_util_critical_section_enter();

const uint32_t current_tick = intf->read();
intf_elapsed_ticks += (current_tick - intf_last_tick) & intf_mask;
intf_last_tick = current_tick;

/* Schedule next interrupt half way to overflow */
uint32_t next = (current_tick + intf_mask / 2) & intf_mask;
intf->set_interrupt(next);

uint32_t elapsed = intf_elapsed_ticks;

core_util_critical_section_exit();
return elapsed;
}

void ticker_event_handler_stub(const ticker_data_t *const ticker)
{
intf->clear_interrupt();
elapsed_ticks_update();
}

/* Test that the ticker is operating at the frequency it specifies. */
Expand All @@ -68,13 +93,13 @@ void ticker_frequency_test()
char _value[128] = { };
int expected_key = 1;
const uint32_t ticker_freq = intf->get_info()->frequency;
const uint32_t ticker_bits = intf->get_info()->bits;
const uint32_t ticker_max = (1 << ticker_bits) - 1;

intf->init();

elapsed_ticks_reset();

/* Detect overflow for tickers with lower counters width. */
intf->set_interrupt(0);
elapsed_ticks_update();

greentea_send_kv("timing_drift_check_start", 0);

Expand All @@ -84,19 +109,17 @@ void ticker_frequency_test()
expected_key = strcmp(_key, "base_time");
} while (expected_key);

overflowCounter = 0;

const uint32_t begin_ticks = intf->read();
const uint32_t begin_ticks = elapsed_ticks_update();

/* Assume that there was no overflow at this point - we are just after init. */
greentea_send_kv(_key, ticks_to_us(begin_ticks, ticker_freq));

/* Wait for 2nd signal from host. */
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));

const uint32_t end_ticks = intf->read();
const uint32_t end_ticks = elapsed_ticks_update();

greentea_send_kv(_key, ticks_to_us(end_ticks + overflowCounter * ticker_max, ticker_freq));
greentea_send_kv(_key, ticks_to_us(end_ticks, ticker_freq));

/* Get the results from host. */
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
Expand Down

0 comments on commit 00b8e24

Please sign in to comment.