Skip to content

Commit fad6755

Browse files
Merge pull request #5028 from c1728p9/ticker_api_changes
Update ticker to map closely to hardware
2 parents bf9c507 + 77dd420 commit fad6755

File tree

8 files changed

+438
-36
lines changed

8 files changed

+438
-36
lines changed

TESTS/mbed_hal/ticker/main.cpp

Lines changed: 238 additions & 6 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,12 +2346,28 @@ 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

21362368
static utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
21372369
{
2138-
GREENTEA_SETUP(30, "default_auto");
2370+
GREENTEA_SETUP(60, "default_auto");
21392371
return verbose_test_setup_handler(number_of_cases);
21402372
}
21412373

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)