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