Skip to content

Commit ec55b82

Browse files
committed
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.
1 parent 0fd13b5 commit ec55b82

File tree

7 files changed

+375
-35
lines changed

7 files changed

+375
-35
lines changed

TESTS/mbed_hal/ticker/main.cpp

Lines changed: 237 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ using namespace utest::v1;
2929

3030
#define MBED_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
3131

32-
#define TIMESTAMP_MAX_DELTA MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA
32+
#define TIMESTAMP_MAX_DELTA_BITS(bits) ((uint64_t)(0x7 << ((bits) - 4)))
33+
#define TIMESTAMP_MAX_DELTA TIMESTAMP_MAX_DELTA_BITS(32)
3334

3435
struct ticker_interface_stub_t {
3536
ticker_interface_t interface;
@@ -43,9 +44,11 @@ struct ticker_interface_stub_t {
4344
unsigned int clear_interrupt_call;
4445
unsigned int set_interrupt_call;
4546
unsigned int fire_interrupt_call;
47+
unsigned int get_info_call;
4648
};
4749

4850
static ticker_interface_stub_t interface_stub = { 0 };
51+
static ticker_info_t interface_info_stub = { 0 };
4952

5053
static void ticker_interface_stub_init()
5154
{
@@ -81,6 +84,12 @@ static void ticker_interface_stub_fire_interrupt()
8184
++interface_stub.fire_interrupt_call;
8285
}
8386

87+
static const ticker_info_t *ticker_interface_stub_get_info()
88+
{
89+
++interface_stub.get_info_call;
90+
return &interface_info_stub;
91+
}
92+
8493
static void reset_ticker_interface_stub()
8594
{
8695
interface_stub.interface.init = ticker_interface_stub_init;
@@ -91,6 +100,7 @@ static void reset_ticker_interface_stub()
91100
ticker_interface_stub_clear_interrupt;
92101
interface_stub.interface.set_interrupt =ticker_interface_stub_set_interrupt;
93102
interface_stub.interface.fire_interrupt = ticker_interface_stub_fire_interrupt;
103+
interface_stub.interface.get_info = ticker_interface_stub_get_info;
94104
interface_stub.initialized = false;
95105
interface_stub.interrupt_flag = false;
96106
interface_stub.timestamp = 0;
@@ -101,6 +111,9 @@ static void reset_ticker_interface_stub()
101111
interface_stub.clear_interrupt_call = 0;
102112
interface_stub.set_interrupt_call = 0;
103113
interface_stub.fire_interrupt_call = 0;
114+
115+
interface_info_stub.frequency = 1000000;
116+
interface_info_stub.bits = 32;
104117
}
105118

106119
// stub of the event queue
@@ -115,6 +128,12 @@ static void reset_queue_stub()
115128
{
116129
queue_stub.event_handler = NULL;
117130
queue_stub.head = NULL,
131+
queue_stub.tick_last_read = 0;
132+
queue_stub.tick_remainder = 0;
133+
queue_stub.frequency = 0;
134+
queue_stub.bitmask = 0;
135+
queue_stub.max_delta = 0;
136+
queue_stub.max_delta_us = 0;
118137
queue_stub.present_time = 0;
119138
queue_stub.initialized = false;
120139
}
@@ -131,6 +150,34 @@ static void reset_ticker_stub()
131150
reset_ticker_interface_stub();
132151
}
133152

