1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/portability/cmsis_os2.h>
10 #include <zephyr/portability/cmsis_types.h>
11
12 #define STACKSZ CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE
13
14 /* This is used to check the thread yield functionality between 2 threads */
15 static int thread_yield_check;
16 static int thread_yield_check_dynamic;
17
18 static K_THREAD_STACK_DEFINE(test_stack1, STACKSZ);
19 static osThreadAttr_t os_thread1_attr = {
20 .name = "Thread1",
21 .stack_mem = &test_stack1,
22 .stack_size = STACKSZ,
23 .priority = osPriorityHigh,
24 };
25
26 static K_THREAD_STACK_DEFINE(test_stack2, STACKSZ);
27 static osThreadAttr_t os_thread2_attr = {
28 .name = "Thread2",
29 .stack_mem = &test_stack2,
30 .stack_size = STACKSZ,
31 .priority = osPriorityHigh,
32 };
33
34 struct thread1_args {
35 int *yield_check;
36 const char *name;
37 };
38
thread1(void * argument)39 static void thread1(void *argument)
40 {
41 osStatus_t status;
42 osThreadId_t thread_id;
43 const char *name;
44 struct thread1_args *args = (struct thread1_args *)argument;
45
46 thread_id = osThreadGetId();
47 zassert_true(thread_id != NULL, "Failed getting Thread ID");
48
49 name = osThreadGetName(thread_id);
50 zassert_str_equal(args->name, name, "Failed getting Thread name");
51
52 /* This thread starts off at a high priority (same as thread2) */
53 (*args->yield_check)++;
54 zassert_equal(*args->yield_check, 1);
55
56 /* Yield to thread2 which is of same priority */
57 status = osThreadYield();
58 zassert_true(status == osOK, "Error doing thread yield");
59
60 /* thread_yield_check should now be 2 as it was incremented
61 * in thread2.
62 */
63 zassert_equal(*args->yield_check, 2);
64
65 osThreadExit();
66 }
67
thread2(void * argument)68 static void thread2(void *argument)
69 {
70 uint32_t i, num_threads, max_num_threads = 5U;
71 osThreadId_t *thread_array;
72 int *yield_check = (int *)argument;
73
74 /* By now thread1 would have set thread_yield_check to 1 and would
75 * have yielded the CPU. Incrementing it over here would essentially
76 * confirm that the yield was indeed executed.
77 */
78 (*yield_check)++;
79
80 thread_array = k_calloc(max_num_threads, sizeof(osThreadId_t));
81 num_threads = osThreadEnumerate(thread_array, max_num_threads);
82 zassert_equal(num_threads, 2, "Incorrect number of cmsis rtos v2 threads");
83
84 for (i = 0U; i < num_threads; i++) {
85 uint32_t size = osThreadGetStackSize(thread_array[i]);
86 uint32_t space = osThreadGetStackSpace(thread_array[i]);
87
88 zassert_true(space < size, "stack size remaining is not what is expected");
89 }
90
91 zassert_equal(osThreadGetState(thread_array[1]), osThreadReady,
92 "Thread not in ready state");
93 zassert_equal(osThreadGetState(thread_array[0]), osThreadRunning,
94 "Thread not in running state");
95
96 zassert_equal(osThreadSuspend(thread_array[1]), osOK, "");
97 zassert_equal(osThreadGetState(thread_array[1]), osThreadBlocked,
98 "Thread not in blocked state");
99
100 zassert_equal(osThreadResume(thread_array[1]), osOK, "");
101 zassert_equal(osThreadGetState(thread_array[1]), osThreadReady,
102 "Thread not in ready state");
103
104 k_free(thread_array);
105
106 /* Yield back to thread1 which is of same priority */
107 osThreadYield();
108 }
109
thread_apis_common(int * yield_check,const char * thread1_name,osThreadAttr_t * thread1_attr,osThreadAttr_t * thread2_attr)110 static void thread_apis_common(int *yield_check, const char *thread1_name,
111 osThreadAttr_t *thread1_attr, osThreadAttr_t *thread2_attr)
112 {
113 osThreadId_t id1;
114 osThreadId_t id2;
115
116 struct thread1_args args = {.yield_check = yield_check, .name = thread1_name};
117
118 id1 = osThreadNew(thread1, &args, thread1_attr);
119 zassert_true(id1 != NULL, "Failed creating thread1");
120
121 id2 = osThreadNew(thread2, yield_check, thread2_attr);
122 zassert_true(id2 != NULL, "Failed creating thread2");
123
124 zassert_equal(osThreadGetCount(), 2, "Incorrect number of cmsis rtos v2 threads");
125
126 do {
127 osDelay(100);
128 } while (*yield_check != 2);
129 }
130
ZTEST(cmsis_thread_apis,test_thread_apis_dynamic)131 ZTEST(cmsis_thread_apis, test_thread_apis_dynamic)
132 {
133 thread_apis_common(&thread_yield_check_dynamic, "ZephyrThread", NULL, NULL);
134 }
135
ZTEST(cmsis_thread_apis,test_thread_apis)136 ZTEST(cmsis_thread_apis, test_thread_apis)
137 {
138 thread_apis_common(&thread_yield_check, os_thread1_attr.name, &os_thread1_attr,
139 &os_thread2_attr);
140 }
141
142 static osPriority_t OsPriorityInvalid = 60;
143
144 /* This is used to indicate the completion of processing for thread3 */
145 static int thread3_state;
146 static int thread3_state_dynamic;
147
148 static K_THREAD_STACK_DEFINE(test_stack3, STACKSZ);
149 static osThreadAttr_t thread3_attr = {
150 .name = "Thread3",
151 .stack_mem = &test_stack3,
152 .stack_size = STACKSZ,
153 .priority = osPriorityNormal,
154 };
155
thread3(void * argument)156 static void thread3(void *argument)
157 {
158 osStatus_t status;
159 osPriority_t rv;
160 osThreadId_t id = osThreadGetId();
161 osPriority_t prio = osThreadGetPriority(id);
162 int *state = (int *)argument;
163
164 /* Lower the priority of the current thread */
165 osThreadSetPriority(id, osPriorityBelowNormal);
166 rv = osThreadGetPriority(id);
167 zassert_equal(rv, osPriorityBelowNormal, "Expected priority to be changed to %d, not %d",
168 (int)osPriorityBelowNormal, (int)rv);
169
170 /* Increase the priority of the current thread */
171 osThreadSetPriority(id, osPriorityAboveNormal);
172 rv = osThreadGetPriority(id);
173 zassert_equal(rv, osPriorityAboveNormal, "Expected priority to be changed to %d, not %d",
174 (int)osPriorityAboveNormal, (int)rv);
175
176 /* Restore the priority of the current thread */
177 osThreadSetPriority(id, prio);
178 rv = osThreadGetPriority(id);
179 zassert_equal(rv, prio, "Expected priority to be changed to %d, not %d", (int)prio,
180 (int)rv);
181
182 /* Try to set unsupported priority and assert failure */
183 status = osThreadSetPriority(id, OsPriorityInvalid);
184 zassert_true(status == osErrorParameter, "Something's wrong with osThreadSetPriority!");
185
186 /* Indication that thread3 is done with its processing */
187 *state = 1;
188
189 /* Keep looping till it gets killed */
190 do {
191 osDelay(100);
192 } while (1);
193 }
194
thread_prior_common(int * state,osThreadAttr_t * attr)195 static void thread_prior_common(int *state, osThreadAttr_t *attr)
196 {
197 osStatus_t status;
198 osThreadId_t id3;
199
200 id3 = osThreadNew(thread3, state, attr);
201 zassert_true(id3 != NULL, "Failed creating thread3");
202
203 /* Keep delaying 10 milliseconds to ensure thread3 is done with
204 * its execution. It loops at the end and is terminated here.
205 */
206 do {
207 osDelay(10);
208 } while (*state == 0);
209
210 status = osThreadTerminate(id3);
211 zassert_true(status == osOK, "Error terminating thread3");
212
213 /* Try to set priority to inactive thread and assert failure */
214 status = osThreadSetPriority(id3, osPriorityNormal);
215 zassert_true(status == osErrorResource, "Something's wrong with osThreadSetPriority!");
216
217 /* Try to terminate inactive thread and assert failure */
218 status = osThreadTerminate(id3);
219 zassert_true(status == osErrorResource, "Something's wrong with osThreadTerminate!");
220
221 *state = 0;
222 }
223
ZTEST(cmsis_thread_apis,test_thread_prio_dynamic)224 ZTEST(cmsis_thread_apis, test_thread_prio_dynamic)
225 {
226 thread_prior_common(&thread3_state_dynamic, NULL);
227 }
228
ZTEST(cmsis_thread_apis,test_thread_prio)229 ZTEST(cmsis_thread_apis, test_thread_prio)
230 {
231 thread_prior_common(&thread3_state, &thread3_attr);
232 }
233
234 #define DELAY_MS 1000
235 #define DELTA_MS 500
236
thread5(void * argument)237 static void thread5(void *argument)
238 {
239 printk(" * Thread B started.\n");
240 osDelay(k_ms_to_ticks_ceil32(DELAY_MS));
241 printk(" * Thread B joining...\n");
242 }
243
thread4(void * argument)244 static void thread4(void *argument)
245 {
246 osThreadId_t tB = argument;
247 osStatus_t status;
248
249 printk(" + Thread A started.\n");
250 status = osThreadJoin(tB);
251 zassert_equal(status, osOK, "osThreadJoin thread B failed!");
252 printk(" + Thread A joining...\n");
253 }
254
ZTEST(cmsis_thread_apis,test_thread_join)255 ZTEST(cmsis_thread_apis, test_thread_join)
256 {
257 osThreadAttr_t attr = {0};
258 int64_t time_stamp;
259 int64_t milliseconds_spent;
260 osThreadId_t tA, tB;
261 osStatus_t status;
262
263 attr.attr_bits = osThreadJoinable;
264
265 time_stamp = k_uptime_get();
266
267 printk(" - Creating thread B...\n");
268 tB = osThreadNew(thread5, NULL, &attr);
269 zassert_not_null(tB, "Failed to create thread B with osThreadNew!");
270
271 printk(" - Creating thread A...\n");
272 attr.priority = osPriorityLow;
273 tA = osThreadNew(thread4, tB, &attr);
274 zassert_not_null(tA, "Failed to create thread A with osThreadNew!");
275
276 printk(" - Waiting for thread B to join...\n");
277 status = osThreadJoin(tB);
278 zassert_equal(status, osOK, "osThreadJoin thread B failed!");
279
280 if (status == osOK) {
281 printk(" - Thread B joined.\n");
282 }
283
284 milliseconds_spent = k_uptime_delta(&time_stamp);
285 zassert_true((milliseconds_spent >= DELAY_MS - DELTA_MS) &&
286 (milliseconds_spent <= DELAY_MS + DELTA_MS),
287 "Join completed but was too fast or too slow.");
288
289 printk(" - Waiting for thread A to join...\n");
290 status = osThreadJoin(tA);
291 zassert_equal(status, osOK, "osThreadJoin thread A failed!");
292
293 if (status == osOK) {
294 printk(" - Thread A joined.\n");
295 }
296 }
297
ZTEST(cmsis_thread_apis,test_thread_detached)298 ZTEST(cmsis_thread_apis, test_thread_detached)
299 {
300 osThreadId_t thread;
301 osStatus_t status;
302
303 thread = osThreadNew(thread5, NULL, NULL); /* osThreadDetached */
304 zassert_not_null(thread, "Failed to create thread with osThreadNew!");
305
306 osDelay(k_ms_to_ticks_ceil32(DELAY_MS - DELTA_MS));
307
308 status = osThreadJoin(thread);
309 zassert_equal(status, osErrorResource, "Incorrect status returned from osThreadJoin!");
310
311 osDelay(k_ms_to_ticks_ceil32(DELTA_MS));
312 }
313
thread6(void * argument)314 void thread6(void *argument)
315 {
316 osThreadId_t thread = argument;
317 osStatus_t status;
318
319 /* Thread passed as argument may or may not have already terminated, but is expected to be
320 * joinable for this test as it was created with osThreadJoinable attr_bits.
321 */
322 status = osThreadJoin(thread);
323 zassert_equal(status, osOK, "Incorrect status returned from osThreadJoin!");
324 }
325
ZTEST(cmsis_thread_apis,test_thread_joinable_detach)326 ZTEST(cmsis_thread_apis, test_thread_joinable_detach)
327 {
328 osThreadAttr_t attr = {0};
329 osThreadId_t tA, tB;
330 osStatus_t status;
331
332 attr.attr_bits = osThreadJoinable;
333
334 tA = osThreadNew(thread5, NULL, &attr);
335 zassert_not_null(tA, "Failed to create thread with osThreadNew!");
336
337 tB = osThreadNew(thread6, tA, &attr);
338 zassert_not_null(tB, "Failed to create thread with osThreadNew!");
339
340 osDelay(k_ms_to_ticks_ceil32(DELAY_MS - DELTA_MS));
341
342 status = osThreadDetach(tA);
343 zassert_equal(status, osOK, "osThreadDetach failed.");
344
345 osDelay(k_ms_to_ticks_ceil32(DELTA_MS));
346 }
347
ZTEST(cmsis_thread_apis,test_thread_joinable_terminate)348 ZTEST(cmsis_thread_apis, test_thread_joinable_terminate)
349 {
350 osThreadAttr_t attr = {0};
351 osThreadId_t tA, tB;
352 osStatus_t status;
353
354 attr.attr_bits = osThreadJoinable;
355
356 tA = osThreadNew(thread5, NULL, &attr);
357 zassert_not_null(tA, "Failed to create thread with osThreadNew!");
358
359 tB = osThreadNew(thread6, tA, &attr);
360 zassert_not_null(tB, "Failed to create thread with osThreadNew!");
361
362 osDelay(k_ms_to_ticks_ceil32(DELAY_MS - DELTA_MS));
363
364 status = osThreadTerminate(tA);
365 zassert_equal(status, osOK, "osThreadTerminate failed.");
366
367 osDelay(k_ms_to_ticks_ceil32(DELTA_MS));
368 }
369
370 static K_THREAD_STACK_DEFINE(test_stack7, STACKSZ);
371 static struct cmsis_rtos_thread_cb test_cb7;
372 static const osThreadAttr_t os_thread7_attr = {
373 .name = "Thread7",
374 .cb_mem = &test_cb7,
375 .cb_size = sizeof(test_cb7),
376 .stack_mem = &test_stack7,
377 .stack_size = STACKSZ,
378 .priority = osPriorityNormal,
379 };
thread7(void * argument)380 static void thread7(void *argument)
381 {
382 printf("Thread 7 ran\n");
383 }
ZTEST(cmsis_thread_apis,test_thread_apis_static_allocation)384 ZTEST(cmsis_thread_apis, test_thread_apis_static_allocation)
385 {
386 osThreadId_t id;
387
388 id = osThreadNew(thread7, NULL, &os_thread7_attr);
389 zassert_not_null(id, "Failed to create thread with osThreadNew using static cb/stack");
390 }
391
392 static K_THREAD_STACK_DEFINE(test_stack8, STACKSZ);
393 static struct cmsis_rtos_thread_cb test_cb8;
394 static const osThreadAttr_t os_thread8_attr = {
395 .name = "Thread8",
396 .attr_bits = osThreadJoinable,
397 .cb_mem = &test_cb8,
398 .cb_size = sizeof(test_cb8),
399 .stack_mem = &test_stack8,
400 .stack_size = STACKSZ,
401 .priority = osPriorityNormal,
402 };
thread8(void * argument)403 static void thread8(void *argument)
404 {
405 printf("Thread 8 ran\n");
406 osDelay(k_ms_to_ticks_ceil32(DELAY_MS));
407 osThreadExit();
408 }
ZTEST(cmsis_thread_apis,test_thread_apis_join_after_exit)409 ZTEST(cmsis_thread_apis, test_thread_apis_join_after_exit)
410 {
411 osThreadId_t id;
412 osStatus_t status;
413
414 id = osThreadNew(thread8, NULL, &os_thread8_attr);
415 zassert_not_null(id, "Failed to create thread with osThreadNew using static cb/stack");
416
417 status = osThreadJoin(id);
418 zassert_equal(status, osOK, "osThreadJoin failed with status=%d!", status);
419 }
420
421 static K_THREAD_STACK_DEFINE(test_stack9, STACKSZ);
422 static struct cmsis_rtos_thread_cb test_cb9;
423 static const osThreadAttr_t os_thread9_attr = {
424 .name = "Thread9",
425 .attr_bits = osThreadJoinable,
426 .cb_mem = &test_cb9,
427 .cb_size = sizeof(test_cb9),
428 .stack_mem = &test_stack9,
429 .stack_size = STACKSZ,
430 .priority = osPriorityNormal,
431 };
thread9(void * argument)432 static void thread9(void *argument)
433 {
434 osThreadExit();
435 }
ZTEST(cmsis_thread_apis,test_thread_apis_multiple_new_static)436 ZTEST(cmsis_thread_apis, test_thread_apis_multiple_new_static)
437 {
438 osThreadId_t id;
439 osStatus_t status;
440
441 for (int i = 0; i < 100; i++) {
442 id = osThreadNew(thread9, NULL, &os_thread9_attr);
443 zassert_not_null(id,
444 "Failed to create thread with osThreadNew using static cb/stack");
445
446 status = osThreadJoin(id);
447 zassert_equal(status, osOK, "osThreadJoin failed with status=%d!", status);
448 }
449 }
450 ZTEST_SUITE(cmsis_thread_apis, NULL, NULL, NULL, NULL, NULL);
451