/* task.c - test microkernel task APIs */ /* * Copyright (c) 2012-2014 Wind River Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* DESCRIPTION This module tests the following task APIs: isr_task_id_get(), isr_task_priority_get(), task_id_get(), task_priority_get(), task_resume(), task_suspend(), task_priority_set(), task_sleep(), task_yield() */ #include #include #include #include #include #define RT_PRIO 10 /* RegressionTask prio - must match prj.mdef */ #define HT_PRIO 20 /* HelperTask prio - must match prj.mdef */ #define SLEEP_TIME SECONDS(1) #define CMD_TASKID 0 #define CMD_PRIORITY 1 typedef struct { int cmd; int data; } ISR_INFO; static ISR_INFO isrInfo; static int tcRC = TC_PASS; /* test case return code */ static int helperData; static volatile int is_main_task_ready = 0; #ifdef TEST_PRIV_TASKS /* Note this is in reverse order of what is defined under * test_task/prj.mdef. This is due to compiler filling linker * section like a stack. This is to preserve the same order * in memory as test_task. */ DEFINE_TASK(RT_TASKID, 10, RegressionTask, 2048, EXE); DEFINE_TASK(HT_TASKID, 20, HelperTask, 2048, EXE); #endif /** * * @brief ISR handler to call isr_task_id_get() and isr_task_priority_get() * * @return N/A */ void isr_task_command_handler(void *data) { ISR_INFO *pInfo = (ISR_INFO *) data; int value = -1; switch (pInfo->cmd) { case CMD_TASKID: value = isr_task_id_get(); break; case CMD_PRIORITY: value = isr_task_priority_get(); break; } pInfo->data = value; } /** * * @brief Test isr_task_id_get() and isr_task_priority_get * * @return TC_PASS on success, TC_FAIL on failure */ int isrAPIsTest(int taskId, int taskPrio) { isrInfo.cmd = CMD_TASKID; irq_offload(isr_task_command_handler, &isrInfo); if (isrInfo.data != taskId) { TC_ERROR("isr_task_id_get() returned %d, not %d\n", isrInfo.data, taskId); return TC_FAIL; } isrInfo.cmd = CMD_PRIORITY; irq_offload(isr_task_command_handler, &isrInfo); if (isrInfo.data != taskPrio) { TC_ERROR("isr_task_priority_get() returned %d, not %d\n", isrInfo.data, taskPrio); return TC_FAIL; } return TC_PASS; } /** * * @brief Test task_id_get() and task_priority_get() macros * * @return TC_PASS on success, TC_FAIL on failure */ int taskMacrosTest(int taskId, int taskPrio) { int value; value = task_id_get(); if (value != taskId) { TC_ERROR("task_id_get() returned 0x%x, not 0x%x\n", value, taskId); return TC_FAIL; } value = task_priority_get(); if (value != taskPrio) { TC_ERROR("task_priority_get() returned %d, not %d\n", value, taskPrio); return TC_FAIL; } return TC_PASS; } /** * * @brief Helper task portion to test setting the priority * * @return N/A */ void helperTaskSetPrioTest(void) { task_sem_take(HT_SEM, TICKS_UNLIMITED); helperData = task_priority_get(); /* Helper task priority lowered by 5 */ task_sem_give(RT_SEM); task_sem_take(HT_SEM, TICKS_UNLIMITED); helperData = task_priority_get(); /* Helper task prioirty raised by 10 */ task_sem_give(RT_SEM); task_sem_take(HT_SEM, TICKS_UNLIMITED); helperData = task_priority_get(); /* Helper task prioirty restored */ task_sem_give(RT_SEM); } /** * * @brief Test the task_priority_set() API * * @return N/A */ int taskSetPrioTest(void) { int rv; /* Lower the priority of the current task (RegressionTask) */ task_priority_set(RT_TASKID, RT_PRIO + 5); rv = task_priority_get(); if (rv != RT_PRIO + 5) { TC_ERROR("Expected priority to be changed to %d, not %d\n", RT_PRIO + 5, rv); return TC_FAIL; } /* Raise the priority of the current task (RegressionTask) */ task_priority_set(RT_TASKID, RT_PRIO - 5); rv = task_priority_get(); if (rv != RT_PRIO - 5) { TC_ERROR("Expected priority to be changed to %d, not %d\n", RT_PRIO - 5, rv); return TC_FAIL; } /* Restore the priority of the current task (RegressionTask) */ task_priority_set(RT_TASKID, RT_PRIO); rv = task_priority_get(); if (rv != RT_PRIO) { TC_ERROR("Expected priority to be changed to %d, not %d\n", RT_PRIO, rv); return TC_FAIL; } /* Lower the priority of the helper task (HelperTask) */ task_priority_set(HT_TASKID, HT_PRIO + 5); task_sem_give(HT_SEM); task_sem_take(RT_SEM, TICKS_UNLIMITED); if (helperData != HT_PRIO + 5) { TC_ERROR("Expected priority to be changed to %d, not %d\n", HT_PRIO + 5, helperData); return TC_FAIL; } /* Raise the priority of the helper task (HelperTask) */ task_priority_set(HT_TASKID, HT_PRIO - 5); task_sem_give(HT_SEM); task_sem_take(RT_SEM, TICKS_UNLIMITED); if (helperData != HT_PRIO - 5) { TC_ERROR("Expected priority to be changed to %d, not %d\n", HT_PRIO - 5, helperData); return TC_FAIL; } /* Restore the priority of the helper task (HelperTask) */ task_priority_set(HT_TASKID, HT_PRIO); task_sem_give(HT_SEM); task_sem_take(RT_SEM, TICKS_UNLIMITED); if (helperData != HT_PRIO) { TC_ERROR("Expected priority to be changed to %d, not %d\n", HT_PRIO, helperData); return TC_FAIL; } return TC_PASS; } /** * * @brief Helper task portion to test task_sleep() * * @return N/A */ void helperTaskSleepTest(void) { int32_t firstTick; task_sem_take(HT_SEM, TICKS_UNLIMITED); firstTick = sys_tick_get_32(); while (!is_main_task_ready) { /* busy work */ } helperData = sys_tick_get_32() - firstTick; task_sem_give(RT_SEM); } /** * * @brief Test task_sleep() * * @return TC_PASS on success, TC_FAIL on failure */ int taskSleepTest(void) { int32_t tick; task_sem_give(HT_SEM); /* align on tick boundary and get current tick */ tick = sys_tick_get_32(); while (tick == sys_tick_get_32()) { } /* compensate for the extra tick we just waited */ ++tick; task_sleep(SLEEP_TIME); tick = sys_tick_get_32() - tick; is_main_task_ready = 1; task_sem_take(RT_SEM, TICKS_UNLIMITED); /* * By design this should be exact, but at least one cycle of * slop is required experimentally on Qemu. */ if (tick < SLEEP_TIME || tick > SLEEP_TIME + 1) { TC_ERROR("task_sleep() slept for %d ticks, not %d\n", tick, SLEEP_TIME); return TC_FAIL; } /* * Similarly check that the helper task ran for approximately * SLEEP_TIME. On QEMU, when the host CPU is overloaded, it * has been observed that the tick count can be missed by 1 on * either side. Allow for 2 ticks to be sure. This check is * only there to make sure that the helper task did run for * approximately the whole time the main task was sleeping. */ const int tick_error_allowed = 2; if (helperData > SLEEP_TIME + tick_error_allowed || helperData < SLEEP_TIME - tick_error_allowed) { TC_ERROR("helper task should have run for around %d ticks " "(+/-%d), but ran for %d ticks\n", SLEEP_TIME, tick_error_allowed, helperData); return TC_FAIL; } return TC_PASS; } /** * * @brief Helper task portion of task_yield() test * * @return N/A */ void helperTaskYieldTest(void) { int i; task_sem_take(HT_SEM, TICKS_UNLIMITED); for (i = 0; i < 5; i++) { helperData++; task_yield(); } task_sem_give(RT_SEM); } /** * * @brief Test task_yield() * * @return TC_PASS on success, TC_FAIL on failure */ int taskYieldTest(void) { int prevHelperData; int i; helperData = 0; /* 1st raise the priority of the helper task */ task_priority_set(HT_TASKID, RT_PRIO); task_sem_give(HT_SEM); for (i = 0; i < 5; i++) { prevHelperData = helperData; task_yield(); if (helperData == prevHelperData) { TC_ERROR("Iter %d. helperData did not change (%d)\n", i + 1, helperData); return TC_FAIL; } } /* Restore helper task priority */ task_priority_set(HT_TASKID, HT_PRIO); /* Ensure that the helper task finishes */ task_sem_take(RT_SEM, TICKS_UNLIMITED); return TC_PASS; } /** * * @brief Helper task portion of task_suspend() and * task_resume() tests * * @return N/A */ void helperTaskSuspendTest(void) { helperData++; task_sem_take(HT_SEM, TICKS_UNLIMITED); } /** * * @brief Test task_suspend() and task_resume() * * This test suspends the helper task. Once it is suspended, the main task * (RegressionTask) sleeps for one second. If the helper task is truly * suspended, it will not execute and modify . Once confirmed, * the helper task is resumed, and the main task sleeps once more. If the * helper task has truly resumed, it will modify . * * @return TC_PASS on success or TC_FAIL on failure */ int taskSuspendTest(void) { int prevHelperData; task_suspend(HT_TASKID); /* Suspend the helper task */ prevHelperData = helperData; task_sleep(SLEEP_TIME); if (prevHelperData != helperData) { TC_ERROR("Helper task did not suspend!\n"); return TC_FAIL; } task_resume(HT_TASKID); task_sleep(SLEEP_TIME); if (prevHelperData == helperData) { TC_ERROR("Helper task did not resume!\n"); return TC_FAIL; } task_sem_give(HT_SEM); return TC_PASS; } /** * * @brief Helper task to test the task APIs * * @return N/A */ void HelperTask(void) { int rv; task_sem_take(HT_SEM, TICKS_UNLIMITED); rv = isrAPIsTest(HT_TASKID, HT_PRIO); if (rv != TC_PASS) { tcRC = TC_FAIL; return; } task_sem_give(RT_SEM); task_sem_take(HT_SEM, TICKS_UNLIMITED); rv = taskMacrosTest(HT_TASKID, HT_PRIO); if (rv != TC_PASS) { tcRC = TC_FAIL; return; } task_sem_give(RT_SEM); helperTaskSetPrioTest(); helperTaskSleepTest(); helperTaskYieldTest(); helperTaskSuspendTest(); } /** * * @brief Main task to test the task APIs * * @return N/A */ void RegressionTask(void) { int rv; TC_START("Test Microkernel Task API"); PRINT_LINE; task_start(HT_TASKID); TC_PRINT("Testing isr_task_id_get() and isr_task_priority_get()\n"); rv = isrAPIsTest(RT_TASKID, RT_PRIO); if (rv != TC_PASS) { tcRC = TC_FAIL; goto errorReturn; } task_sem_give(HT_SEM); task_sem_take(RT_SEM, TICKS_UNLIMITED); TC_PRINT("Testing task_id_get() and task_priority_get()\n"); rv = taskMacrosTest(RT_TASKID, RT_PRIO); if (rv != TC_PASS) { tcRC = TC_FAIL; goto errorReturn; } task_sem_give(HT_SEM); task_sem_take(RT_SEM, TICKS_UNLIMITED); TC_PRINT("Testing task_priority_set()\n"); if (taskSetPrioTest() != TC_PASS) { tcRC = TC_FAIL; goto errorReturn; } TC_PRINT("Testing task_sleep()\n"); if (taskSleepTest() != TC_PASS) { tcRC = TC_FAIL; goto errorReturn; } TC_PRINT("Testing task_yield()\n"); if (taskYieldTest() != TC_PASS) { tcRC = TC_FAIL; goto errorReturn; } TC_PRINT("Testing task_suspend() and task_resume()\n"); if (taskSuspendTest() != TC_PASS) { tcRC = TC_FAIL; goto errorReturn; } errorReturn: TC_END_RESULT(tcRC); TC_END_REPORT(tcRC); } /* RegressionTask */