atomkernel.c File Reference

#include <stddef.h>
#include "atom.h"

Defines

#define STACK_CHECK_BYTE   0x5A

Functions

void atomSched (uint8_t timer_tick)
uint8_t atomThreadCreate (ATOM_TCB *tcb_ptr, uint8_t priority, void(*entry_point)(uint32_t), uint32_t entry_param, void *stack_top, uint32_t stack_size)
void atomIntEnter (void)
void atomIntExit (uint8_t timer_tick)
ATOM_TCBatomCurrentContext (void)
uint8_t atomOSInit (void *idle_thread_stack_top, uint32_t idle_thread_stack_size)
void atomOSStart (void)
uint8_t tcbEnqueuePriority (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr)
ATOM_TCBtcbDequeueHead (ATOM_TCB **tcb_queue_ptr)
ATOM_TCBtcbDequeueEntry (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr)
ATOM_TCBtcbDequeuePriority (ATOM_TCB **tcb_queue_ptr, uint8_t priority)

Variables

ATOM_TCBtcbReadyQ = NULL
uint8_t atomOSStarted = FALSE

Detailed Description

Kernel library.

This module implements the core kernel functionality of managing threads, context-switching and interrupt handlers. It also contains functions for managing queues of TCBs (task control blocks) which are used not only for the queue of ready threads, but also by other OS primitives (such as semaphores) for generically managing lists of TCBs.

Core kernel functionality such as managing the queue of ready threads and how context-switch decisions are made is described within the code. However a quick summary is as follows:

There is a ready queue of threads. There must always be at least one thread ready-to-run. If no application threads are ready, the internal kernel idle thread will be run. This ensures that there is a thread to run at all times.

Application code creates threads using atomThreadCreate(). These threads are added to the ready queue and eventually run when it is their turn (based on priority). When threads are currently-running they are taken off the ready queue. Threads continue to run until:

Thread scheduling decisions are made by atomSched(). This is called at several times, but should never be called by application code directly:

When a thread reschedule needs to take place, the scheduler calls out to the architecture-specific port to perform the context-switch, using archContextSwitch() which must be provided by each architecture port. This function carries out the low-level saving and restoring of registers appropriate for the architecture. The thread being switched out must have a set of CPU registers saved, and the thread being scheduled in has a set of CPU registers restored (which were previously saved). In this fashion threads are rescheduled with the CPU registers in exactly the same state as when the thread was scheduled out.

New threads which have never been scheduled in have a pre-formatted stack area containing a set of CPU register values ready for restoring that appears exactly as if the thread had been previously scheduled out. In other words, the scheduler need not know when it restores registers to switch a thread in whether it has previously run or if it has never been run since the thread was created. The context-save area is formatted in exactly the same manner.

Functions contained in this module:

Application-callable initialisation functions:

Application-callable general functions:

Internal kernel functions:


Define Documentation

#define STACK_CHECK_BYTE   0x5A

Bytecode to fill thread stacks with for stack-checking purposes

Referenced by atomThreadCreate().


Function Documentation

ATOM_TCB* atomCurrentContext ( void   ) 

atomCurrentContext

Get the current thread context.

Returns a pointer to the current thread's TCB, or NULL if not in thread-context (in interrupt context).

Return values:
Pointer to current thread's TCB, NULL if in interrupt context

Referenced by atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSemDelete(), atomSemGet(), atomSemPut(), atomThreadCreate(), and atomTimerDelay().

void atomIntEnter ( void   ) 

atomIntEnter

Interrupt handler entry routine.

Must be called at the start of any interrupt handlers that may call an OS primitive and make a thread ready.

Returns:
None
void atomIntExit ( uint8_t  timer_tick  ) 

atomIntExit

Interrupt handler exit routine.

Must be called at the end of any interrupt handlers that may call an OS primitive and make a thread ready.

This is responsible for calling the scheduler at the end of interrupt handlers to determine whether a new thread has now been made ready and should be scheduled in.

