blob: 3518c145bf7d5a96fb7f2b63b9464fbbf2155145 [file] [log] [blame]
/*
* Copyright (c) 2016 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.
*/
#include <tc_util.h>
#include <zephyr.h>
#include <nano_private.h>
#include <stdbool.h>
#define SECONDS(x) ((x) * sys_clock_ticks_per_sec)
#define HALF_SECOND (sys_clock_ticks_per_sec / 2)
#define THIRD_SECOND (sys_clock_ticks_per_sec / 3)
#define FOURTH_SECOND (sys_clock_ticks_per_sec / 4)
#define FIBER_STACKSIZE 1024
#define FIFO_TEST_START 10
#define FIFO_TEST_END 20
#define SEM_TEST_START 30
#define SEM_TEST_END 40
#define LIFO_TEST_START 50
#define LIFO_TEST_END 60
#define NON_NULL_PTR ((void *)0x12345678)
struct fifo_data {
uint32_t reserved;
uint32_t data;
};
struct lifo_data {
uint32_t reserved;
uint32_t data;
};
static char __stack fiber_stack[2][FIBER_STACKSIZE];
static struct nano_fifo fifo;
static struct nano_lifo lifo;
static struct nano_timer timer;
static struct nano_sem start_test_sem;
static struct nano_sem sync_test_sem;
static struct nano_sem end_test_sem;
struct fifo_data fifo_test_data[4] = {
{0, FIFO_TEST_END + 1}, {0, FIFO_TEST_END + 2},
{0, FIFO_TEST_END + 3}, {0, FIFO_TEST_END + 4}
};
struct lifo_data lifo_test_data[4] = {
{0, LIFO_TEST_END + 1}, {0, LIFO_TEST_END + 2},
{0, LIFO_TEST_END + 3}, {0, LIFO_TEST_END + 4}
};
static uint32_t timer_start_tick;
static uint32_t timer_end_tick;
static void *timer_data;
static int __noinit fiber_high_state;
static int __noinit fiber_low_state;
static int __noinit task_high_state;
static int __noinit task_low_state;
static int __noinit counter;
static int increment_counter(void)
{
int tmp;
int key = irq_lock();
tmp = ++counter;
irq_unlock(key);
return tmp;
}
static int sync_threads(void *arg)
{
struct nano_sem *sem = arg;
nano_fiber_sem_give(sem);
nano_fiber_sem_give(sem);
nano_fiber_sem_give(sem);
nano_fiber_sem_give(sem);
return 0;
}
static void fifo_tests(int32_t timeout, volatile int *state,
void *(*get)(struct nano_fifo *, int32_t),
int (*sem_take)(struct nano_sem *, int32_t))
{
struct fifo_data *data;
sem_take(&start_test_sem, TICKS_UNLIMITED);
*state = FIFO_TEST_START;
/* Expect this to time out */
data = get(&fifo, timeout);
if (data != NULL) {
TC_ERROR("**** Unexpected data on FIFO get\n");
return;
}
*state = increment_counter();
/* Sync up fifo test threads */
sem_take(&sync_test_sem, TICKS_UNLIMITED);
/* Expect this to receive data from the fifo */
*state = FIFO_TEST_END;
data = get(&fifo, timeout);
if (data == NULL) {
TC_ERROR("**** No data on FIFO get\n");
return;
}
*state = increment_counter();
if (data->data != *state) {
TC_ERROR("**** Got FIFO data %d, not %d (%d)\n",
data->data, *state, timeout);
return;
}
sem_take(&end_test_sem, TICKS_UNLIMITED);
}
static void lifo_tests(int32_t timeout, volatile int *state,
void *(*get)(struct nano_lifo *, int32_t),
int (*sem_take)(struct nano_sem *, int32_t))
{
struct lifo_data *data;
sem_take(&start_test_sem, TICKS_UNLIMITED);
*state = LIFO_TEST_START;
/* Expect this to time out */
data = get(&lifo, timeout);
if (data != NULL) {
TC_ERROR("**** Unexpected data on LIFO get\n");
return;
}
*state = increment_counter();
/* Sync up all threads */
sem_take(&sync_test_sem, TICKS_UNLIMITED);
/* Expect this to receive data from the lifo */
*state = LIFO_TEST_END;
data = get(&lifo, timeout);
if (data == NULL) {
TC_ERROR("**** No data on LIFO get\n");
return;
}
*state = increment_counter();
if (data->data != *state) {
TC_ERROR("**** Got LIFO data %d, not %d (%d)\n",
data->data, *state, timeout);
return;
}
sem_take(&end_test_sem, TICKS_UNLIMITED);
}
static void timer_tests(void)
{
nano_task_sem_take(&start_test_sem, TICKS_UNLIMITED);
timer_start_tick = sys_tick_get_32();
nano_task_timer_start(&timer, SECONDS(1));
timer_data = nano_task_timer_test(&timer, TICKS_UNLIMITED);
timer_end_tick = sys_tick_get_32();
nano_task_sem_take(&end_test_sem, TICKS_UNLIMITED);
}
static void fiber_high(int arg1, int arg2)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
fifo_tests(SECONDS(1), &fiber_high_state,
nano_fiber_fifo_get, nano_fiber_sem_take);
lifo_tests(SECONDS(1), &fiber_high_state,
nano_fiber_lifo_get, nano_fiber_sem_take);
}
static void fiber_low(int arg1, int arg2)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
fifo_tests(HALF_SECOND, &fiber_low_state,
nano_fiber_fifo_get, nano_fiber_sem_take);
lifo_tests(HALF_SECOND, &fiber_low_state,
nano_fiber_lifo_get, nano_fiber_sem_take);
}
void task_high(void)
{
TC_START("Test Microkernel Tasks Pending on Nanokernel Objects");
nano_fifo_init(&fifo);
nano_lifo_init(&lifo);
nano_timer_init(&timer, NON_NULL_PTR);
nano_sem_init(&start_test_sem);
nano_sem_init(&sync_test_sem);
nano_sem_init(&end_test_sem);
counter = SEM_TEST_START;
task_fiber_start(fiber_stack[0], FIBER_STACKSIZE,
fiber_high, 0, 0, 3, 0);
task_fiber_start(fiber_stack[1], FIBER_STACKSIZE,
fiber_low, 0, 0, 7, 0);
counter = FIFO_TEST_START;
fifo_tests(THIRD_SECOND, &task_high_state,
nano_task_fifo_get, nano_task_sem_take);
counter = LIFO_TEST_START;
lifo_tests(THIRD_SECOND, &task_high_state,
nano_task_lifo_get, nano_task_sem_take);
timer_tests();
}
void task_low(void)
{
fifo_tests(FOURTH_SECOND, &task_low_state,
nano_task_fifo_get, nano_task_sem_take);
lifo_tests(FOURTH_SECOND, &task_low_state,
nano_task_lifo_get, nano_task_sem_take);
}
void task_monitor(void)
{
int result = TC_FAIL;
task_offload_to_fiber(sync_threads, &start_test_sem);
/*
* Verify that microkernel tasks 'task_high' and 'task_low' do not
* busy-wait. If they are not busy-waiting, then they must be pending.
*/
TC_PRINT("Testing microkernel tasks block on nanokernel fifos ...\n");
if ((fiber_high_state != FIFO_TEST_START) ||
(fiber_low_state != FIFO_TEST_START) ||
(task_high_state != FIFO_TEST_START) ||
(task_low_state != FIFO_TEST_START)) {
goto error;
}
/* Give waiting threads time to time-out */
task_sleep(SECONDS(2));
/* Verify that the fibers and tasks timed-out in the correct order. */
TC_PRINT("Testing nanokernel fifos time-out in correct order ...\n");
if ((task_low_state != FIFO_TEST_START + 1) ||
(task_high_state != FIFO_TEST_START + 2) ||
(fiber_low_state != FIFO_TEST_START + 3) ||
(fiber_high_state != FIFO_TEST_START + 4)) {
TC_ERROR("**** Threads timed-out in unexpected order\n");
goto error;
}
counter = FIFO_TEST_END;
task_offload_to_fiber(sync_threads, &sync_test_sem);
/* Two fibers and two tasks should be waiting on the FIFO */
/* Add data to the FIFO */
TC_PRINT("Testing nanokernel fifos delivered data correctly ...\n");
nano_task_fifo_put(&fifo, &fifo_test_data[0]);
nano_task_fifo_put(&fifo, &fifo_test_data[1]);
nano_task_fifo_put(&fifo, &fifo_test_data[2]);
nano_task_fifo_put(&fifo, &fifo_test_data[3]);
if ((fiber_high_state != FIFO_TEST_END + 1) ||
(fiber_low_state != FIFO_TEST_END + 2) ||
(task_high_state != FIFO_TEST_END + 3) ||
(task_low_state != FIFO_TEST_END + 4)) {
TC_ERROR("**** Unexpected delivery order\n");
goto error;
}
task_offload_to_fiber(sync_threads, &end_test_sem);
/* ******************************************************** */
task_offload_to_fiber(sync_threads, &start_test_sem);
/*
* Verify that microkernel tasks 'task_high' and 'task_low' do not
* busy-wait. If they are not busy-waiting, then they must be pending.
*/
TC_PRINT("Testing microkernel tasks block on nanokernel lifos ...\n");
if ((fiber_high_state != LIFO_TEST_START) ||
(fiber_low_state != LIFO_TEST_START) ||
(task_high_state != LIFO_TEST_START) ||
(task_low_state != LIFO_TEST_START)) {
goto error;
}
/* Give waiting threads time to time-out */
task_sleep(SECONDS(2));
TC_PRINT("Testing nanokernel lifos time-out in correct order ...\n");
if ((task_low_state != LIFO_TEST_START + 1) ||
(task_high_state != LIFO_TEST_START + 2) ||
(fiber_low_state != LIFO_TEST_START + 3) ||
(fiber_high_state != LIFO_TEST_START + 4)) {
TC_ERROR("**** Threads timed-out in unexpected order\n");
goto error;
}
counter = LIFO_TEST_END;
task_offload_to_fiber(sync_threads, &sync_test_sem);
/* Two fibers and two tasks should be waiting on the LIFO */
/* Add data to the LIFO */
nano_task_lifo_put(&lifo, &lifo_test_data[0]);
nano_task_lifo_put(&lifo, &lifo_test_data[1]);
nano_task_lifo_put(&lifo, &lifo_test_data[2]);
nano_task_lifo_put(&lifo, &lifo_test_data[3]);
TC_PRINT("Testing nanokernel lifos delivered data correctly ...\n");
if ((fiber_high_state != LIFO_TEST_END + 1) ||
(fiber_low_state != LIFO_TEST_END + 2) ||
(task_high_state != LIFO_TEST_END + 3) ||
(task_low_state != LIFO_TEST_END + 4)) {
TC_ERROR("**** Unexpected timeout order\n");
goto error;
}
task_offload_to_fiber(sync_threads, &end_test_sem);
/* ******************************************************** */
timer_end_tick = 0;
nano_task_sem_give(&start_test_sem); /* start timer tests */
/*
* NOTE: The timer test is running in the context of high_task().
* Scheduling is expected to yield to high_task(). If high_task()
* does not pend as expected, then timer_end_tick will be non-zero.
*/
TC_PRINT("Testing microkernel task waiting on nanokernel timer ...\n");
if (timer_end_tick != 0) {
TC_ERROR("Task did not pend on nanokernel timer\n");
goto error;
}
/* Let the timer expire */
task_sleep(SECONDS(2));
if (timer_end_tick < timer_start_tick + SECONDS(1)) {
TC_ERROR("Task waiting on a nanokernel timer error\n");
goto error;
}
if (timer_data != NON_NULL_PTR) {
TC_ERROR("Incorrect data from nanokernel timer\n");
goto error;
}
nano_task_sem_give(&end_test_sem);
result = TC_PASS;
error:
TC_END_RESULT(result);
TC_END_REPORT(result);
}