Skip to content

Commit b3bbeb5

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 5793ab5 commit b3bbeb5

File tree

6 files changed

+357
-20
lines changed

6 files changed

+357
-20
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
@@ -153,4 +161,243 @@ ZTEST(nrf_grtc_timer, test_timer_abort_in_compare_mode)
153161
z_nrf_grtc_timer_chan_free(channel);
154162
}
155163

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

0 commit comments

Comments
 (0)