Skip to content

Commit fe516b9

Browse files
committed
board: native_posix: Add test for k_busy_wait and cpu_hold
Add a new test for k_busy_wait and cpu_hold Signed-off-by: Alberto Escolar Piedras <[email protected]> Signed-off-by: Wolfgang Puffitsch <[email protected]>
1 parent 6d34761 commit fe516b9

File tree

5 files changed

+324
-0
lines changed

5 files changed

+324
-0
lines changed

drivers/timer/native_posix_timer.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ static void np_timer_isr(const void *arg)
4747
z_clock_announce(elapsed_ticks);
4848
}
4949

50+
/**
51+
* This function exists only to enable tests to call into the timer ISR
52+
*/
53+
void np_timer_isr_test_hook(const void *arg)
54+
{
55+
np_timer_isr(NULL);
56+
}
57+
5058
/*
5159
* @brief Initialize system timer driver
5260
*
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.13.1)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(cpu_wait)
7+
8+
target_sources(app PRIVATE src/main.c)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_TICKLESS_KERNEL=n
3+
CONFIG_ZTEST_THREAD_PRIORITY=1
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/*
2+
* Copyright (c) 2020 Oticon A/S
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <ztest.h>
8+
#include <zephyr.h>
9+
#include <sys/printk.h>
10+
11+
#include <stdlib.h>
12+
#include <stdio.h>
13+
#include "board_soc.h"
14+
15+
/**
16+
* @brief Basic test of the POSIX arch k_busy_wait() and cpu_hold()
17+
* functions
18+
*
19+
* In this basic case, only one k_busy_wait() or posix_cpu_hold executes
20+
* at a time
21+
*/
22+
static void test_cpu_hold_basic(void)
23+
{
24+
uint32_t wait_times[] = {1, 30, 0, 121, 10000};
25+
uint64_t time2, time1 = posix_get_hw_cycle();
26+
27+
for (int i = 0; i < ARRAY_SIZE(wait_times); i++) {
28+
k_busy_wait(wait_times[i]);
29+
time2 = posix_get_hw_cycle();
30+
zassert_true(time2 - time1 == wait_times[i],
31+
"k_busy_wait failed "
32+
PRIu64"-"PRIu64"!="PRIu32"\n",
33+
time2, time1, wait_times[i]);
34+
time1 = time2;
35+
}
36+
37+
for (int i = 0; i < ARRAY_SIZE(wait_times); i++) {
38+
posix_cpu_hold(wait_times[i]);
39+
time2 = posix_get_hw_cycle();
40+
zassert_true(time2 - time1 == wait_times[i],
41+
"posix_cpu_hold failed "
42+
PRIu64"-"PRIu64"!="PRIu32"\n",
43+
time2, time1, wait_times[i]);
44+
time1 = time2;
45+
}
46+
}
47+
48+
#define WASTED_TIME 1000 /* 1ms */
49+
#define THREAD_PRIO 0
50+
#define THREAD_DELAY 0
51+
/* Note: the duration of WASTED_TIME and thread priorities should not be changed
52+
* without thought, as they do matter for the test
53+
*/
54+
55+
#define ONE_TICK_TIME (1000000ul / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
56+
#define TWO_TICKS_TIME (2*1000000ul / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
57+
#define ONE_AND_HALF_TICKS (ONE_TICK_TIME + (ONE_TICK_TIME>>1))
58+
#define TWO_AND_HALF_TICKS ((ONE_TICK_TIME<<1) + (ONE_TICK_TIME>>1))
59+
60+
#if (WASTED_TIME > ONE_TICK_TIME/2)
61+
#error "This test will not work with this system tick period"
62+
#endif
63+
64+
static void thread_entry(void *p1, void *p2, void *p3);
65+
66+
K_THREAD_DEFINE(TIME_WASTER, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE,
67+
thread_entry, 0, 0, 0,
68+
THREAD_PRIO, 0, THREAD_DELAY);
69+
K_SEM_DEFINE(start_sema, 0, 1);
70+
K_SEM_DEFINE(end_sema, 0, 1);
71+
72+
/**
73+
* Thread meant to come up and waste time during the k_busy_wait() and
74+
* posix_cpu_hold() calls of test_cpu_hold_with_another_thread()
75+
*/
76+
static void thread_entry(void *p1, void *p2, void *p3)
77+
{
78+
int i;
79+
80+
ARG_UNUSED(p1);
81+
ARG_UNUSED(p2);
82+
ARG_UNUSED(p3);
83+
84+
for (i = 0; i < 4; i++) {
85+
/* Synchronize start of subtest with test thread */
86+
k_sem_take(&start_sema, K_FOREVER);
87+
/* Sleep until next tick
88+
* This sleep will take 2 ticks as the semaphore will
89+
* be given just after the previous tick boundary
90+
*/
91+
k_sleep(Z_TIMEOUT_TICKS(1));
92+
/* Waste time */
93+
k_busy_wait(WASTED_TIME);
94+
/* Synchronize end of subtest with test thread */
95+
k_sem_give(&end_sema);
96+
}
97+
}
98+
99+
/**
100+
* @brief Test the POSIX arch k_busy_wait and cpu_hold while another thread
101+
* takes time during this test thread waits
102+
*
103+
* Note: This test relies on the exact timing of the ticks.
104+
* For native_posix it works, with a tick of 10ms. In general this test will
105+
* probably give problems if the tick time is not a relatively even number
106+
* of microseconds
107+
*/
108+
static void test_cpu_hold_with_another_thread(void)
109+
{
110+
uint64_t time2, time1;
111+
112+
/* k_busy_wait part: */
113+
114+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
115+
k_sem_give(&start_sema);
116+
117+
time1 = posix_get_hw_cycle();
118+
k_busy_wait(TWO_TICKS_TIME + 1);
119+
/* The thread should have switched in and have used
120+
* WASTED_TIME us (1ms) right after 2*one_tick_time
121+
* As that is longer than 2 ticks + 1us, the total
122+
* should be 2 ticks + WASTED_TIME
123+
*/
124+
time2 = posix_get_hw_cycle();
125+
126+
zassert_true(time2 - time1 == TWO_TICKS_TIME + WASTED_TIME,
127+
"k_busy_wait failed "
128+
PRIu64"-"PRIu64"!="PRIu32"\n",
129+
time2, time1, TWO_TICKS_TIME + WASTED_TIME);
130+
131+
k_sem_take(&end_sema, K_FOREVER);
132+
133+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
134+
k_sem_give(&start_sema);
135+
136+
time1 = posix_get_hw_cycle();
137+
k_busy_wait(TWO_AND_HALF_TICKS);
138+
/* The thread should have used WASTED_TIME us (1ms) after
139+
* 2*one_tick_time, but as that is lower than 2.5 ticks, in
140+
* total the wait should be 2.5 ticks
141+
*/
142+
time2 = posix_get_hw_cycle();
143+
144+
zassert_true(time2 - time1 == TWO_AND_HALF_TICKS,
145+
"k_busy_wait failed "
146+
PRIu64"-"PRIu64"!="PRIu32"\n",
147+
time2, time1, TWO_AND_HALF_TICKS);
148+
149+
k_sem_take(&end_sema, K_FOREVER);
150+
151+
/* CPU hold part: */
152+
153+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
154+
k_sem_give(&start_sema);
155+
156+
time1 = posix_get_hw_cycle();
157+
posix_cpu_hold(TWO_TICKS_TIME + 1);
158+
/* The thread should have used WASTED_TIME us after 2*one_tick_time,
159+
* so the total should be 2 ticks + WASTED_TIME + 1.
160+
* That is we spend 2 ticks + 1 us in this context as requested.
161+
*/
162+
time2 = posix_get_hw_cycle();
163+
164+
zassert_true(time2 - time1 == TWO_TICKS_TIME + WASTED_TIME + 1,
165+
"k_busy_wait failed "
166+
PRIu64"-"PRIu64"!="PRIu32"\n",
167+
time2, time1, TWO_TICKS_TIME + WASTED_TIME + 1);
168+
169+
k_sem_take(&end_sema, K_FOREVER);
170+
171+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
172+
k_sem_give(&start_sema);
173+
174+
time1 = posix_get_hw_cycle();
175+
posix_cpu_hold(TWO_AND_HALF_TICKS);
176+
/* The thread should have used WASTED_TIME us after 2*one_tick_time,
177+
* so the total wait should be 2.5 ticks + WASTED_TIME.
178+
* That is 2.5 ticks in this context, and WASTED_TIME in the other
179+
* thread context
180+
*/
181+
182+
time2 = posix_get_hw_cycle();
183+
184+
zassert_true(time2 - time1 == TWO_AND_HALF_TICKS + WASTED_TIME,
185+
"k_busy_wait failed "
186+
PRIu64"-"PRIu64"!="PRIu32"\n",
187+
time2, time1, TWO_AND_HALF_TICKS + WASTED_TIME);
188+
189+
k_sem_take(&end_sema, K_FOREVER);
190+
}
191+
192+
/**
193+
* Replacement system tick timer interrupt handler which wastes time
194+
* before calling the real one
195+
*/
196+
static void np_timer_isr_test_replacement(const void *arg)
197+
{
198+
ARG_UNUSED(arg);
199+
200+
k_busy_wait(WASTED_TIME);
201+
202+
void np_timer_isr_test_hook(const void *arg);
203+
np_timer_isr_test_hook(NULL);
204+
}
205+
206+
/**
207+
* @brief Test posix arch k_busy_wait and cpu_hold with interrupts that take
208+
* time.
209+
* The test is timed so that interrupts arrive during the wait times.
210+
*
211+
* The kernel is configured as NOT-tickless, and the default tick period is
212+
* 10ms
213+
*/
214+
static void test_cpu_hold_with_interrupts(void)
215+
{
216+
#if defined(CONFIG_BOARD_NATIVE_POSIX)
217+
/* So far we only have a test for native_posix.
218+
* As the test hooks into an interrupt to cause an extra delay
219+
* this is very platform specific
220+
*/
221+
uint64_t time2, time1;
222+
223+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until a tick boundary */
224+
225+
IRQ_CONNECT(TIMER_TICK_IRQ, 1, np_timer_isr_test_replacement, 0, 0);
226+
227+
time1 = posix_get_hw_cycle();
228+
k_busy_wait(ONE_TICK_TIME + 1);
229+
/* Just after ONE_TICK_TIME (10ms) the timer interrupt has come,
230+
* causing a delay of WASTED_TIME (1ms), so the k_busy_wait()
231+
* returns immediately as it was waiting for 10.001 ms
232+
*/
233+
time2 = posix_get_hw_cycle();
234+
235+
zassert_true(time2 - time1 == ONE_TICK_TIME + WASTED_TIME,
236+
"k_busy_wait failed "
237+
PRIu64"-"PRIu64"!="PRIu32"\n",
238+
time2, time1, ONE_TICK_TIME);
239+
240+
241+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
242+
243+
time1 = posix_get_hw_cycle();
244+
k_busy_wait(ONE_AND_HALF_TICKS);
245+
/* Just after ONE_TICK_TIME (10ms) the timer interrupt has come,
246+
* causing a delay of WASTED_TIME (1ms), after that, the k_busy_wait()
247+
* continues until 15ms
248+
*/
249+
time2 = posix_get_hw_cycle();
250+
251+
zassert_true(time2 - time1 == ONE_AND_HALF_TICKS,
252+
"k_busy_wait failed "
253+
PRIu64"-"PRIu64"!="PRIu32"\n",
254+
time2, time1, ONE_TICK_TIME);
255+
256+
257+
258+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
259+
260+
time1 = posix_get_hw_cycle();
261+
posix_cpu_hold(ONE_TICK_TIME + 1);
262+
/* Just after ONE_TICK_TIME (10ms) the timer interrupt has come,
263+
* causing a delay of WASTED_TIME (1ms), but posix_cpu_hold continues
264+
* until it spends 10.001 ms in this context. That is 11.001ms in total
265+
*/
266+
time2 = posix_get_hw_cycle();
267+
268+
zassert_true(time2 - time1 == ONE_TICK_TIME + 1 + WASTED_TIME,
269+
"k_busy_wait failed "
270+
PRIu64"-"PRIu64"!="PRIu32"\n",
271+
time2, time1, ONE_TICK_TIME);
272+
273+
274+
k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */
275+
276+
time1 = posix_get_hw_cycle();
277+
posix_cpu_hold(ONE_AND_HALF_TICKS);
278+
/* Just after ONE_TICK_TIME (10ms) the timer interrupt has come,
279+
* causing a delay of WASTED_TIME (1ms), but posix_cpu_hold continues
280+
* until it spends 15ms in this context. That is 16ms in total
281+
*/
282+
time2 = posix_get_hw_cycle();
283+
284+
zassert_true(time2 - time1 == ONE_AND_HALF_TICKS + WASTED_TIME,
285+
"k_busy_wait failed "
286+
PRIu64"-"PRIu64"!="PRIu32"\n",
287+
time2, time1, ONE_TICK_TIME);
288+
289+
#endif /* defined(CONFIG_BOARD_NATIVE_POSIX) */
290+
}
291+
292+
void test_main(void)
293+
{
294+
ztest_test_suite(native_cpu_hold,
295+
ztest_unit_test(test_cpu_hold_basic),
296+
ztest_unit_test(test_cpu_hold_with_another_thread),
297+
ztest_unit_test(test_cpu_hold_with_interrupts)
298+
);
299+
300+
ztest_run_test_suite(native_cpu_hold);
301+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Test of the posix_arch k_busy_wait & cpu_hold() functionality
2+
tests:
3+
boards.native_posix.cpu_wait:
4+
platform_allow: native_posix native_posix_64

0 commit comments

Comments
 (0)