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