| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /* |
| * @file |
| * @brief Use stack API's in different scenarios |
| * |
| * This module tests following three basic scenarios: |
| * |
| * Scenario #1 |
| * Test thread enters items into a stack, starts the Child thread and |
| * waits for a semaphore. Child thread extracts all items from the stack |
| * and enters some items back into the stack. Child thread gives the |
| * semaphore for Test thread to continue. Once the control is returned |
| * back to Test thread, it extracts all items from the stack. |
| * |
| * Scenario #2 |
| * Test thread enters an item into stack2, starts a Child thread and |
| * extract an item from stack1 once the item is there. The child thread |
| * will extract an item from stack2 once the item is there and and enter |
| * an item to stack1. The flow of control goes from Test thread to Child |
| * thread and so forth. |
| * |
| * Scenario #3 |
| * Tests the ISR interfaces. Test thread pushes items into stack2 and gives |
| * control to the Child thread. Child thread pops items from stack2 and then |
| * pushes items into stack1. Child thread gives back control to the Test thread |
| * and Test thread pops the items from stack1. |
| * All the Push and Pop operations happen in ISR Context. |
| */ |
| |
| |
| /** |
| * @brief Tests for Kernel stack objects |
| * @defgroup kernel_stack_tests Stacks |
| * @ingroup all_tests |
| * @{ |
| * @} |
| */ |
| |
| #include <ztest.h> |
| #include <irq_offload.h> |
| |
| #define TSTACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE) |
| #define STACK_LEN 4 |
| |
| /* stack objects used in this test */ |
| K_STACK_DEFINE(stack1, STACK_LEN); |
| K_STACK_DEFINE(stack2, STACK_LEN); |
| |
| /* thread info * */ |
| K_THREAD_STACK_DEFINE(threadstack, TSTACK_SIZE); |
| struct k_thread thread_data; |
| |
| /* Data pushed to stack */ |
| static ZTEST_DMEM stack_data_t data1[STACK_LEN] = { 0xAAAA, 0xBBBB, 0xCCCC, 0xDDDD }; |
| static ZTEST_DMEM stack_data_t data2[STACK_LEN] = { 0x1111, 0x2222, 0x3333, 0x4444 }; |
| static ZTEST_DMEM stack_data_t data_isr[STACK_LEN] = { 0xABCD, 0xABCD, 0xABCD, |
| 0xABCD }; |
| |
| /* semaphore to sync threads */ |
| static struct k_sem end_sema; |
| |
| |
| |
| K_HEAP_DEFINE(test_pool, 128 * 3); |
| |
| extern struct k_stack kstack; |
| extern struct k_stack stack; |
| extern struct k_sem end_sema; |
| |
| extern void test_stack_thread2thread(void); |
| extern void test_stack_thread2isr(void); |
| extern void test_stack_pop_fail(void); |
| extern void test_stack_alloc_thread2thread(void); |
| extern void test_stack_pop_can_wait(void); |
| extern void test_stack_cleanup_error(void); |
| extern void test_stack_push_full(void); |
| extern void test_stack_multithread_competition(void); |
| extern void test_stack_alloc_null(void); |
| #ifdef CONFIG_USERSPACE |
| extern void test_stack_user_thread2thread(void); |
| extern void test_stack_user_pop_fail(void); |
| extern void test_stack_user_init_null(void); |
| extern void test_stack_user_init_invalid_value(void); |
| extern void test_stack_user_push_null(void); |
| extern void test_stack_user_pop_null(void); |
| extern void test_stack_user_pop_permission(void); |
| #else |
| #define dummy_test(_name) \ |
| static void _name(void) \ |
| { \ |
| ztest_test_skip(); \ |
| } |
| |
| dummy_test(test_stack_user_thread2thread); |
| dummy_test(test_stack_user_pop_fail); |
| dummy_test(test_stack_user_init_null); |
| dummy_test(test_stack_user_init_invalid_value); |
| dummy_test(test_stack_user_push_null); |
| dummy_test(test_stack_user_pop_null); |
| dummy_test(test_stack_user_pop_permission); |
| #endif /* CONFIG_USERSPACE */ |
| |
| /* entry of contexts */ |
| static void tIsr_entry_push(const void *p) |
| { |
| uint32_t i; |
| |
| /* Push items to stack */ |
| for (i = 0U; i < STACK_LEN; i++) { |
| k_stack_push((struct k_stack *)p, data_isr[i]); |
| } |
| } |
| |
| static void tIsr_entry_pop(const void *p) |
| { |
| uint32_t i; |
| |
| /* Pop items from stack */ |
| for (i = 0U; i < STACK_LEN; i++) { |
| if (p == &stack1) { |
| k_stack_pop((struct k_stack *)p, &data1[i], K_NO_WAIT); |
| } else { |
| k_stack_pop((struct k_stack *)p, &data2[i], K_NO_WAIT); |
| } |
| } |
| } |
| |
| static void thread_entry_fn_single(void *p1, void *p2, void *p3) |
| { |
| stack_data_t tmp[STACK_LEN]; |
| uint32_t i; |
| |
| /* Pop items from stack */ |
| for (i = STACK_LEN; i; i--) { |
| k_stack_pop((struct k_stack *)p1, &tmp[i - 1], K_NO_WAIT); |
| } |
| zassert_false(memcmp(tmp, data1, sizeof(tmp)), |
| "Push & Pop items does not match"); |
| |
| /* Push items from stack */ |
| for (i = 0U; i < STACK_LEN; i++) { |
| k_stack_push((struct k_stack *)p1, data2[i]); |
| } |
| |
| /* Give control back to Test thread */ |
| k_sem_give(&end_sema); |
| } |
| |
| static void thread_entry_fn_dual(void *p1, void *p2, void *p3) |
| { |
| stack_data_t tmp[STACK_LEN]; |
| uint32_t i; |
| |
| for (i = 0U; i < STACK_LEN; i++) { |
| /* Pop items from stack2 */ |
| k_stack_pop(p2, &tmp[i], K_FOREVER); |
| |
| /* Push items to stack1 */ |
| k_stack_push(p1, data1[i]); |
| |
| } |
| zassert_false(memcmp(tmp, data2, sizeof(tmp)), |
| "Push & Pop items does not match"); |
| } |
| |
| static void thread_entry_fn_isr(void *p1, void *p2, void *p3) |
| { |
| /* Pop items from stack2 */ |
| irq_offload(tIsr_entry_pop, (const void *)p2); |
| zassert_false(memcmp(data_isr, data2, sizeof(data_isr)), |
| "Push & Pop items does not match"); |
| |
| /* Push items to stack1 */ |
| irq_offload(tIsr_entry_push, (const void *)p1); |
| |
| /* Give control back to Test thread */ |
| k_sem_give(&end_sema); |
| } |
| |
| /** |
| * @addtogroup kernel_stack_tests |
| * @{ |
| */ |
| |
| /** |
| * @brief Verify data passing between threads using single stack |
| * @see k_stack_push(), #K_STACK_DEFINE(x), k_stack_pop() |
| */ |
| static void test_single_stack_play(void) |
| { |
| stack_data_t tmp[STACK_LEN]; |
| uint32_t i; |
| |
| /* Init kernel objects */ |
| k_sem_init(&end_sema, 0, 1); |
| |
| /* Push items to stack */ |
| for (i = 0U; i < STACK_LEN; i++) { |
| k_stack_push(&stack1, data1[i]); |
| } |
| |
| k_tid_t tid = k_thread_create(&thread_data, threadstack, TSTACK_SIZE, |
| thread_entry_fn_single, &stack1, NULL, |
| NULL, K_PRIO_PREEMPT(0), K_USER | |
| K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* Let the child thread run */ |
| k_sem_take(&end_sema, K_FOREVER); |
| |
| /* Pop items from stack */ |
| for (i = STACK_LEN; i; i--) { |
| k_stack_pop(&stack1, &tmp[i - 1], K_NO_WAIT); |
| } |
| |
| zassert_false(memcmp(tmp, data2, sizeof(tmp)), |
| "Push & Pop items does not match"); |
| |
| /* Clear the spawn thread to avoid side effect */ |
| k_thread_abort(tid); |
| } |
| |
| /** |
| * @brief Verify data passing between threads using dual stack |
| * @see k_stack_push(), #K_STACK_DEFINE(x), k_stack_pop() |
| */ |
| static void test_dual_stack_play(void) |
| { |
| stack_data_t tmp[STACK_LEN]; |
| uint32_t i; |
| |
| k_tid_t tid = k_thread_create(&thread_data, threadstack, TSTACK_SIZE, |
| thread_entry_fn_dual, &stack1, &stack2, |
| NULL, K_PRIO_PREEMPT(0), K_USER | |
| K_INHERIT_PERMS, K_NO_WAIT); |
| |
| for (i = 0U; i < STACK_LEN; i++) { |
| /* Push items to stack2 */ |
| k_stack_push(&stack2, data2[i]); |
| |
| /* Pop items from stack1 */ |
| k_stack_pop(&stack1, &tmp[i], K_FOREVER); |
| } |
| |
| zassert_false(memcmp(tmp, data1, sizeof(tmp)), |
| "Push & Pop items does not match"); |
| |
| /* Clear the spawn thread to avoid side effect */ |
| k_thread_abort(tid); |
| } |
| |
| /** |
| * @brief Verify data passing between thread and ISR |
| * @see k_stack_push(), #K_STACK_DEFINE(x), k_stack_pop() |
| */ |
| static void test_isr_stack_play(void) |
| { |
| /* Init kernel objects */ |
| k_sem_init(&end_sema, 0, 1); |
| |
| k_tid_t tid = k_thread_create(&thread_data, threadstack, TSTACK_SIZE, |
| thread_entry_fn_isr, &stack1, &stack2, |
| NULL, K_PRIO_PREEMPT(0), |
| K_INHERIT_PERMS, K_NO_WAIT); |
| |
| |
| /* Push items to stack2 */ |
| irq_offload(tIsr_entry_push, (const void *)&stack2); |
| |
| /* Let the child thread run */ |
| k_sem_take(&end_sema, K_FOREVER); |
| |
| /* Pop items from stack1 */ |
| irq_offload(tIsr_entry_pop, (const void *)&stack1); |
| |
| zassert_false(memcmp(data_isr, data1, sizeof(data_isr)), |
| "Push & Pop items does not match"); |
| |
| /* Clear the spawn thread to avoid side effect */ |
| k_thread_abort(tid); |
| } |
| |
| /* the thread entry */ |
| void thread_entry_wait(void *p1, void *p2, void *p3) |
| { |
| stack_data_t *txdata = p3; |
| |
| k_stack_push(p1, *(txdata + 2)); |
| k_stack_push(p1, *(txdata + 3)); |
| } |
| |
| /** |
| * @brief Test that the stack pop can be waited |
| * if no item available |
| * |
| * @details Create and initialize a new stack |
| * Set two timeout parameters to indicate |
| * the maximum amount of time the thread will wait. |
| * |
| * @ingroup kernel_stack_tests |
| * |
| * @see k_stack_push(), #K_STACK_DEFINE(x), k_stack_pop() |
| */ |
| void test_stack_pop_can_wait(void) |
| { |
| struct k_stack stack3; |
| stack_data_t tx_data[STACK_LEN] = { 0xaa, 0xbb, 0xcc, 0xdd }; |
| stack_data_t rx_data[STACK_LEN] = { 0 }; |
| |
| k_stack_alloc_init(&stack3, 2); |
| k_tid_t tid = k_thread_create(&thread_data, threadstack, |
| TSTACK_SIZE, thread_entry_wait, &stack3, |
| NULL, tx_data, K_PRIO_PREEMPT(0), 0, |
| K_NO_WAIT); |
| |
| for (int i = 0; i < 2; i++) { |
| k_stack_push(&stack3, tx_data[i]); |
| } |
| |
| for (int i = 0; i < 3; i++) { |
| k_stack_pop(&stack3, &rx_data[i], K_FOREVER); |
| } |
| |
| zassert_true(rx_data[2] == tx_data[2], "wait forever and pop failed\n"); |
| k_stack_pop(&stack3, &rx_data[3], K_MSEC(50)); |
| zassert_true(rx_data[3] == tx_data[3], "Wait maximum time pop failed\n"); |
| /* Clear the spawn thread to avoid side effect */ |
| k_thread_abort(tid); |
| /*free the buffer allocated*/ |
| k_stack_cleanup(&stack3); |
| } |
| |
| /** |
| * @} |
| */ |
| |
| extern struct k_stack threadstack1; |
| extern struct k_thread thread_data1; |
| extern struct k_sem end_sema1; |
| |
| /*test case main entry*/ |
| void test_main(void) |
| { |
| k_thread_access_grant(k_current_get(), &stack1, &stack2, &thread_data, |
| &end_sema, &threadstack, &kstack, &stack, &thread_data1, |
| &end_sema1, &threadstack1); |
| |
| k_thread_heap_assign(k_current_get(), &test_pool); |
| |
| ztest_test_suite(test_stack_usage, |
| ztest_unit_test(test_stack_thread2thread), |
| ztest_user_unit_test(test_stack_user_thread2thread), |
| ztest_unit_test(test_stack_thread2isr), |
| ztest_unit_test(test_stack_pop_fail), |
| ztest_unit_test(test_stack_multithread_competition), |
| ztest_unit_test(test_stack_cleanup_error), |
| ztest_unit_test(test_stack_push_full), |
| ztest_user_unit_test(test_stack_user_pop_fail), |
| ztest_user_unit_test(test_stack_user_init_null), |
| ztest_user_unit_test(test_stack_user_init_invalid_value), |
| ztest_user_unit_test(test_stack_user_push_null), |
| ztest_user_unit_test(test_stack_user_pop_null), |
| ztest_user_unit_test(test_stack_user_pop_permission), |
| ztest_unit_test(test_stack_alloc_thread2thread), |
| ztest_unit_test(test_stack_alloc_null), |
| ztest_user_unit_test(test_single_stack_play), |
| ztest_1cpu_user_unit_test(test_dual_stack_play), |
| ztest_1cpu_unit_test(test_isr_stack_play), |
| ztest_unit_test(test_stack_pop_can_wait)); |
| ztest_run_test_suite(test_stack_usage); |
| } |