Parameters:
timer_tick TRUE if this is a timer tick
Returns:
None

References atomSched().

uint8_t atomOSInit ( void *  idle_thread_stack_top,
uint32_t  idle_thread_stack_size 
)

atomOSInit

Initialise the atomthreads OS.

Must be called before any application code uses the atomthreads APIs. No threads are actually started until the application calls atomOSStart().

Callers must provide a pointer to some storage for the idle thread stack. The caller is responsible for calculating the appropriate space required for their particular architecture.

Applications should use the following initialisation sequence:

Interrupts should be disabled until the first thread restore is complete, to avoid any complications due to interrupts occurring while crucial operating system facilities are being initialised. They are normally enabled by the archFirstThreadRestore() routine in the architecture port.

Parameters:
[in] idle_thread_stack_top Ptr to top of stack area for idle thread
[in] idle_thread_stack_size Size of idle thread stack in bytes
Return values:
ATOM_OK Success
ATOM_ERROR Initialisation error

References atomOSStarted, atomThreadCreate(), FALSE, IDLE_THREAD_PRIORITY, and uint8_t.

void atomOSStart ( void   ) 

atomOSStart

Start the highest priority thread running.

This function must be called after all OS initialisation is complete, and at least one application thread has been created. It will start executing the highest priority thread created (or first created if multiple threads share the highest priority).

Interrupts must still be disabled at this point. They must only be enabled when the first thread is restored and started by the architecture port's archFirstThreadRestore() routine.

Returns:
None

Enable the OS started flag. This stops routines like atomThreadCreate() attempting to schedule in a newly-created thread until the scheduler is up and running.

