Skip to content

Commit f8e9ab6

Browse files
committed
[nrf fromlist] tests: drivers: timer: nrf_grtc_timer: Add stress test
Add stress test that randomly starts and aborts multiple timers from various contexts. Test checks if timers do not expire prematurely. Upstream PR #: 87944 Signed-off-by: Krzysztof Chruściński <[email protected]>
1 parent ef5ebf0 commit f8e9ab6

File tree

6 files changed

+354
-17
lines changed

6 files changed

+354
-17
lines changed

tests/drivers/timer/nrf_grtc_timer/boards/nrf54h20dk_nrf54h20_cpuapp.overlay

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,21 @@
22

33
&grtc {
44
/delete-property/ child-owned-channels;
5+
interrupts = <109 2>;
6+
};
7+
8+
test_timer: &timer131 {
9+
status = "okay";
10+
interrupts = <419 1>;
11+
};
12+
13+
&timer130 {
14+
status = "okay";
15+
prescaler = <0>;
16+
};
17+
18+
/ {
19+
chosen {
20+
zephyr,cpu-load-counter = &timer130;
21+
};
522
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* SPDX-License-Identifier: Apache-2.0 */
2+
3+
&grtc {
4+
interrupts = <228 2>;
5+
};
6+
7+
test_timer: &timer21 {
8+
status = "okay";
9+
interrupts = <203 1>;
10+
};
11+
12+
&timer20 {
13+
status = "okay";
14+
interrupts = <202 0>;
15+
};
16+
17+
&timer00 {
18+
status = "okay";
19+
prescaler = <0>;
20+
};
21+
22+
/ {
23+
chosen {
24+
zephyr,cpu-load-counter = &timer00;
25+
};
26+
27+
busy-sim {
28+
compatible = "vnd,busy-sim";
29+
status = "okay";
30+
counter = <&timer20>;
31+
};
32+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* SPDX-License-Identifier: Apache-2.0 */
2+
3+
&grtc {
4+
/*interrupts = <226 2>;*/
5+
};
6+
7+
test_timer: &timer21 {
8+
status = "okay";
9+
/*interrupts = <203 2>;*/
10+
};
11+
12+
&timer20 {
13+
status = "okay";
14+
interrupts = <202 0>;
15+
};
16+
17+
&timer00 {
18+
status = "okay";
19+
prescaler = <0>;
20+
};
21+
22+
/ {
23+
chosen {
24+
zephyr,cpu-load-counter = &timer00;
25+
};
26+
27+
busy-sim {
28+
compatible = "vnd,busy-sim";
29+
status = "okay";
30+
counter = <&timer20>;
31+
};
32+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
CONFIG_ZTEST=y
22
CONFIG_NRF_GRTC_TIMER=y
3+
CONFIG_COUNTER=y
4+
CONFIG_TEST_RANDOM_GENERATOR=y
5+
CONFIG_XOSHIRO_RANDOM_GENERATOR=y
6+
CONFIG_LOG_PRINTK=y
7+
CONFIG_CPU_LOAD=y
8+
CONFIG_CPU_LOAD_USE_COUNTER=y

tests/drivers/timer/nrf_grtc_timer/src/main.c

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@
55
*/
66
#include <zephyr/ztest.h>
77
#include <zephyr/drivers/timer/nrf_grtc_timer.h>
8+
#include <zephyr/drivers/counter.h>
9+
#include <zephyr/drivers/timer/system_timer.h>
10+
#include <zephyr/random/random.h>
11+
#include <zephyr/logging/log.h>
12+
#include <zephyr/busy_sim.h>
13+
#include <zephyr/debug/cpu_load.h>
14+
#include <nrfx_grtc.h>
815
#include <hal/nrf_grtc.h>
16+
LOG_MODULE_REGISTER(test, 1);
917

1018
#define GRTC_SLEW_TICKS 10
1119
#define NUMBER_OF_TRIES 2000
@@ -150,4 +158,243 @@ ZTEST(nrf_grtc_timer, test_timer_abort_in_compare_mode)
150158
z_nrf_grtc_timer_chan_free(channel);
151159
}
152160

161+
enum test_timer_state {
162+
TIMER_IDLE,
163+
TIMER_PREPARE,
164+
TIMER_ACTIVE
165+
};
166+
167+
enum test_ctx {
168+
TEST_HIGH_PRI,
169+
TEST_TIMER_CB,
170+
TEST_THREAD
171+
};
172+
173+
struct test_grtc_timer {
174+
struct k_timer timer;
175+
uint32_t ticks;
176+
uint32_t expire;
177+
uint32_t start_cnt;
178+
uint32_t expire_cnt;
179+
uint32_t abort_cnt;
180+
uint32_t exp_expire;
181+
int max_late;
182+
int min_late;
183+
int avg_late;
184+
uint32_t early_cnt;
185+
enum test_timer_state state;
186+
};
187+
188+
static atomic_t test_active_cnt;
189+
static struct test_grtc_timer timers[8];
190+
static uint32_t test_end;
191+
static k_tid_t test_tid;
192+
static volatile bool test_run;
193+
static uint32_t ctx_cnt[3];
194+
static const char *const ctx_name[] = { "HIGH PRIO ISR", "TIMER CALLBACK", "THREAD" };
195+
196+
static bool stress_test_action(int ctx, int id)
197+
{
198+
struct test_grtc_timer *timer = &timers[id];
199+
200+
ctx_cnt[ctx]++;
201+
if (timer->state == TIMER_ACTIVE) {
202+
/* Aborting soon to expire timers from higher interrupt priority may lead
203+
* to test failures.
204+
*/
205+
if (ctx == 0 && (k_timer_remaining_get(&timer->timer) < 5)) {
206+
return true;
207+
}
208+
209+
if (timer->abort_cnt < timer->expire_cnt / 2) {
210+
bool any_active;
211+
212+
timer->state = TIMER_PREPARE;
213+
k_timer_stop(&timer->timer);
214+
timer->abort_cnt++;
215+
any_active = atomic_dec(&test_active_cnt) > 1;
216+
timer->state = TIMER_IDLE;
217+
218+
return any_active;
219+
}
220+
} else if (timer->state == TIMER_IDLE) {
221+
int ticks = 10 + (sys_rand32_get() & 0x3F);
222+
k_timeout_t t = K_TICKS(ticks);
223+
224+
timer->exp_expire = k_ticks_to_cyc_floor32(sys_clock_tick_get_32() + ticks);
225+
timer->state = TIMER_PREPARE;
226+
timer->ticks = ticks;
227+
k_timer_start(&timer->timer, t, K_NO_WAIT);
228+
atomic_inc(&test_active_cnt);
229+
timer->start_cnt++;
230+
timer->state = TIMER_ACTIVE;
231+
}
232+
233+
return true;
234+
}
235+
236+
static void stress_test_actions(int ctx)
237+
{
238+
uint32_t r = sys_rand32_get();
239+
int action_cnt = Z_MAX(r & 0x3, 1);
240+
int tmr_id = (r >> 8) % ARRAY_SIZE(timers);
241+
242+
/* Occasionally wake thread context from which timer actions are also executed. */
243+
if ((((r >> 2) & 0x3) == 0) || test_active_cnt < 2) {
244+
LOG_DBG("ctx:%d thread wakeup", ctx);
245+
k_wakeup(test_tid);
246+
}
247+
248+
for (int i = 0; i < action_cnt; i++) {
249+
if (stress_test_action(ctx, tmr_id) == false) {
250+
stress_test_action(ctx, tmr_id);
251+
}
252+
}
253+
}
254+
255+
static void timer_cb(struct k_timer *timer)
256+
{
257+
struct test_grtc_timer *test_timer = CONTAINER_OF(timer, struct test_grtc_timer, timer);
258+
uint32_t now = k_cycle_get_32();
259+
int diff = now - test_timer->exp_expire;
260+
261+
atomic_dec(&test_active_cnt);
262+
zassert_true(diff >= 0);
263+
test_timer->max_late = MAX(diff, test_timer->max_late);
264+
test_timer->min_late = MIN(diff, test_timer->min_late);
265+
266+
if (test_timer->expire_cnt == 0) {
267+
test_timer->avg_late = diff;
268+
} else {
269+
test_timer->avg_late = (test_timer->avg_late * test_timer->expire_cnt + diff) /
270+
(test_timer->expire_cnt + 1);
271+
}
272+
273+
test_timer->expire_cnt++;
274+
test_timer->state = TIMER_IDLE;
275+
276+
if (test_run) {
277+
stress_test_actions(TEST_TIMER_CB);
278+
}
279+
}
280+
281+
static void counter_set(const struct device *dev, struct counter_alarm_cfg *cfg)
282+
{
283+
int err;
284+
uint32_t us = 150 + (sys_rand32_get() & 0x3F);
285+
286+
cfg->ticks = counter_us_to_ticks(dev, us);
287+
err = counter_set_channel_alarm(dev, 0, cfg);
288+
zassert_equal(err, 0);
289+
}
290+
291+
static void counter_cb(const struct device *dev, uint8_t chan_id, uint32_t ticks, void *user_data)
292+
{
293+
struct counter_alarm_cfg *config = user_data;
294+
295+
if (test_run) {
296+
stress_test_actions(TEST_HIGH_PRI);
297+
counter_set(dev, config);
298+
}
299+
}
300+
301+
static void report_progress(uint32_t start, uint32_t end)
302+
{
303+
static uint32_t next_report;
304+
static uint32_t step;
305+
static uint32_t progress;
306+
307+
if (next_report == 0) {
308+
step = (end - start) / 10;
309+
next_report = start + step;
310+
}
311+
312+
if (k_uptime_get_32() > next_report) {
313+
next_report += step;
314+
progress += 10;
315+
printk("%d%%\r", progress);
316+
}
317+
}
318+
319+
static void grtc_stress_test(bool busy_sim_en)
320+
{
321+
static struct counter_alarm_cfg alarm_cfg;
322+
#if DT_NODE_EXISTS(DT_NODELABEL(test_timer)) && DT_NODE_HAS_STATUS(DT_NODELABEL(test_timer), okay)
323+
const struct device *const counter_dev = DEVICE_DT_GET(DT_NODELABEL(test_timer));
324+
#else
325+
const struct device *const counter_dev = NULL;
326+
#endif
327+
uint32_t test_ms = 5000;
328+
uint32_t test_start = k_uptime_get_32();
329+
uint32_t load;
330+
331+
test_end = k_cycle_get_32() + k_ms_to_cyc_floor32(test_ms);
332+
test_tid = k_current_get();
333+
334+
for (size_t i = 0; i < ARRAY_SIZE(timers); i++) {
335+
k_timer_init(&timers[i].timer, timer_cb, NULL);
336+
}
337+
338+
if (IS_ENABLED(CONFIG_CPU_LOAD)) {
339+
(void)cpu_load_get(true);
340+
}
341+
342+
if (counter_dev) {
343+
counter_start(counter_dev);
344+
}
345+
346+
alarm_cfg.callback = counter_cb;
347+
alarm_cfg.user_data = &alarm_cfg;
348+
test_run = true;
349+
350+
if (counter_dev) {
351+
counter_set(counter_dev, &alarm_cfg);
352+
}
353+
354+
if (busy_sim_en) {
355+
busy_sim_start(500, 200, 1000, 400, NULL);
356+
}
357+
358+
LOG_DBG("Starting test, will end at %d", test_end);
359+
while (k_cycle_get_32() < test_end) {
360+
report_progress(test_start, test_start + test_ms);
361+
stress_test_actions(TEST_THREAD);
362+
k_sleep(K_MSEC(test_ms));
363+
}
364+
365+
load = IS_ENABLED(CONFIG_CPU_LOAD) ? cpu_load_get(true) : 0;
366+
367+
test_run = false;
368+
k_msleep(50);
369+
370+
for (size_t i = 0; i < ARRAY_SIZE(timers); i++) {
371+
zassert_equal(timers[i].state, TIMER_IDLE, "Unexpected timer %d state:%d",
372+
i, timers[i].state);
373+
TC_PRINT("Timer%d (%p)\r\n\tstart_cnt:%d abort_cnt:%d expire_cnt:%d\n",
374+
i, &timers[i], timers[i].start_cnt, timers[i].abort_cnt,
375+
timers[i].expire_cnt);
376+
TC_PRINT("\tavarage late:%d ticks, max late:%d, min late:%d early:%d\n",
377+
timers[i].avg_late, timers[i].max_late, timers[i].min_late,
378+
timers[i].early_cnt);
379+
}
380+
381+
for (size_t i = 0; i < ARRAY_SIZE(ctx_cnt); i++) {
382+
TC_PRINT("Context: %s executed %d times\n", ctx_name[i], ctx_cnt[i]);
383+
}
384+
TC_PRINT("CPU load during test:%d.%d\n", load / 10, load % 10);
385+
386+
if (busy_sim_en) {
387+
busy_sim_stop();
388+
}
389+
390+
if (counter_dev) {
391+
counter_stop(counter_dev);
392+
}
393+
}
394+
395+
ZTEST(nrf_grtc_timer, test_stress)
396+
{
397+
grtc_stress_test(false);
398+
}
399+
153400
ZTEST_SUITE(nrf_grtc_timer, NULL, NULL, NULL, NULL, NULL);
Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
common:
2+
tags:
3+
- drivers
4+
platform_allow:
5+
- nrf54l15dk/nrf54l15/cpuapp
6+
- nrf54l15dk/nrf54l15/cpuflpr
7+
- nrf54l15bsim/nrf54l15/cpuapp
8+
- nrf54h20dk/nrf54h20/cpuapp
9+
- nrf54h20dk/nrf54h20/cpurad
10+
- nrf54h20dk/nrf54h20/cpuppr
11+
- nrf54l20pdk/nrf54l20/cpuapp
12+
- nrf54l20pdk/nrf54l20/cpuflpr
13+
- ophelia4ev/nrf54l15/cpuapp
14+
- ophelia4ev/nrf54l15/cpuflpr
15+
integration_platforms:
16+
- nrf54l15dk/nrf54l15/cpuapp
117
tests:
2-
drivers.timer.nrf_grtc_timer:
3-
tags: drivers
4-
platform_allow:
5-
- nrf54l09pdk/nrf54l09/cpuapp
6-
- nrf54l09pdk/nrf54l09/cpuflpr
7-
- nrf54l15dk/nrf54l15/cpuapp
8-
- nrf54l15dk/nrf54l15/cpuflpr
9-
- nrf54l15bsim/nrf54l15/cpuapp
10-
- nrf54h20dk/nrf54h20/cpuapp
11-
- nrf54h20dk/nrf54h20/cpurad
12-
- nrf54h20dk/nrf54h20/cpuppr
13-
- nrf54l20pdk/nrf54l20/cpuapp
14-
- nrf54l20pdk/nrf54l20/cpuflpr
15-
- ophelia4ev/nrf54l15/cpuapp
16-
- ophelia4ev/nrf54l15/cpuflpr
17-
integration_platforms:
18-
- nrf54l20pdk/nrf54l20/cpuapp
18+
drivers.timer.nrf_grtc_timer: {}
19+
drivers.timer.nrf_grtc_timer.no_assert:
20+
extra_configs:
21+
- CONFIG_ASSERT=n

0 commit comments

Comments
 (0)