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