blob: a94598f93f519e531b51803ec1c33fc3a42c9f05 [file] [log] [blame]
/*
* Copyright (c) 2016 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* @file
* @brief Test early sleeping microkernel mechanism
*
* This test verifies that both fiber_sleep() and task_sleep()
* can each be used to put the calling thread to sleep for a specified
* number of ticks during system initialization (before k_server() starts)
* as well as after the microkernel initializes (after k_server() starts).
*
* To ensure that the nanokernel timeout both operates correctly during
* system initialization and that it allows fibers to sleep for a specified
* number of ticks the test has a fiber invoke fiber_sleep() before the init
* task invokes task_sleep(). The fiber sleep time is less than that of the
* task sleep time so that the fiber will wake before the init task wakes.
*/
#include <zephyr.h>
#include <tc_util.h>
#include <init.h>
#define FIBER_TICKS_TO_SLEEP 40
#define TASK_TICKS_TO_SLEEP 50
/* time that the task was actually sleeping */
static int task_actual_sleep_ticks;
static int task_actual_sleep_nano_ticks;
static int task_actual_sleep_micro_ticks;
static int task_actual_sleep_app_ticks;
/* time that the fiber was actually sleeping */
static volatile int fiber_actual_sleep_ticks;
/*
* Flag is changed by lower priority task to make sure
* that sleeping did not go in a tight toop
*/
static bool alternate_task_run;
/* test fiber synchronization semaphore */
static struct nano_sem test_fiber_sem;
/**
*
* @brief Put task to sleep and measure time it really slept
*
* @param ticks_to_sleep number of ticks for a task to sleep
*
* @return number of ticks the task actually slept
*/
int test_task_sleep(int ticks_to_sleep)
{
uint32_t start_time;
uint32_t stop_time;
start_time = sys_cycle_get_32();
task_sleep(ticks_to_sleep);
stop_time = sys_cycle_get_32();
return (stop_time - start_time) / sys_clock_hw_cycles_per_tick;
}
/**
*
* @brief Put fiber to sleep and measure time it really slept
*
* @param ticks_to_sleep number of ticks for a fiber to sleep
*
* @return number of ticks the fiber actually slept
*/
int test_fiber_sleep(int ticks_to_sleep)
{
uint32_t start_time;
uint32_t stop_time;
start_time = sys_cycle_get_32();
fiber_sleep(ticks_to_sleep);
stop_time = sys_cycle_get_32();
return (stop_time - start_time) / sys_clock_hw_cycles_per_tick;
}
/**
*
* @brief Early task sleep test
*
* Note: it will be used to test the early sleep at SECONDARY level too
*
* Call task_sleep() and checks the time sleep actually
* took to make sure that task actually slept
*
* @return 0
*/
static int test_early_task_sleep(struct device *unused)
{
ARG_UNUSED(unused);
task_actual_sleep_ticks = test_task_sleep(TASK_TICKS_TO_SLEEP);
return 0;
}
SYS_INIT(test_early_task_sleep, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
/**
*
* @brief Early task sleep test in NANOKERNEL level only
*
* Call task_sleep() and checks the time sleep actually
* took to make sure that task actually slept
*
* @return 0
*/
static int test_early_task_sleep_in_nanokernel_level(struct device *unused)
{
ARG_UNUSED(unused);
task_actual_sleep_nano_ticks = test_task_sleep(TASK_TICKS_TO_SLEEP);
return 0;
}
SYS_INIT(test_early_task_sleep_in_nanokernel_level,
NANOKERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
/**
*
* @brief Early task sleep test in MICROKERNEL level only
*
* Call task_sleep() and checks the time sleep actually
* took to make sure that task actually slept
*
* @return 0
*/
static int test_early_task_sleep_in_microkernel_level(struct device *unused)
{
ARG_UNUSED(unused);
task_actual_sleep_micro_ticks = test_task_sleep(TASK_TICKS_TO_SLEEP);
return 0;
}
SYS_INIT(test_early_task_sleep_in_microkernel_level,
MICROKERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
/**
*
* @brief Early task sleep test in APPLICATION level only
*
* Call task_sleep() and checks the time sleep actually
* took to make sure that task actually slept
*
* @return 0
*/
static int test_early_task_sleep_in_application_level(struct device *unused)
{
ARG_UNUSED(unused);
task_actual_sleep_app_ticks = test_task_sleep(TASK_TICKS_TO_SLEEP);
return 0;
}
SYS_INIT(test_early_task_sleep_in_application_level,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
/**
*
* @brief Fiber function that measures fiber sleep time
*
* @return N/A
*/
static void test_fiber(int ticks_to_sleep, int unused)
{
ARG_UNUSED(unused);
while (1) {
fiber_actual_sleep_ticks = test_fiber_sleep(ticks_to_sleep);
fiber_sem_give(TEST_FIBER_SEM);
nano_sem_take(&test_fiber_sem, TICKS_UNLIMITED);
}
}
#define STACKSIZE 512
char __stack test_fiber_stack[STACKSIZE];
/**
*
* @brief Initialize test fiber data
*
* @return 0
*/
static int test_fiber_start(struct device *unused)
{
ARG_UNUSED(unused);
fiber_actual_sleep_ticks = 0;
nano_sem_init(&test_fiber_sem);
task_fiber_start(&test_fiber_stack[0], STACKSIZE,
(nano_fiber_entry_t) test_fiber,
FIBER_TICKS_TO_SLEEP, 0, 7, 0);
return 0;
}
SYS_INIT(test_fiber_start, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
/**
*
* @brief Lower priority task to make sure that main task really sleeps
*
*
*
* @return N/A
*/
void AlternateTask(void)
{
alternate_task_run = true;
}
/**
*
* @brief Regression task
*
* Checks the results of the early sleep
*
* @return N/A
*/
void RegressionTask(void)
{
TC_START("Test early and regular task and fiber sleep functionality\n");
alternate_task_run = false;
TC_PRINT("Test fiber_sleep() call during the system initialization\n");
/*
* Make sure that the fiber_sleep() called during the
* initialization has returned.
* fiber_sleep() invoked during the initialization for the
* shorter period that task_sleep() should return by now.
*/
if (task_sem_take(TEST_FIBER_SEM, TICKS_NONE) != RC_OK) {
TC_ERROR("fiber_sleep() has not returned while expected\n");
}
/*
* Check that the fiber_sleep() called during the system
* initialization put the fiber to sleep for the specified
* amount of time
*
* On heavily loaded systems QEMU may demonstrate a drift
* of hardware clock ticks to system clock. Test verifies
* that sleep took at least not less amount of time.
* Allow up to 1 tick variance as the test may not have put
* the task to sleep on a tick boundary.
*/
if ((fiber_actual_sleep_ticks + 1) < FIBER_TICKS_TO_SLEEP) {
TC_ERROR("fiber_sleep() time is too small: %d\n",
fiber_actual_sleep_ticks);
goto error_out;
}
/*
* Check that the task_sleep() called during the system
* initialization puts the task to sleep for the specified
* amount of time
*/
TC_PRINT("Test task_sleep() call during the system initialization\n");
TC_PRINT("- At SECONDARY level\n");
if ((task_actual_sleep_ticks + 1) < TASK_TICKS_TO_SLEEP) {
TC_ERROR("task_sleep() time is is too small: %d\n",
task_actual_sleep_ticks);
goto error_out;
}
/*
* Check that the task_sleep() called during the system
* initialization at NANOKERNEL level puts the task to sleep for
* the specified amount of time
*/
TC_PRINT("- At NANOKERNEL level\n");
if ((task_actual_sleep_nano_ticks + 1) < TASK_TICKS_TO_SLEEP) {
TC_ERROR("task_sleep() time is is too small: %d\n",
task_actual_sleep_nano_ticks);
goto error_out;
}
/*
* Check that the task_sleep() called during the system
* initialization at MICROKERNEL level puts the task to sleep for
* the specified amount of time
*/
TC_PRINT("- At MICROKERNEL level\n");
if ((task_actual_sleep_micro_ticks + 1) < TASK_TICKS_TO_SLEEP) {
TC_ERROR("task_sleep() time is is too small: %d\n",
task_actual_sleep_micro_ticks);
goto error_out;
}
/*
* Check that the task_sleep() called during the system
* initialization at APPLICATION level puts the task to sleep for
* the specified amount of time
*/
TC_PRINT("- At APPLICATION level\n");
if ((task_actual_sleep_app_ticks + 1) < TASK_TICKS_TO_SLEEP) {
TC_ERROR("task_sleep() time is is too small: %d\n",
task_actual_sleep_app_ticks);
goto error_out;
}
/*
* Check that the task_sleep() called during the normal
* microkernel work put the task to sleep for the specified
* amount of time
*/
TC_PRINT("Test task_sleep() call on a running system\n");
task_actual_sleep_ticks = test_task_sleep(TASK_TICKS_TO_SLEEP);
if ((task_actual_sleep_ticks + 1) < TASK_TICKS_TO_SLEEP) {
TC_ERROR("task_sleep() time is too small: %d\n",
task_actual_sleep_ticks);
goto error_out;
}
/* check that calling task_sleep() allowed the lower priority task run */
if (!alternate_task_run) {
TC_ERROR("Lower priority task did not run during task_sleep()\n");
goto error_out;
}
/*
* Check that the fiber_sleep() called during the normal
* microkernel work put the fiber to sleep for the specified
* amount of time
*/
TC_PRINT("Test fiber_sleep() call on a running system\n");
fiber_actual_sleep_ticks = 0;
nano_sem_give(&test_fiber_sem);
/* wait for the test fiber return from the sleep */
task_sem_take(TEST_FIBER_SEM, TICKS_UNLIMITED);
if ((fiber_actual_sleep_ticks + 1) < FIBER_TICKS_TO_SLEEP) {
TC_ERROR("fiber_sleep() time is too small: %d\n",
fiber_actual_sleep_ticks);
goto error_out;
}
TC_END_RESULT(TC_PASS);
TC_END_REPORT(TC_PASS);
return;
error_out:
TC_END_RESULT(TC_FAIL);
TC_END_REPORT(TC_FAIL);
}