Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2016 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <tc_util.h>
#include <zephyr.h>
#include <kernel_structs.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);
}