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) 2010-2015 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.
 */

/**
 * @brief Nanokernel fixed-size stack object
 *
 * This module provides the nanokernel stack object implementation, including
 * the following APIs:
 *
 *  nano_stack_init
 *  nano_fiber_stack_push, nano_task_stack_push, nano_isr_stack_push
 *  nano_fiber_stack_pop, nano_task_stack_pop, nano_isr_stack_pop
 *
 * @param stack the stack to initialize
 * @param data pointer to the container for the stack
 *
 * @internal
 * In some cases the compiler "alias" attribute is used to map two or more
 * APIs to the same function, since they have identical implementations.
 * @endinternal
 *
 */

#include <nano_private.h>
#include <toolchain.h>
#include <sections.h>

void nano_stack_init(struct nano_stack *stack, uint32_t *data)
{
	stack->next = stack->base = data;
	stack->fiber = (struct tcs *)0;
}

FUNC_ALIAS(_stack_push_non_preemptible, nano_isr_stack_push, void);
FUNC_ALIAS(_stack_push_non_preemptible, nano_fiber_stack_push, void);

/**
 *
 * @brief Push data onto a stack (no context switch)
 *
 * This routine pushes a data item onto a stack object; it may be called from
 * either a fiber or ISR context.  A fiber pending on the stack object will be
 * made ready, but will NOT be scheduled to execute.
 *
 * @param stack Stack on which to interact
 * @param data Data to push on stack
 * @return N/A
 *
 * @internal
 * This function is capable of supporting invocations from both a fiber and an
 * ISR context.  However, the nano_isr_stack_push and nano_fiber_stack_push
 * aliases are created to support any required implementation differences in
 * the future without introducing a source code migration issue.
 * @endinternal
 */
void _stack_push_non_preemptible(struct nano_stack *stack, uint32_t data)
{
	struct tcs *tcs;
	unsigned int imask;

	imask = irq_lock();

	tcs = stack->fiber;
	if (tcs) {
		stack->fiber = 0;
		fiberRtnValueSet(tcs, data);
		_nano_fiber_ready(tcs);
	} else {
		*(stack->next) = data;
		stack->next++;
	}

	irq_unlock(imask);
}


void nano_task_stack_push(struct nano_stack *stack, uint32_t data)
{
	struct tcs *tcs;
	unsigned int imask;

	imask = irq_lock();

	tcs = stack->fiber;
	if (tcs) {
		stack->fiber = 0;
		fiberRtnValueSet(tcs, data);
		_nano_fiber_ready(tcs);
		_Swap(imask);
		return;
	}

	*(stack->next) = data;
	stack->next++;

	irq_unlock(imask);
}

void nano_stack_push(struct nano_stack *stack, uint32_t data)
{
	static void (*func[3])(struct nano_stack *, uint32_t) = {
		nano_isr_stack_push,
		nano_fiber_stack_push,
		nano_task_stack_push
	};

	func[sys_execution_context_type_get()](stack, data);
}

FUNC_ALIAS(_stack_pop, nano_isr_stack_pop, int);
FUNC_ALIAS(_stack_pop, nano_fiber_stack_pop, int);

/**
 *
 * @brief Pop data from a nanokernel stack
 *
 * Pop the first data word from a nanokernel stack object; it may be called
 * from either a fiber or ISR context.
 *
 * If the stack is not empty, a data word is popped and copied to the provided
 * address <pData> and a non-zero value is returned. If the stack is empty,
 * it waits until data is ready.
 *
 * @param stack Stack to operate on
 * @param pData Container for data to pop
 * @param timeout_in_ticks Affects the action taken should the stack be empty.
 * If TICKS_NONE, then return immediately. If TICKS_UNLIMITED, then wait as
 * long as necessary. No other value is currently supported as this routine
 * does not support CONFIG_NANO_TIMEOUTS.
 *
 * @return 1 popped data from the stack; 0 otherwise
 */
int _stack_pop(struct nano_stack *stack, uint32_t *pData, int32_t timeout_in_ticks)
{
	unsigned int imask;

	imask = irq_lock();

	if (likely(stack->next > stack->base)) {
		stack->next--;
		*pData = *(stack->next);
		irq_unlock(imask);
		return 1;
	}

	if (timeout_in_ticks != TICKS_NONE) {
		stack->fiber = _nanokernel.current;
		*pData = (uint32_t) _Swap(imask);
		return 1;
	}

	irq_unlock(imask);
	return 0;
}

int nano_task_stack_pop(struct nano_stack *stack, uint32_t *pData, int32_t timeout_in_ticks)
{
	unsigned int imask;

	imask = irq_lock();

	while (1) {
		/*
		 * Predict that the branch will be taken to break out of the loop.
		 * There is little cost to a misprediction since that leads to idle.
		 */

		if (likely(stack->next > stack->base)) {
			stack->next--;
			*pData = *(stack->next);
			irq_unlock(imask);
			return 1;
		}

		if (timeout_in_ticks == TICKS_NONE) {
			break;
		}

		/*
		 * Invoke nano_cpu_atomic_idle() with interrupts still disabled to
		 * prevent the scenario where an interrupt fires after re-enabling
		 * interrupts and before executing the "halt" instruction.  If the
		 * ISR performs a nano_isr_stack_push() on the same stack object,
		 * the subsequent execution of the "halt" instruction will result
		 * in the queued data being ignored until the next interrupt, if
		 * any.
		 *
		 * Thus it should be clear that an architectures implementation
		 * of nano_cpu_atomic_idle() must be able to atomically re-enable
		 * interrupts and enter a low-power mode.
		 *
		 * This explanation is valid for all nanokernel objects: stacks,
		 * FIFOs, LIFOs, and semaphores, for their
		 * nano_task_<object>_<get>() routines.
		 */

		nano_cpu_atomic_idle(imask);
		imask = irq_lock();
	}

	irq_unlock(imask);
	return 0;
}

int nano_stack_pop(struct nano_stack *stack, uint32_t *pData, int32_t timeout_in_ticks)
{
	static int (*func[3])(struct nano_stack *, uint32_t *, int32_t) = {
		nano_isr_stack_pop,
		nano_fiber_stack_pop,
		nano_task_stack_pop,
	};

	return func[sys_execution_context_type_get()](stack, pData, timeout_in_ticks);
}