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