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