blob: 796ff8ae5f0a141d6258b23f7d631b6d90554a71 [file] [log] [blame]
/* task.c - test microkernel task APIs */
/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
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 <tc_util.h>
#include <zephyr.h>
#include <arch/cpu.h>
#include <irq_offload.h>
#include <util_test_common.h>
#define RT_PRIO 5 /* RegressionTask prio - must match prj.mdef */
#define HT_PRIO 10 /* 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
DEFINE_TASK(HT_TASKID, HT_PRIO, HelperTask, 2048, NULL);
DEFINE_TASK(RT_TASKID, RT_PRIO, RegressionTask, 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 = (int)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(ktask_t taskId, kpriority_t taskPrio)
{
isrInfo.cmd = CMD_TASKID;
irq_offload(isr_task_command_handler, &isrInfo);
if (isrInfo.data != (int)taskId) {
TC_ERROR("isr_task_id_get() returned %d, not %d\n",
isrInfo.data, (int)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(ktask_t taskId, int taskPrio)
{
ktask_t taskIdValue;
kpriority_t taskPrioValue;
taskIdValue = task_id_get();
if (taskIdValue != taskId) {
TC_ERROR("task_id_get() returned 0x%x, not 0x%x\n",
(uint32_t)taskIdValue, (uint32_t)taskId);
return TC_FAIL;
}
taskPrioValue = task_priority_get();
if (taskPrioValue != taskPrio) {
TC_ERROR("task_priority_get() returned %d, not %d\n",
taskPrioValue, 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 + 2);
rv = task_priority_get();
if (rv != RT_PRIO + 2) {
TC_ERROR("Expected priority to be changed to %d, not %d\n",
RT_PRIO + 2, rv);
return TC_FAIL;
}
/* Raise the priority of the current task (RegressionTask) */
task_priority_set(RT_TASKID, RT_PRIO - 2);
rv = task_priority_get();
if (rv != RT_PRIO - 2) {
TC_ERROR("Expected priority to be changed to %d, not %d\n",
RT_PRIO - 2, 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 + 2);
task_sem_give(HT_SEM);
task_sem_take(RT_SEM, TICKS_UNLIMITED);
if (helperData != HT_PRIO + 2) {
TC_ERROR("Expected priority to be changed to %d, not %d\n",
HT_PRIO + 2, helperData);
return TC_FAIL;
}
/* Raise the priority of the helper task (HelperTask) */
task_priority_set(HT_TASKID, HT_PRIO - 2);
task_sem_give(HT_SEM);
task_sem_take(RT_SEM, TICKS_UNLIMITED);
if (helperData != HT_PRIO - 2) {
TC_ERROR("Expected priority to be changed to %d, not %d\n",
HT_PRIO - 2, 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 <helperData>. Once confirmed,
* the helper task is resumed, and the main task sleeps once more. If the
* helper task has truly resumed, it will modify <helperData>.
*
* @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 */