1 /*
2  * Copyright (c) 2016 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/tc_util.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/arch/cpu.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/irq_offload.h>
12 #include <stdbool.h>
13 
14 #if defined(CONFIG_ASSERT) && defined(CONFIG_DEBUG)
15 #define THREAD_STACK    (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
16 #else
17 #define THREAD_STACK    (384 + CONFIG_TEST_EXTRA_STACK_SIZE)
18 #endif
19 
20 #define TEST_THREAD_PRIORITY    -4
21 #define HELPER_THREAD_PRIORITY  -10
22 
23 #define ONE_SECOND		(MSEC_PER_SEC)
24 #define ONE_SECOND_ALIGNED	\
25 	(uint32_t)(k_ticks_to_ms_floor64(k_ms_to_ticks_ceil32(ONE_SECOND) + _TICK_ALIGN))
26 
27 #if defined(CONFIG_SOC_XILINX_ZYNQMP)
28 /*
29  * The Xilinx QEMU, used to emulate the Xilinx ZynqMP platform, is particularly
30  * unstable in terms of timing. The tick margin of at least 5 is necessary to
31  * allow this test to pass with a reasonable repeatability.
32  */
33 #define TICK_MARGIN		5
34 #else
35 #define TICK_MARGIN		1
36 #endif
37 
38 static struct k_sem test_thread_sem;
39 static struct k_sem helper_thread_sem;
40 static struct k_sem task_sem;
41 
42 static K_THREAD_STACK_DEFINE(test_thread_stack, THREAD_STACK);
43 static K_THREAD_STACK_DEFINE(helper_thread_stack, THREAD_STACK);
44 
45 static k_tid_t test_thread_id;
46 static k_tid_t helper_thread_id;
47 
48 static struct k_thread test_thread_data;
49 static struct k_thread helper_thread_data;
50 
51 static bool test_failure = true;     /* Assume the test will fail */
52 
53 /**
54  * @brief Test sleep and wakeup APIs
55  *
56  * @defgroup kernel_sleep_tests Sleep Tests
57  *
58  * @ingroup all_tests
59  *
60  * This module tests the following sleep and wakeup scenarios:
61  * 1. k_sleep() without cancellation
62  * 2. k_sleep() cancelled via k_wakeup()
63  * 3. k_sleep() cancelled via k_wakeup()
64  * 4. k_sleep() cancelled via k_wakeup()
65  * 5. k_sleep() - no cancellation exists
66  *
67  * @{
68  * @}
69  */
test_objects_init(void)70 static void test_objects_init(void)
71 {
72 	k_sem_init(&test_thread_sem, 0, UINT_MAX);
73 	k_sem_init(&helper_thread_sem, 0, UINT_MAX);
74 	k_sem_init(&task_sem, 0, UINT_MAX);
75 }
76 
align_to_tick_boundary(void)77 static void align_to_tick_boundary(void)
78 {
79 	uint32_t tick;
80 
81 	tick = k_uptime_get_32();
82 	while (k_uptime_get_32() == tick) {
83 		/* Busy wait to align to tick boundary */
84 		Z_SPIN_DELAY(50);
85 	}
86 
87 }
88 
89 /* Shouldn't ever sleep for less than requested time, but allow for 1
90  * tick of "too long" slop for aliasing between wakeup and
91  * measurement. Qemu at least will leak the external world's clock
92  * rate into the simulator when the host is under load.
93  */
sleep_time_valid(uint32_t start,uint32_t end,uint32_t dur)94 static int sleep_time_valid(uint32_t start, uint32_t end, uint32_t dur)
95 {
96 	uint32_t dt = end - start;
97 
98 	return dt >= dur && dt <= (dur + TICK_MARGIN);
99 }
100 
test_thread(void * p1,void * p2,void * p3)101 static void test_thread(void *p1, void *p2, void *p3)
102 {
103 	ARG_UNUSED(p1);
104 	ARG_UNUSED(p2);
105 	ARG_UNUSED(p3);
106 
107 	uint32_t start_tick;
108 	uint32_t end_tick;
109 
110 	k_sem_take(&test_thread_sem, K_FOREVER);
111 
112 	align_to_tick_boundary();
113 
114 	start_tick = k_uptime_get_32();
115 	k_sleep(K_SECONDS(1));
116 	end_tick = k_uptime_get_32();
117 
118 	if (!sleep_time_valid(start_tick, end_tick, ONE_SECOND_ALIGNED)) {
119 		TC_ERROR(" *** k_sleep() slept for %d ticks not %d.",
120 			 end_tick - start_tick, ONE_SECOND_ALIGNED);
121 
122 		return;
123 	}
124 
125 	k_sem_give(&helper_thread_sem);   /* Activate helper thread */
126 	align_to_tick_boundary();
127 
128 	start_tick = k_uptime_get_32();
129 	k_sleep(K_SECONDS(1));
130 	end_tick = k_uptime_get_32();
131 
132 	if (end_tick - start_tick > TICK_MARGIN) {
133 		TC_ERROR(" *** k_wakeup() took too long (%d ticks)\n",
134 			 end_tick - start_tick);
135 		return;
136 	}
137 
138 	k_sem_give(&helper_thread_sem);   /* Activate helper thread */
139 	align_to_tick_boundary();
140 
141 	start_tick = k_uptime_get_32();
142 	k_sleep(K_SECONDS(1));
143 	end_tick = k_uptime_get_32();
144 
145 	if (end_tick - start_tick > TICK_MARGIN) {
146 		TC_ERROR(" *** k_wakeup() took too long (%d ticks)\n",
147 			 end_tick - start_tick);
148 		return;
149 	}
150 
151 	k_sem_give(&task_sem);    /* Activate task */
152 	align_to_tick_boundary();
153 
154 	start_tick = k_uptime_get_32();
155 	k_sleep(K_SECONDS(1));	/* Task will execute */
156 	end_tick = k_uptime_get_32();
157 
158 	if (end_tick - start_tick > TICK_MARGIN) {
159 		TC_ERROR(" *** k_wakeup() took too long (%d ticks) at LAST\n",
160 			 end_tick - start_tick);
161 		return;
162 	}
163 	test_failure = false;
164 }
165 
irq_offload_isr(const void * arg)166 static void irq_offload_isr(const void *arg)
167 {
168 
169 	k_wakeup((k_tid_t) arg);
170 }
171 
helper_thread(void * p1,void * p2,void * p3)172 static void helper_thread(void *p1, void *p2, void *p3)
173 {
174 	ARG_UNUSED(p1);
175 	ARG_UNUSED(p2);
176 	ARG_UNUSED(p3);
177 
178 	k_sem_take(&helper_thread_sem, K_FOREVER);
179 	/* Wake the test thread */
180 	k_wakeup(test_thread_id);
181 	k_sem_take(&helper_thread_sem, K_FOREVER);
182 	/* Wake the test thread from an ISR */
183 	irq_offload(irq_offload_isr, (const void *)test_thread_id);
184 }
185 
186 /**
187  * @brief Test sleep functionality
188  *
189  * @ingroup kernel_sleep_tests
190  *
191  * @see k_sleep(), k_wakeup(), k_uptime_get_32()
192  */
ZTEST(sleep,test_sleep)193 ZTEST(sleep, test_sleep)
194 {
195 	int status = TC_FAIL;
196 	uint32_t start_tick;
197 	uint32_t end_tick;
198 
199 	/*
200 	 * Main thread(test_main) priority is 0 but ztest thread runs at
201 	 * priority -1. To run the test smoothly make both main and ztest
202 	 * threads run at same priority level.
203 	 */
204 	k_thread_priority_set(k_current_get(), 0);
205 	test_objects_init();
206 
207 	test_thread_id = k_thread_create(&test_thread_data, test_thread_stack,
208 					 THREAD_STACK,
209 					 test_thread,
210 					 0, 0, NULL, TEST_THREAD_PRIORITY,
211 					 0, K_NO_WAIT);
212 
213 	helper_thread_id = k_thread_create(&helper_thread_data,
214 					   helper_thread_stack, THREAD_STACK,
215 					   helper_thread,
216 					   0, 0, NULL, HELPER_THREAD_PRIORITY,
217 					   0, K_NO_WAIT);
218 
219 	/* Activate test_thread */
220 	k_sem_give(&test_thread_sem);
221 
222 	/* Wait for test_thread to activate us */
223 	k_sem_take(&task_sem, K_FOREVER);
224 
225 	/* Wake the test thread */
226 	k_wakeup(test_thread_id);
227 
228 	zassert_false(test_failure, "test failure");
229 
230 	align_to_tick_boundary();
231 	start_tick = k_uptime_get_32();
232 	k_sleep(K_SECONDS(1));
233 	end_tick = k_uptime_get_32();
234 	zassert_true(sleep_time_valid(start_tick, end_tick, ONE_SECOND_ALIGNED),
235 		     "k_sleep() slept for %d ticks, not %d\n",
236 		     end_tick - start_tick, ONE_SECOND_ALIGNED);
237 
238 	status = TC_PASS;
239 }
240 
forever_thread_entry(void * p1,void * p2,void * p3)241 static void forever_thread_entry(void *p1, void *p2, void *p3)
242 {
243 	int32_t ret;
244 
245 	ret = k_sleep(K_FOREVER);
246 	zassert_equal(ret, K_TICKS_FOREVER, "unexpected return value");
247 	k_sem_give(&test_thread_sem);
248 }
249 
ZTEST(sleep,test_sleep_forever)250 ZTEST(sleep, test_sleep_forever)
251 {
252 	test_objects_init();
253 
254 	test_thread_id = k_thread_create(&test_thread_data,
255 					 test_thread_stack,
256 					 THREAD_STACK,
257 					 forever_thread_entry,
258 					 0, 0, NULL, TEST_THREAD_PRIORITY,
259 					 K_USER | K_INHERIT_PERMS, K_NO_WAIT);
260 
261 	/* Allow forever thread to run */
262 	k_yield();
263 
264 	k_wakeup(test_thread_id);
265 	k_sem_take(&test_thread_sem, K_FOREVER);
266 }
267 
268 /*test case main entry*/
sleep_setup(void)269 static void *sleep_setup(void)
270 {
271 	k_thread_access_grant(k_current_get(), &test_thread_sem);
272 
273 	return NULL;
274 }
275 
276 ZTEST_SUITE(sleep, NULL, sleep_setup,
277 		ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
278