Select Page

Atomthread Kernel Functions Reference

Function Documentation

atomCurrentContext()

ATOM_TCB* atomCurrentContext(void);

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().

atomIntEnter()

void atomIntEnter(void);

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

atomIntExit()

void atomIntExit(uint8_t timer_tick );

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().

atomOSInit()

uint8_t atomOSInit(void * idle_thread_stack_top, uint32_t idle_thread_stack_size);

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:

  • Call atomOSInit() before calling any atomthreads APIs
  • Arrange for a timer to call atomTimerTick() periodically
  • Create one or more application threads using atomThreadCreate()
  • Start the OS using atomOSStart(). At this point the highest priority application thread created will be started.

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.

atomOSStart()

void atomOSStart(void);

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.

atomSched()

void atomSched(uint8_t timer_tick);

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().

atomThreadCreate()

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);

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().

tcbDequeueEntry()

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

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().

tcbDequeueHead()

ATOM_TCB* tcbDequeueHead(ATOM_TCB ** tcb_queue_ptr);

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().

tcbDequeuePriority()

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

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().

tcbEnqueuePriority()

uint8_t tcbEnqueuePriority(ATOM_TCB ** tcb_queue_ptr, ATOM_TCB * tcb_ptr);

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

atomOSStarted

uint8_t atomOSStarted = FALSE; Set to TRUE when OS is started and running threads

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

tcbReadyQ

ATOM_TCB* tcbReadyQ = NULL

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().