153+
const uint32_t test_frequencies[] = {
154+
1,
155+
32768, // 2^15
156+
1000000,
157+
0xFFFFFFFF // 2^32 - 1
158+
};
159+
160+
const uint32_t test_bitwidths[] = {
161+
32,
162+
31,
163+
16,
164+
8
165+
};
166+
167+
template < void (F)(uint32_t a, uint32_t b)>
168+
static void test_over_frequency_and_width(void)
169+
{
170+
for (unsigned int i = 0; i < MBED_ARRAY_SIZE(test_frequencies); i++) {
171+
for (unsigned int j = 0; j < MBED_ARRAY_SIZE(test_bitwidths); j++) {
172+
reset_ticker_stub();
173+
interface_info_stub.frequency = test_frequencies[i];
174+
interface_info_stub.bits = test_bitwidths[j];
175+
176+
F(test_frequencies[i], test_bitwidths[j]);
177+
}
178+
}
179+
}
180+
134181
static utest::v1::status_t case_setup_handler(
135182
const Case *const source, const size_t index_of_case
136183
) {
@@ -175,8 +222,7 @@ static utest::v1::status_t greentea_failure_handler(
175222
* Then:
176223
* - The ticker interface should be initialized
177224
* - The queue handler should be set to the handler provided in parameter
178-
* - The internal ticker timestamp should be synced with the counter in the
179-
* interface counter.
225+
* - The internal ticker timestamp should be zero
180226
* - interrupt should be scheduled in current timestamp +
181227
* TIMESTAMP_MAX_DELTA
182228
* - The queue should not contains any event
@@ -192,7 +238,7 @@ static void test_ticker_initialization()
192238

193239
TEST_ASSERT_TRUE(interface_stub.initialized);
194240
TEST_ASSERT_EQUAL_PTR(dummy_handler, queue_stub.event_handler);
195-
TEST_ASSERT_EQUAL_UINT64(interface_stub.timestamp, queue_stub.present_time);
241+
TEST_ASSERT_EQUAL_UINT64(0, queue_stub.present_time);
196242
TEST_ASSERT_EQUAL(1, interface_stub.set_interrupt_call);
197243
TEST_ASSERT_EQUAL_UINT32(
198244
interface_stub.timestamp + TIMESTAMP_MAX_DELTA,
@@ -347,7 +393,7 @@ static void test_legacy_insert_event_outside_overflow_range()
347393

348394
// test the beginning of the range
349395
ticker_event_t first_event = { 0 };
350-
const timestamp_t timestamp_first_event = interface_stub.timestamp + 1;
396+
const timestamp_t timestamp_first_event = interface_stub.timestamp + 1;
351397
const uint32_t id_first_event = 0xAAAAAAAA;
352398

353399
ticker_insert_event(
@@ -820,6 +866,7 @@ static void test_insert_event_us_outside_overflow_range()
820866
ticker_set_handler(&ticker_stub, NULL);
821867
interface_stub.set_interrupt_call = 0;
822868
interface_stub.timestamp = 0xAAAAAAAA;
869+
queue_stub.tick_last_read = interface_stub.timestamp;
823870
queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp;
824871

825872
// test the end of the range
@@ -881,6 +928,7 @@ static void test_insert_event_us_in_overflow_range()
881928
ticker_set_handler(&ticker_stub, NULL);
882929
interface_stub.set_interrupt_call = 0;
883930
interface_stub.timestamp = 0xAAAAAAAA;
931+
queue_stub.tick_last_read = interface_stub.timestamp;
884932
queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp;
885933

886934
// test the end of the range
@@ -944,6 +992,7 @@ static void test_insert_event_us_underflow()
944992
interface_stub.set_interrupt_call = 0;
945993

946994
interface_stub.timestamp = 0xAAAAAAAA;
995+
queue_stub.tick_last_read = interface_stub.timestamp;
947996
queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp;
948997

949998
// test the end of the range
@@ -979,6 +1028,7 @@ static void test_insert_event_us_head()
9791028
ticker_set_handler(&ticker_stub, NULL);
9801029
interface_stub.set_interrupt_call = 0;
9811030
interface_stub.timestamp = 0xAAAAAAAA;
1031+
queue_stub.tick_last_read = interface_stub.timestamp;
9821032
queue_stub.present_time = 10ULL << 32 | interface_stub.timestamp;
9831033

9841034
const us_timestamp_t timestamps[] = {
@@ -2003,6 +2053,8 @@ static uint32_t ticker_interface_stub_read_interrupt_time()
20032053
*/
20042054
static void test_set_interrupt_past_time()
20052055
{
2056+
ticker_set_handler(&ticker_stub, NULL);
2057+
20062058
interface_stub.set_interrupt_call = 0;
20072059
interface_stub.fire_interrupt_call = 0;
20082060
interface_stub.timestamp = 0xFF;
@@ -2023,6 +2075,8 @@ static void test_set_interrupt_past_time()
20232075
*/
20242076
static void test_set_interrupt_past_time_with_delay()
20252077
{
2078+
ticker_set_handler(&ticker_stub, NULL);
2079+
20262080
interface_stub.set_interrupt_call = 0;
20272081
interface_stub.fire_interrupt_call = 0;
20282082
interface_stub.timestamp = 0xFF;
@@ -2038,6 +2092,168 @@ static void test_set_interrupt_past_time_with_delay()
20382092
TEST_ASSERT_EQUAL(1, interface_stub.fire_interrupt_call);
20392093
}
20402094

2095+
/**
2096+
* Convert ticks at a given frequency to time in microseconds
2097+
*
2098+
* Assert if there is a 64-bit overflow
2099+
*/
2100+
static uint64_t convert_to_us(uint64_t ticks, uint32_t frequency)
2101+
{
2102+
uint64_t scaled_ticks = ticks * 1000000;
2103+
// Assert that there was not an overflow
2104+
TEST_ASSERT_EQUAL(ticks, scaled_ticks / 1000000);
2105+
return scaled_ticks / frequency;
2106+
}
2107+
2108+
/**
2109+
* Given an uninitialized ticker instance and an interface of a
2110+
* certain frequency and bit width.
2111+
* Then the time returned the ticker should match the cumulative time.
2112+
*/
2113+
void test_frequencies_and_masks(uint32_t frequency, uint32_t bits)
2114+
{
2115+
const uint32_t bitmask = ((uint64_t)1 << bits) - 1;
2116+
2117+
ticker_set_handler(&ticker_stub, NULL);
2118+
uint64_t ticks = 0;
2119+
2120+
// Single tick
2121+
ticks += 1;
2122+
interface_stub.timestamp = ticks & bitmask;
2123+
TEST_ASSERT_EQUAL_UINT32(convert_to_us(ticks, frequency), ticker_read(&ticker_stub));
2124+
TEST_ASSERT_EQUAL_UINT64(convert_to_us(ticks, frequency), ticker_read_us(&ticker_stub));
2125+
2126+
// Run until the loop before 64-bit overflow (worst case with frequency=1hz, bits=32)
2127+
for (unsigned int k = 0; k < 4294; k++) {
2128+
2129+
// Largest value possible tick
2130+
ticks += ((uint64_t)1 << bits) - 1;
2131+
interface_stub.timestamp = ticks & bitmask;
2132+
TEST_ASSERT_EQUAL_UINT32(convert_to_us(ticks, frequency), ticker_read(&ticker_stub));
2133+
TEST_ASSERT_EQUAL_UINT64(convert_to_us(ticks, frequency), ticker_read_us(&ticker_stub));
2134+
}
2135+
}
2136+
2137+
/**
2138+
* Given an uninitialized ticker_data instance.
2139+
* When the ticker is initialized
2140+
* Then:
2141+
* - The internal ticker timestamp should be zero
2142+
* - interrupt should be scheduled in current (timestamp +
2143+
* TIMESTAMP_MAX_DELTA_BITS(bitwidth)) % modval
2144+
* - The queue should not contains any event
2145+
*/
2146+
static void test_ticker_max_value()
2147+
{
2148+
for (int bitwidth = 8; bitwidth <= 32; bitwidth++) {
2149+
const uint64_t modval = 1ULL << bitwidth;
2150+
2151+
// setup of the stub
2152+
reset_ticker_stub();
2153+
interface_info_stub.bits = bitwidth;
2154+
interface_stub.timestamp = 0xBA;
2155+
2156+
ticker_set_handler(&ticker_stub, NULL);
2157+
2158+
TEST_ASSERT_EQUAL_UINT64(0, queue_stub.present_time);
2159+
TEST_ASSERT_EQUAL(1, interface_stub.set_interrupt_call);
2160+
TEST_ASSERT_EQUAL_UINT32(
2161+
(interface_stub.timestamp + TIMESTAMP_MAX_DELTA_BITS(bitwidth)) % modval,
2162+
interface_stub.interrupt_timestamp
2163+
);
2164+
TEST_ASSERT_EQUAL_PTR(NULL, queue_stub.head);
2165+
TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call);
2166+
}
2167+
}
2168+
2169+
/**
2170+
* Check that _ticker_match_interval_passed correctly detects matches
2171+
*
2172+
* Brute force test that _ticker_match_interval_passed returns the correct match value
2173+
* for all cominations of values within a small range.
2174+
*/
2175+
static void test_match_interval_passed()
2176+
{
2177+
2178+
for (int modval = 1; modval <= 5; modval++) {
2179+
for (int prev = 0; prev < modval; prev++) {
2180+
for (int cur = 0; cur < modval; cur++) {
2181+
for (int match = 0; match < modval; match++) {
2182+
uint32_t delta = (cur - prev) % modval;
2183+
uint32_t delta_to_match = (match - prev) % modval;
2184+
bool match_expected = false;
2185+
if (delta_to_match) {
2186+
match_expected = delta >= delta_to_match;
2187+
}
2188+
2189+
// Sanity checks
2190+
if (prev == cur) {
2191+
// No time has passed
2192+
TEST_ASSERT_EQUAL(false, match_expected);
2193+
} else if (match == prev) {
2194+
// Match can't occur without an overflow occurring
2195+
TEST_ASSERT_EQUAL(false, match_expected);
2196+
} else if (cur == match) {
2197+
// All other cases where cur == match a match should be expected
2198+
TEST_ASSERT_EQUAL(true, match_expected);
2199+
}
2200+
2201+
// Actual test
2202+
TEST_ASSERT_EQUAL(match_expected, _ticker_match_interval_passed(prev, cur, match));
2203+
}
2204+
}
2205+
}
2206+
}
2207+
}
2208+
2209+
typedef struct {
2210+
timestamp_t prev;
2211+
timestamp_t cur;
2212+
timestamp_t match;
2213+
bool result;
2214+
} match_interval_entry_t;
2215+
2216+
/**
2217+
* Check that _ticker_match_interval_passed correctly detects matches
2218+
*
2219+
* Use a table of pre-computed values to check that _ticker_match_interval_passed
2220+
* returns the correct match value.
2221+
*/
2222+
static void test_match_interval_passed_table()
2223+
{
2224+
static const match_interval_entry_t test_values[] = {
2225+
/* prev, cur, match, result */
2226+
{0x00000000, 0x00000000, 0x00000000, false},
2227+
{0x00000000, 0x00000000, 0xffffffff, false},
2228+
{0x00000000, 0x00000000, 0x00000001, false},
2229+
{0x00000000, 0xffffffff, 0x00000000, false},
2230+
{0x00000000, 0x00000001, 0x00000000, false},
2231+
{0xffffffff, 0x00000000, 0x00000000, true},
2232+
{0x00000001, 0x00000000, 0x00000000, true},
2233+
{0x00005555, 0x00005555, 0x00005555, false},
2234+
{0x00005555, 0x00005555, 0x00005554, false},
2235+
{0x00005555, 0x00005555, 0x00005556, false},
2236+
{0x00005555, 0x00005554, 0x00005555, false},
2237+
{0x00005555, 0x00005556, 0x00005555, false},
2238+
{0x00005554, 0x00005555, 0x00005555, true},
2239+
{0x00005556, 0x00005555, 0x00005555, true},
2240+
{0xffffffff, 0xffffffff, 0xffffffff, false},
2241+
{0xffffffff, 0xffffffff, 0xfffffffe, false},
2242+
{0xffffffff, 0xffffffff, 0x00000000, false},
2243+
{0xffffffff, 0xfffffffe, 0xffffffff, false},
2244+
{0xffffffff, 0x00000000, 0xffffffff, false},
2245+
{0xfffffffe, 0xffffffff, 0xffffffff, true},
2246+
{0x00000000, 0xffffffff, 0xffffffff, true},
2247+
};
2248+
for (int i = 0; i < MBED_ARRAY_SIZE(test_values); i++) {
2249+
const uint32_t prev = test_values[i].prev;
2250+
const uint32_t cur = test_values[i].cur;
2251+
const uint32_t match = test_values[i].match;
2252+
const uint32_t result = test_values[i].result;
2253+
TEST_ASSERT_EQUAL(result, _ticker_match_interval_passed(prev, cur, match));
2254+
}
2255+
}
2256+
20412257
static const case_t cases[] = {
20422258
MAKE_TEST_CASE("ticker initialization", test_ticker_initialization),
20432259
MAKE_TEST_CASE(
@@ -2130,6 +2346,22 @@ static const case_t cases[] = {
21302346
MAKE_TEST_CASE(
21312347
"test_set_interrupt_past_time_with_delay",
21322348
test_set_interrupt_past_time_with_delay
2349+
),
2350+
MAKE_TEST_CASE(
2351+
"test_frequencies_and_masks",
2352+
test_over_frequency_and_width<test_frequencies_and_masks>
2353+
),
2354+
MAKE_TEST_CASE(
2355+
"test_ticker_max_value",
2356+
test_ticker_max_value
2357+
),
2358+
MAKE_TEST_CASE(
2359+
"test_match_interval_passed",
2360+
test_match_interval_passed
2361+
),
2362+
MAKE_TEST_CASE(
2363+
"test_match_interval_passed_table",
2364+
test_match_interval_passed_table
21332365
)
21342366
};
21352367

hal/lp_ticker_api.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ void lp_ticker_clear_interrupt(void);
8080
*/
8181
void lp_ticker_fire_interrupt(void);
8282

83+
/** Get frequency and counter bits of this ticker.
84+
*
85+
*/
86+
const ticker_info_t* lp_ticker_get_info(void);
87+
8388
/**@}*/
8489

8590
#ifdef __cplusplus

hal/mbed_lp_ticker_api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static const ticker_interface_t lp_interface = {
2626
.clear_interrupt = lp_ticker_clear_interrupt,
2727
.set_interrupt = lp_ticker_set_interrupt,
2828
.fire_interrupt = lp_ticker_fire_interrupt,
29+
.get_info = lp_ticker_get_info,
2930
};
3031

3132
static const ticker_data_t lp_data = {

0 commit comments

Comments
 (0)