Application calls to atomThreadCreate() should have added at least one thread to the ready queue. Take the highest priority one off and schedule it in. If no threads were created, the OS will simply start the idle thread (the lowest priority allowed to be scheduled is the idle thread's priority, 255).

References archFirstThreadRestore(), atomOSStarted, tcbDequeuePriority(), and TRUE.

void atomSched ( uint8_t  timer_tick  ) 

atomSched

This is an internal function not for use by application code.

This is the main scheduler routine. It is called by the various OS library routines to check if any threads should be scheduled in now. If so, the context will be switched from the current thread to the new one.

The scheduler is priority-based with round-robin performed on threads with the same priority. Round-robin is only performed on timer ticks however. During reschedules caused by an OS operation (e.g. after giving or taking a semaphore) we only allow the scheduling in of threads with higher priority than current priority. On timer ticks we also allow the scheduling of same-priority threads - in that case we schedule in the head of the ready list for that priority and put the current thread at the tail.

Parameters:
[in] timer_tick Should be TRUE when called from the system tick
Returns:
None

Check the OS has actually started. As long as the proper initialisation sequence is followed there should be no calls here until the OS is started, but we check to handle badly-behaved ports.

If the current thread is going into suspension, then unconditionally dequeue the next thread for execution.

Dequeue the next ready to run thread. There will always be at least the idle thread waiting. Note that this could actually be the suspending thread if it was unsuspended before the scheduler was called.

Don't need to add the current thread to any queue because it was suspended by another OS mechanism and will be sitting on a suspend queue or similar within one of the OS primitive libraries (e.g. semaphore).

Otherwise the current thread is still ready, but check if any other threads are ready.

Current priority is already highest (0), don't allow preempt by threads of any priority because this is not a time-slice.

References atomOSStarted, CRITICAL_END, CRITICAL_START, CRITICAL_STORE, FALSE, int16_t, atom_tcb::priority, atom_tcb::suspended, tcbDequeueHead(), tcbDequeuePriority(), tcbEnqueuePriority(), TRUE, and uint8_t.

Referenced by atomIntExit(), atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSemDelete(), atomSemGet(), atomSemPut(), atomThreadCreate(), and atomTimerDelay().

uint8_t atomThreadCreate ( ATOM_TCB tcb_ptr,
uint8_t  priority,
void(*)(uint32_t)  entry_point,
uint32_t  entry_param,
void *  stack_top,
uint32_t  stack_size 
)

atomThreadCreate

Creates and starts a new thread.

Callers provide the ATOM_TCB structure storage, these are not obtained from an internal TCB free list.

The function puts the new thread on the ready queue and calls the scheduler. If the priority is higher than the current priority, then the new thread may be scheduled in before the function returns.

Optionally prefills the thread stack with a known value to enable stack usage checking (if the ATOM_STACK_CHECKING macro is defined).

Parameters:
[in] tcb_ptr Pointer to the thread's TCB storage
[in] priority Priority of the thread (0 to 255)
[in] entry_point Thread entry point
[in] entry_param Parameter passed to thread entry point
[in] stack_top Top of the stack area
[in] stack_size Size of the stack area in bytes
Return values:
ATOM_OK Success
ATOM_ERR_PARAM Bad parameters
ATOM_ERR_QUEUE Error putting the thread on the ready queue

Store the thread entry point and parameter in the TCB. This may not be necessary for all architecture ports if they put all of this information in the initial thread stack.

Additional processing only required if stack-checking is enabled. Incurs a slight overhead on each thread creation and uses some additional storage in the TCB, but can be compiled out if not desired.

Call the arch-specific routine to set up the stack. This routine is responsible for creating the context save area necessary for allowing atomThreadSwitch() to schedule it in. The initial archContextSwitch() call when this thread gets scheduled in the first time will then restore the program counter to the thread entry point, and any other necessary register values ready for it to start running.

If the OS is started and we're in thread context, check if we should be scheduled in now.

References archThreadContextInit(), ATOM_ERR_PARAM, ATOM_ERR_QUEUE, ATOM_OK, atomCurrentContext(), atomOSStarted, atomSched(), CRITICAL_END, CRITICAL_START, CRITICAL_STORE, atom_tcb::entry_param, atom_tcb::entry_point, FALSE, atom_tcb::next_tcb, atom_tcb::prev_tcb, atom_tcb::priority, STACK_CHECK_BYTE, atom_tcb::suspend_timo_cb, atom_tcb::suspended, tcbEnqueuePriority(), TRUE, and uint8_t.

Referenced by atomOSInit().

ATOM_TCB* tcbDequeueEntry ( ATOM_TCB **  tcb_queue_ptr,
ATOM_TCB tcb_ptr 
)

tcbDequeueEntry

This is an internal function not for use by application code.

Dequeues a particular TCB from the queue pointed to by tcb_queue_ptr.

The TCB will be removed from the queue.

tcb_queue_ptr may be modified by the routine if the dequeued TCB was the list head. It is valid for tcb_queue_ptr to point to a NULL pointer, which is the case if the queue is currently empty. In this case the function returns NULL.

NOTE: Assumes that the caller is already in a critical section.

Parameters:
[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
[in] tcb_ptr Pointer to TCB to dequeue
Returns:
Pointer to the dequeued TCB, or NULL if entry wasn't found

References atom_tcb::next_tcb, and atom_tcb::prev_tcb.

Referenced by atomMutexGet(), atomQueueGet(), atomQueuePut(), and atomSemGet().

ATOM_TCB* tcbDequeueHead ( ATOM_TCB **  tcb_queue_ptr  ) 

tcbDequeueHead

This is an internal function not for use by application code.

Dequeues the highest priority TCB on the queue pointed to by tcb_queue_ptr.

The TCB will be removed from the queue. Same priority TCBs are dequeued in FIFO order.

tcb_queue_ptr will be modified by the routine if a TCB is dequeued, as this will be the list head. It is valid for tcb_queue_ptr to point to a NULL pointer, which is the case if the queue is currently empty. In this case the function returns NULL.

NOTE: Assumes that the caller is already in a critical section.

Parameters:
[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
Returns:
Pointer to highest priority TCB on queue, or NULL if queue empty

References atom_tcb::next_tcb, and atom_tcb::prev_tcb.

Referenced by atomMutexDelete(), atomMutexPut(), atomQueueDelete(), atomSched(), atomSemDelete(), and atomSemPut().

ATOM_TCB* tcbDequeuePriority ( ATOM_TCB **  tcb_queue_ptr,
uint8_t  priority 
)

tcbDequeuePriority

This is an internal function not for use by application code.

Dequeues the first TCB of the given priority or higher, from the queue pointed to by tcb_queue_ptr. Because the queue is ordered high priority first, we only ever dequeue the list head, if any. If the list head is lower priority than we wish to dequeue, then all following ones will also be lower priority and hence are not parsed.

The TCB will be removed from the queue. Same priority TCBs will be dequeued in FIFO order.

tcb_queue_ptr may be modified by the routine if the dequeued TCB was the list head. It is valid for tcb_queue_ptr to point to a NULL pointer, which is the case if the queue is currently empty. In this case the function returns NULL.

NOTE: Assumes that the caller is already in a critical section.

Parameters:
[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
[in] priority Minimum priority to qualify for dequeue
Returns:
Pointer to the dequeued TCB, or NULL if none found within priority

References atom_tcb::next_tcb, and atom_tcb::prev_tcb.

Referenced by atomOSStart(), and atomSched().

uint8_t tcbEnqueuePriority ( ATOM_TCB **  tcb_queue_ptr,
ATOM_TCB tcb_ptr 
)

tcbEnqueuePriority

This is an internal function not for use by application code.

Enqueues the TCB tcb_ptr on the TCB queue pointed to by tcb_queue_ptr. TCBs are placed on the queue in priority order. If there are existing TCBs at the same priority as the TCB to be enqueued, the enqueued TCB will be placed at the end of the same-priority TCBs. Calls to tcbDequeuePriority() will dequeue same-priority TCBs in FIFO order.

tcb_queue_ptr may be modified by the routine if the enqueued TCB becomes the new list head. It is valid for tcb_queue_ptr to point to a NULL pointer, which is the case if the queue is currently empty.

NOTE: Assumes that the caller is already in a critical section.

Parameters:
[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
[in] tcb_ptr Pointer to TCB to enqueue
Return values:
ATOM_OK Success
ATOM_ERR_PARAM Bad parameters

References ATOM_ERR_PARAM, ATOM_OK, atom_tcb::next_tcb, atom_tcb::prev_tcb, atom_tcb::priority, and uint8_t.

Referenced by atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSched(), atomSemDelete(), atomSemGet(), atomSemPut(), and atomThreadCreate().


Variable Documentation

uint8_t atomOSStarted = FALSE

Set to TRUE when OS is started and running threads

Referenced by atomOSInit(), atomOSStart(), atomSched(), atomThreadCreate(), and atomTimerTick().

This is the head of the queue of threads that are ready to run. It is ordered by priority, with the higher priority threads coming first. Where there are multiple threads of the same priority, the TCB (task control block) pointers are FIFO-ordered.

Dequeuing the head is a fast operation because the list is ordered. Enqueuing may have to walk up to the end of the list. This means that context-switch times depend on the number of threads on the ready queue, but efficient use is made of available RAM on tiny systems by avoiding priority tables etc. This scheme can be easily swapped out for other scheduler schemes by replacing the TCB enqueue and dequeue functions.

Once a thread is scheduled in, it is not present on the ready queue or any other kernel queue while it is running. When scheduled out it will be either placed back on the ready queue (if still ready), or will be suspended on some OS primitive if no longer ready (e.g. on the suspended TCB queue for a semaphore, or in the timer list if suspended on a timer delay).

Referenced by atomMutexDelete(), atomMutexPut(), atomQueueDelete(), atomSemDelete(), and atomSemPut().


Generated on Fri Jun 4 01:00:01 2010 for atomthreads by  doxygen 1.6.1