#include "atomtimer.h"
#include "atomport.h"
Go to the source code of this file.
Data Structures | |
struct | atom_tcb |
Defines | |
#define | TRUE 1 |
#define | FALSE 0 |
#define | ATOM_OK 0 |
#define | ATOM_ERROR 1 |
#define | ATOM_TIMEOUT 2 |
#define | ATOM_WOULDBLOCK 3 |
#define | ATOM_ERR_CONTEXT 200 |
#define | ATOM_ERR_PARAM 201 |
#define | ATOM_ERR_DELETED 202 |
#define | ATOM_ERR_OVF 203 |
#define | ATOM_ERR_QUEUE 204 |
#define | ATOM_ERR_TIMER 205 |
#define | ATOM_ERR_NOT_FOUND 206 |
#define | ATOM_ERR_OWNERSHIP 207 |
#define | IDLE_THREAD_PRIORITY 255 |
Typedefs | |
typedef struct atom_tcb | ATOM_TCB |
Functions | |
uint8_t | atomOSInit (void *idle_thread_stack_top, uint32_t stack_size) |
void | atomOSStart (void) |
void | atomSched (uint8_t timer_tick) |
void | atomIntEnter (void) |
void | atomIntExit (uint8_t timer_tick) |
uint8_t | tcbEnqueuePriority (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr) |
ATOM_TCB * | tcbDequeueHead (ATOM_TCB **tcb_queue_ptr) |
ATOM_TCB * | tcbDequeueEntry (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr) |
ATOM_TCB * | tcbDequeuePriority (ATOM_TCB **tcb_queue_ptr, uint8_t priority) |
ATOM_TCB * | atomCurrentContext (void) |
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) |
uint8_t | atomThreadStackCheck (ATOM_TCB *tcb_ptr, uint32_t *used_bytes, uint32_t *free_bytes) |
void | archContextSwitch (ATOM_TCB *old_tcb_ptr, ATOM_TCB *new_tcb_ptr) |
void | archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void(*entry_point)(uint32_t), uint32_t entry_param) |
void | archFirstThreadRestore (ATOM_TCB *new_tcb_ptr) |
void | atomTimerTick (void) |
Variables | |
ATOM_TCB * | tcbReadyQ |
uint8_t | atomOSStarted |
#define ATOM_ERR_CONTEXT 200 |
Referenced by atomMutexGet(), atomQueueGet(), atomQueuePut(), atomSemGet(), and atomTimerDelay().
#define ATOM_ERR_DELETED 202 |
Referenced by atomMutexDelete(), atomQueueDelete(), and atomSemDelete().
#define ATOM_ERR_NOT_FOUND 206 |
Referenced by atomTimerCancel().
#define ATOM_ERR_OVF 203 |
Referenced by atomMutexGet(), and atomSemPut().
#define ATOM_ERR_OWNERSHIP 207 |
Referenced by atomMutexPut().
#define ATOM_ERR_PARAM 201 |
Referenced by atomMutexCreate(), atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueCreate(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSemCreate(), atomSemDelete(), atomSemGet(), atomSemPut(), atomSemResetCount(), atomThreadCreate(), atomTimerCancel(), atomTimerDelay(), atomTimerRegister(), and tcbEnqueuePriority().
#define ATOM_ERR_QUEUE 204 |
Referenced by atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSemDelete(), atomSemGet(), atomSemPut(), and atomThreadCreate().
#define ATOM_ERR_TIMER 205 |
Referenced by atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSemDelete(), atomSemGet(), atomSemPut(), and atomTimerDelay().
#define ATOM_ERROR 1 |
#define ATOM_OK 0 |
Referenced by atomMutexCreate(), atomMutexDelete(), atomMutexGet(), atomMutexPut(), atomQueueCreate(), atomQueueDelete(), atomQueueGet(), atomQueuePut(), atomSemCreate(), atomSemDelete(), atomSemGet(), atomSemPut(), atomSemResetCount(), atomThreadCreate(), atomTimerCancel(), atomTimerDelay(), atomTimerRegister(), and tcbEnqueuePriority().
#define ATOM_TIMEOUT 2 |
#define ATOM_WOULDBLOCK 3 |
Referenced by atomMutexGet(), atomQueueGet(), atomQueuePut(), and atomSemGet().
#define FALSE 0 |
#define IDLE_THREAD_PRIORITY 255 |
Referenced by atomOSInit().
#define TRUE 1 |
void archFirstThreadRestore | ( | ATOM_TCB * | new_tcb_ptr | ) |
Referenced by atomOSStart().
void archThreadContextInit | ( | ATOM_TCB * | tcb_ptr, | |
void * | stack_top, | |||
void(*)(uint32_t) | entry_point, | |||
uint32_t | entry_param | |||
) |
Referenced by atomThreadCreate().
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).
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.
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.
timer_tick | TRUE if this is a timer tick |
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.
[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 |
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.
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.
[in] | timer_tick | Should be TRUE when called from the system tick |
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).
[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 |
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().
uint8_t atomThreadStackCheck | ( | ATOM_TCB * | tcb_ptr, | |
uint32_t * | used_bytes, | |||
uint32_t * | free_bytes | |||
) |
void atomTimerTick | ( | void | ) |
atomTimerTick
System tick handler.
User ports are responsible for calling this routine once per system tick.
On each system tick this routine is called to do the following: 1. Increase the system tick count 2. Call back to any registered timer callbacks
References atomOSStarted.
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.
[in,out] | tcb_queue_ptr | Pointer to TCB queue head pointer |
[in] | tcb_ptr | Pointer to TCB to dequeue |
References atom_tcb::next_tcb, and atom_tcb::prev_tcb.
Referenced by atomMutexGet(), atomQueueGet(), atomQueuePut(), and atomSemGet().
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.
[in,out] | tcb_queue_ptr | Pointer to TCB queue head pointer |
References atom_tcb::next_tcb, and atom_tcb::prev_tcb.
Referenced by atomMutexDelete(), atomMutexPut(), atomQueueDelete(), atomSched(), atomSemDelete(), and atomSemPut().
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.
[in,out] | tcb_queue_ptr | Pointer to TCB queue head pointer |
[in] | priority | Minimum priority to qualify for dequeue |
References atom_tcb::next_tcb, and atom_tcb::prev_tcb.
Referenced by atomOSStart(), and atomSched().
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.
[in,out] | tcb_queue_ptr | Pointer to TCB queue head pointer |
[in] | tcb_ptr | Pointer to TCB to enqueue |
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().
uint8_t atomOSStarted |
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().