tests: drivers: nrf_rtc_timer: Add a test case to check rescheduling

Add a test case that schedules an alarm and then, after a delay that
is changed in every iteration, tries to reschedule it to one cycle
later.
This reveals a problem in the current nrf_rtc_timer implementation
that in certain conditions a scheduled timeout may be missed and
its expiration is handled 512 seconds later (when the system timer
overflows).

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2023-02-13 14:17:28 +01:00 committed by Carles Cufí
commit 8e8644ba6a

View file

@ -477,6 +477,69 @@ ZTEST(nrf_rtc_timer, test_next_cycle_timeouts)
z_nrf_rtc_timer_chan_free(chan);
}
static void tight_rescheduling_handler(int32_t chan,
uint64_t expire_time,
void *user_data)
{
if (user_data) {
*(bool *)user_data = true;
}
}
ZTEST(nrf_rtc_timer, test_tight_rescheduling)
{
int32_t chan;
volatile bool expired;
/* This test tries to schedule an alarm to CYCLE_DIFF cycles from
* the current moment and then, after a delay that is changed in
* each iteration, tries to reschedule this alarm to one cycle later.
* It does not matter if the first alarm actually occurs, the key
* thing is to always get the second one.
*/
enum {
CYCLE_DIFF = 5,
/* One RTC cycle is ~30.5 us. Check a range of delays from
* more than one cycle before the moment on which the first
* alarm is scheduled to a few microseconds after that alarm
* (when it is actually too late for rescheduling).
*/
DELAY_MIN = 30 * CYCLE_DIFF - 40,
DELAY_MAX = 30 * CYCLE_DIFF + 10,
};
chan = z_nrf_rtc_timer_chan_alloc();
zassert_true(chan > 0, "Failed to allocate RTC channel.");
/* Repeat the whole test a couple of times to get also (presumably)
* various micro delays resulting from execution of the test routine
* itself asynchronously to the RTC.
*/
for (uint32_t i = 0; i < 20; ++i) {
for (uint32_t delay = DELAY_MIN; delay <= DELAY_MAX; ++delay) {
uint64_t start = z_nrf_rtc_timer_read();
z_nrf_rtc_timer_set(chan, start + CYCLE_DIFF,
tight_rescheduling_handler, NULL);
k_busy_wait(delay);
expired = false;
z_nrf_rtc_timer_set(chan, start + CYCLE_DIFF + 1,
tight_rescheduling_handler, (void *)&expired);
while (!expired &&
(z_nrf_rtc_timer_read() - start) <
CYCLE_DIFF + 10) {
}
zassert_true(expired,
"Timeout expiration missed (d: %u us, i: %u)",
delay, i);
}
}
z_nrf_rtc_timer_chan_free(chan);
}
static void *rtc_timer_setup(void)
{
init_zli_timer0();