mirror of
https://github.com/kelvinlawson/atomthreads.git
synced 2026-01-11 18:33:16 +01:00
Add Atomthreads RTOS source files.
This commit is contained in:
1161
kernel/Doxyfile
Normal file
1161
kernel/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
119
kernel/atom.h
Executable file
119
kernel/atom.h
Executable file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_H
|
||||
#define __ATOM_H
|
||||
|
||||
#include "atomtimer.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
/* Data types */
|
||||
|
||||
/* Forward declaration */
|
||||
struct atom_tcb;
|
||||
|
||||
typedef struct atom_tcb
|
||||
{
|
||||
/* Thread's current stack pointer. When a thread is scheduled
|
||||
* out the architecture port can save*/
|
||||
POINTER sp_save_ptr;
|
||||
|
||||
/* Thread priority (0-255) */
|
||||
uint8_t priority;
|
||||
|
||||
/* Thread entry point and parameter */
|
||||
void (*entry_point)(uint32_t);
|
||||
uint32_t entry_param;
|
||||
|
||||
/* Queue pointers */
|
||||
struct atom_tcb *prev_tcb; /* Previous TCB in doubly-linked TCB list */
|
||||
struct atom_tcb *next_tcb; /* Next TCB in doubly-linked list */
|
||||
|
||||
/* Suspension data */
|
||||
uint8_t suspended; /* TRUE if task is currently suspended */
|
||||
uint8_t suspend_wake_status; /* Status returned to woken suspend calls */
|
||||
ATOM_TIMER *suspend_timo_cb; /* Callback registered for suspension timeouts */
|
||||
|
||||
} ATOM_TCB;
|
||||
|
||||
|
||||
/* Global data */
|
||||
extern ATOM_TCB *tcbReadyQ;
|
||||
extern uint8_t atomOSStarted;
|
||||
|
||||
|
||||
/* Constants */
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
/* Error values */
|
||||
|
||||
#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
|
||||
|
||||
/* Idle thread priority (lowest) */
|
||||
#define IDLE_THREAD_PRIORITY 255
|
||||
|
||||
|
||||
/* Function prototypes */
|
||||
extern uint8_t atomOSInit (void *idle_thread_stack_top);
|
||||
extern void atomOSStart (void);
|
||||
|
||||
extern void atomSched (uint8_t timer_tick);
|
||||
|
||||
extern void atomIntEnter (void);
|
||||
extern void atomIntExit (uint8_t timer_tick);
|
||||
|
||||
extern uint8_t tcbEnqueuePriority (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr);
|
||||
extern ATOM_TCB *tcbDequeueHead (ATOM_TCB **tcb_queue_ptr);
|
||||
extern ATOM_TCB *tcbDequeueEntry (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr);
|
||||
extern ATOM_TCB *tcbDequeuePriority (ATOM_TCB **tcb_queue_ptr, uint8_t priority);
|
||||
|
||||
extern ATOM_TCB *atomCurrentContext (void);
|
||||
|
||||
extern uint8_t atomThreadCreate (ATOM_TCB *tcb_ptr, uint8_t priority, void (*entry_point)(uint32_t), uint32_t entry_param, void *stack_top);
|
||||
|
||||
extern void archContextSwitch (ATOM_TCB *old_tcb_ptr, ATOM_TCB *new_tcb_ptr);
|
||||
extern void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(uint32_t), uint32_t entry_param);
|
||||
extern void archFirstThreadRestore(ATOM_TCB *new_tcb_ptr);
|
||||
|
||||
extern void atomTimerTick (void);
|
||||
|
||||
|
||||
#endif /* __ATOM_H */
|
||||
796
kernel/atomkernel.c
Executable file
796
kernel/atomkernel.c
Executable file
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "atom.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Global data */
|
||||
|
||||
/**
|
||||
* 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 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 while
|
||||
* it is running. When scheduled out it will be either placed back on the
|
||||
* ready queue, or will be suspended on some OS primitive (e.g. on the
|
||||
* suspended TCB queue for a semaphore, or in the timer list if suspended on
|
||||
* a timer delay).
|
||||
*/
|
||||
ATOM_TCB *tcbReadyQ = NULL;
|
||||
|
||||
/** Set to TRUE when OS is started and running threads */
|
||||
uint8_t atomOSStarted = FALSE;
|
||||
|
||||
|
||||
/* Local data */
|
||||
|
||||
/** This is a pointer to the TCB for the currently-running thread */
|
||||
static ATOM_TCB *curr_tcb = NULL;
|
||||
|
||||
/** Storage for the idle thread's TCB */
|
||||
static ATOM_TCB idle_tcb;
|
||||
|
||||
/* Number of nested interrupts */
|
||||
static int atomIntCnt = 0;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void atomThreadSwitch(ATOM_TCB *old_tcb, ATOM_TCB *new_tcb);
|
||||
static void atomIdleThread (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b 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.
|
||||
*
|
||||
* @param[in] timer_tick Should be TRUE when called from the system tick
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomSched (uint8_t timer_tick)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *new_tcb = NULL;
|
||||
int16_t lowest_pri;
|
||||
|
||||
/**
|
||||
* 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 (atomOSStarted == FALSE)
|
||||
{
|
||||
/* Don't schedule anything in until the OS is started */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enter critical section */
|
||||
CRITICAL_START ();
|
||||
|
||||
/**
|
||||
* If the current thread is going into suspension, then
|
||||
* unconditionally dequeue the next thread for execution.
|
||||
*/
|
||||
if (curr_tcb->suspended == TRUE)
|
||||
{
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
new_tcb = tcbDequeueHead (&tcbReadyQ);
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/* Switch to the new thread */
|
||||
atomThreadSwitch (curr_tcb, new_tcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Otherwise the current thread is still ready, but check
|
||||
* if any other threads are ready.
|
||||
*/
|
||||
else
|
||||
{
|
||||
/* Calculate which priority is allowed to be scheduled in */
|
||||
if (timer_tick == TRUE)
|
||||
{
|
||||
/* Same priority or higher threads can preempt */
|
||||
lowest_pri = (int16_t)curr_tcb->priority;
|
||||
}
|
||||
else if (curr_tcb->priority > 0)
|
||||
{
|
||||
/* Only higher priority threads can preempt, invalid for 0 (highest) */
|
||||
lowest_pri = (int16_t)(curr_tcb->priority - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Current priority is already highest (0), don't allow preempt by
|
||||
* threads of any priority because this is not a time-slice.
|
||||
*/
|
||||
lowest_pri = -1;
|
||||
}
|
||||
|
||||
/* Check if a reschedule is allowed */
|
||||
if (lowest_pri >= 0)
|
||||
{
|
||||
/* Check for a thread at the given minimum priority level or higher */
|
||||
new_tcb = tcbDequeuePriority (&tcbReadyQ, (uint8_t)lowest_pri);
|
||||
|
||||
/* If a thread was found, schedule it in */
|
||||
if (new_tcb)
|
||||
{
|
||||
/* Add the current thread to the ready queue */
|
||||
(void)tcbEnqueuePriority (&tcbReadyQ, curr_tcb);
|
||||
|
||||
/* Switch to the new thread */
|
||||
atomThreadSwitch (curr_tcb, new_tcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Exit critical section */
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomThreadSwitch
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* The function is called by the scheduler to perform a context switch.
|
||||
* Execution will switch to the new thread's context, therefore the
|
||||
* function doesn't actually return until the old thread is scheduled
|
||||
* back in.
|
||||
*
|
||||
* @param[in] old_tcb Pointer to TCB for thread being scheduled out
|
||||
* @param[in] new_tcb Pointer to TCB for thread being scheduled in
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void atomThreadSwitch(ATOM_TCB *old_tcb, ATOM_TCB *new_tcb)
|
||||
{
|
||||
/**
|
||||
* Check if the new thread is actually the current one, in which
|
||||
* case we don't need to do any context switch. This can happen
|
||||
* if a thread goes into suspend but is unsuspended again before
|
||||
* it is fully scheduled out.
|
||||
*/
|
||||
if (old_tcb != new_tcb)
|
||||
{
|
||||
/* Set the new currently-running thread pointer */
|
||||
curr_tcb = new_tcb;
|
||||
|
||||
/* Call the architecture-specific context switch */
|
||||
archContextSwitch (old_tcb, new_tcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* The context switch shifted execution to a different thread. By the time
|
||||
* we get back here, we are running in old_tcb context again. Clear its
|
||||
* suspend status now that we're back.
|
||||
*/
|
||||
old_tcb->suspended = FALSE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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.
|
||||
*
|
||||
* @param[in] tcb_ptr Pointer to the thread's TCB storage
|
||||
* @param[in] priority Priority of the thread (0 to 255)
|
||||
* @param[in] entry_point Thread entry point
|
||||
* @param[in] entry_param Parameter passed to thread entry point
|
||||
* @param[in] stack_top Top of the stack area
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
* @retval ATOM_ERR_QUEUE Error putting the thread on the ready queue
|
||||
*/
|
||||
uint8_t atomThreadCreate (ATOM_TCB *tcb_ptr, uint8_t priority, void (*entry_point)(uint32_t), uint32_t entry_param, void *stack_top)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
|
||||
if ((tcb_ptr == NULL) || (entry_point == NULL) || (stack_top == NULL))
|
||||
{
|
||||
/* Bad parameters */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Set up the TCB initial values */
|
||||
tcb_ptr->suspended = FALSE;
|
||||
tcb_ptr->priority = priority;
|
||||
tcb_ptr->prev_tcb = NULL;
|
||||
tcb_ptr->next_tcb = NULL;
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
tcb_ptr->entry_point = entry_point;
|
||||
tcb_ptr->entry_param = entry_param;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
archThreadContextInit (tcb_ptr, stack_top, entry_point, entry_param);
|
||||
|
||||
/* Protect access to the OS queue */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Put this thread on the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Queue-related error */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* If the OS is started and we're in thread context, check if we
|
||||
* should be scheduled in now.
|
||||
*/
|
||||
if ((atomOSStarted == TRUE) && atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
|
||||
/* Success */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomIntEnter (void)
|
||||
{
|
||||
/* Increment the interrupt count */
|
||||
atomIntCnt++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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.
|
||||
*
|
||||
* @param timer_tick TRUE if this is a timer tick
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomIntExit (uint8_t timer_tick)
|
||||
{
|
||||
/* Decrement the interrupt count */
|
||||
atomIntCnt--;
|
||||
|
||||
/* Call the scheduler */
|
||||
atomSched (timer_tick);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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).
|
||||
*
|
||||
* @retval Pointer to current thread's TCB, NULL if in interrupt context
|
||||
*/
|
||||
ATOM_TCB *atomCurrentContext (void)
|
||||
{
|
||||
/* Return the current thread's TCB or NULL if in interrupt context */
|
||||
if (atomIntCnt == 0)
|
||||
return (curr_tcb);
|
||||
else
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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:
|
||||
*
|
||||
* -> 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.
|
||||
*
|
||||
* @param[in] idle_thread_stack_top Ptr to top of stack area for idle thread
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERROR Initialisation error
|
||||
*/
|
||||
uint8_t atomOSInit (void *idle_thread_stack_top)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Initialise data */
|
||||
curr_tcb = NULL;
|
||||
tcbReadyQ = NULL;
|
||||
atomOSStarted = FALSE;
|
||||
|
||||
/* Create the idle thread */
|
||||
status = atomThreadCreate(&idle_tcb,
|
||||
IDLE_THREAD_PRIORITY,
|
||||
atomIdleThread,
|
||||
0,
|
||||
idle_thread_stack_top);
|
||||
|
||||
/* Return status */
|
||||
return (status);
|
||||
|
||||
}
|
||||
/**
|
||||
* \b 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.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomOSStart (void)
|
||||
{
|
||||
ATOM_TCB *new_tcb;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
atomOSStarted = TRUE;
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
new_tcb = tcbDequeuePriority (&tcbReadyQ, 255);
|
||||
if (new_tcb)
|
||||
{
|
||||
/* Set the new currently-running thread pointer */
|
||||
curr_tcb = new_tcb;
|
||||
|
||||
/* Restore and run the first thread */
|
||||
archFirstThreadRestore (new_tcb);
|
||||
|
||||
/* Never returns to here, execution shifts to new thread context */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No ready threads were found. atomOSInit() probably was not called */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomIdleThread
|
||||
*
|
||||
* Entry point for idle thread.
|
||||
*
|
||||
* This thread must always be present, and will be the thread executed when
|
||||
* no other threads are ready to run. It must not call any library routines
|
||||
* which would cause it to block.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void atomIdleThread (uint32_t data)
|
||||
{
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
/** \todo Provide user idle hooks*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbEnqueuePriority
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Enqueues the TCB \c tcb_ptr on the TCB queue pointed to by \c 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.
|
||||
*
|
||||
* \c 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.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
* @param[in] tcb_ptr Pointer to TCB to enqueue
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
*/
|
||||
uint8_t tcbEnqueuePriority (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr)
|
||||
{
|
||||
uint8_t status;
|
||||
ATOM_TCB *prev_ptr, *next_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if ((tcb_queue_ptr == NULL) || (tcb_ptr == NULL))
|
||||
{
|
||||
/* Return error */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Walk the list and enqueue at the end of the TCBs at this priority */
|
||||
prev_ptr = next_ptr = *tcb_queue_ptr;
|
||||
do
|
||||
{
|
||||
/* Insert if:
|
||||
* next_ptr = NULL (we're at the head of an empty queue or at the tail)
|
||||
* the next TCB in the list is lower priority than the one we're enqueuing.
|
||||
*/
|
||||
if ((next_ptr == NULL) || (next_ptr->priority > tcb_ptr->priority))
|
||||
{
|
||||
/* Make this TCB the new listhead */
|
||||
if (next_ptr == *tcb_queue_ptr)
|
||||
{
|
||||
*tcb_queue_ptr = tcb_ptr;
|
||||
tcb_ptr->prev_tcb = NULL;
|
||||
tcb_ptr->next_tcb = next_ptr;
|
||||
if (next_ptr)
|
||||
next_ptr->prev_tcb = tcb_ptr;
|
||||
}
|
||||
/* Insert between two TCBs or at the tail */
|
||||
else
|
||||
{
|
||||
tcb_ptr->prev_tcb = prev_ptr;
|
||||
tcb_ptr->next_tcb = next_ptr;
|
||||
prev_ptr->next_tcb = tcb_ptr;
|
||||
if (next_ptr)
|
||||
next_ptr->prev_tcb = tcb_ptr;
|
||||
}
|
||||
|
||||
/* Quit the loop, we've finished inserting */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not inserting here, try the next one */
|
||||
prev_ptr = next_ptr;
|
||||
next_ptr = next_ptr->next_tcb;
|
||||
}
|
||||
|
||||
}
|
||||
while (prev_ptr != NULL);
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbDequeueHead
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Dequeues the highest priority TCB on the queue pointed to by
|
||||
* \c tcb_queue_ptr.
|
||||
*
|
||||
* The TCB will be removed from the queue. Same priority TCBs are dequeued in
|
||||
* FIFO order.
|
||||
*
|
||||
* \c 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.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
*
|
||||
* @return Pointer to highest priority TCB on queue, or NULL if queue empty
|
||||
*/
|
||||
ATOM_TCB *tcbDequeueHead (ATOM_TCB **tcb_queue_ptr)
|
||||
{
|
||||
ATOM_TCB *ret_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if (tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check for an empty queue */
|
||||
else if (*tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Remove and return the listhead */
|
||||
else
|
||||
{
|
||||
ret_ptr = *tcb_queue_ptr;
|
||||
*tcb_queue_ptr = ret_ptr->next_tcb;
|
||||
if (*tcb_queue_ptr)
|
||||
(*tcb_queue_ptr)->prev_tcb = NULL;
|
||||
ret_ptr->next_tcb = ret_ptr->prev_tcb = NULL;
|
||||
}
|
||||
|
||||
return (ret_ptr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbDequeueEntry
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Dequeues a particular TCB from the queue pointed to by \c tcb_queue_ptr.
|
||||
*
|
||||
* The TCB will be removed from the queue.
|
||||
*
|
||||
* \c 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.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
* @param[in] tcb_ptr Pointer to TCB to dequeue
|
||||
*
|
||||
* @return Pointer to the dequeued TCB, or NULL if entry wasn't found
|
||||
*/
|
||||
ATOM_TCB *tcbDequeueEntry (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr)
|
||||
{
|
||||
ATOM_TCB *ret_ptr, *prev_ptr, *next_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if (tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check for an empty queue */
|
||||
else if (*tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Find and remove/return the specified entry */
|
||||
else
|
||||
{
|
||||
ret_ptr = NULL;
|
||||
prev_ptr = next_ptr = *tcb_queue_ptr;
|
||||
while (next_ptr)
|
||||
{
|
||||
/* Is this entry the one we're looking for? */
|
||||
if (next_ptr == tcb_ptr)
|
||||
{
|
||||
if (next_ptr == *tcb_queue_ptr)
|
||||
{
|
||||
/* We're removing the list head */
|
||||
*tcb_queue_ptr = next_ptr->next_tcb;
|
||||
if (*tcb_queue_ptr)
|
||||
(*tcb_queue_ptr)->prev_tcb = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We're removing a mid or tail TCB */
|
||||
prev_ptr->next_tcb = next_ptr->next_tcb;
|
||||
if (next_ptr->next_tcb)
|
||||
next_ptr->next_tcb->prev_tcb = prev_ptr;
|
||||
}
|
||||
ret_ptr = next_ptr;
|
||||
ret_ptr->prev_tcb = ret_ptr->next_tcb = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move on to the next in the list */
|
||||
prev_ptr = next_ptr;
|
||||
next_ptr = next_ptr->next_tcb;
|
||||
}
|
||||
}
|
||||
|
||||
return (ret_ptr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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 \c 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.
|
||||
*
|
||||
* \c 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.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
* @param[in] priority Minimum priority to qualify for dequeue
|
||||
*
|
||||
* @return Pointer to the dequeued TCB, or NULL if none found within priority
|
||||
*/
|
||||
ATOM_TCB *tcbDequeuePriority (ATOM_TCB **tcb_queue_ptr, uint8_t priority)
|
||||
{
|
||||
ATOM_TCB *ret_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if (tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check for an empty queue */
|
||||
else if (*tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check if the list head priority is within our range */
|
||||
else if ((*tcb_queue_ptr)->priority <= priority)
|
||||
{
|
||||
/* Remove the list head */
|
||||
ret_ptr = *tcb_queue_ptr;
|
||||
*tcb_queue_ptr = (*tcb_queue_ptr)->next_tcb;
|
||||
if (*tcb_queue_ptr)
|
||||
{
|
||||
(*tcb_queue_ptr)->prev_tcb = NULL;
|
||||
ret_ptr->next_tcb = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No higher priority ready threads found */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
|
||||
return (ret_ptr);
|
||||
}
|
||||
645
kernel/atommutex.c
Executable file
645
kernel/atommutex.c
Executable file
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "atom.h"
|
||||
#include "atommutex.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Local data types */
|
||||
|
||||
typedef struct mutex_timer
|
||||
{
|
||||
ATOM_TCB *tcb_ptr; /* Thread which is suspended with timeout */
|
||||
ATOM_MUTEX *mutex_ptr; /* Mutex the thread is suspended on */
|
||||
} MUTEX_TIMER;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static void atomMutexTimerCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b atomMutexCreate
|
||||
*
|
||||
* Initialises a mutex object.
|
||||
*
|
||||
* Must be called before calling any other mutex library routines on a
|
||||
* mutex. Objects can be deleted later using atomMutexDelete().
|
||||
*
|
||||
* Does not set the owner of a mutex. atomMutexGet() must be called after
|
||||
* creation in order to actually take ownership.
|
||||
*
|
||||
* Does not allocate storage, the caller provides the mutex object.
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @param[in] mutex Pointer to mutex object
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
*/
|
||||
uint8_t atomMutexCreate (ATOM_MUTEX *mutex)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Parameter check */
|
||||
if (mutex == NULL)
|
||||
{
|
||||
/* Bad mutex pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start with no owner (unlocked) */
|
||||
mutex->owner = NULL;
|
||||
|
||||
/* Reset the initial lock count */
|
||||
mutex->count = 0;
|
||||
|
||||
/* Initialise the suspended threads queue */
|
||||
mutex->suspQ = NULL;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomMutexDelete
|
||||
*
|
||||
* Deletes a mutex object.
|
||||
*
|
||||
* Any threads currently suspended on the mutex will be woken up with
|
||||
* return status ATOM_ERR_DELETED. If called at thread context then the
|
||||
* scheduler will be called during this function which may schedule in one
|
||||
* of the woken threads depending on relative priorities.
|
||||
*
|
||||
* This function can be called from interrupt context, but loops internally
|
||||
* waking up all threads blocking on the mutex, so the potential
|
||||
* execution cycles cannot be determined in advance.
|
||||
*
|
||||
* @param[in] mutex Pointer to mutex object
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout on a woken thread
|
||||
*/
|
||||
uint8_t atomMutexDelete (ATOM_MUTEX *mutex)
|
||||
{
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *tcb_ptr;
|
||||
uint8_t woken_threads = FALSE;
|
||||
|
||||
/* Parameter check */
|
||||
if (mutex == NULL)
|
||||
{
|
||||
/* Bad mutex pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default to success status unless errors occur during wakeup */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Wake up all suspended tasks */
|
||||
while (1)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Check if any threads are suspended */
|
||||
tcb_ptr = tcbDequeueHead (&mutex->suspQ);
|
||||
|
||||
/* A thread is suspended on the mutex */
|
||||
if (tcb_ptr)
|
||||
{
|
||||
/* Return error status to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_ERR_DELETED;
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Quit the loop, returning error */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if (tcb_ptr->suspend_timo_cb)
|
||||
{
|
||||
/* Cancel the callback */
|
||||
if (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Quit the loop, returning error */
|
||||
status = ATOM_ERR_TIMER;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Request a reschedule */
|
||||
woken_threads = TRUE;
|
||||
}
|
||||
|
||||
/* No more suspended threads */
|
||||
else
|
||||
{
|
||||
/* Exit critical region and quit the loop */
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call scheduler if any threads were woken up */
|
||||
if (woken_threads == TRUE)
|
||||
{
|
||||
/**
|
||||
* Only call the scheduler if we are in thread context, otherwise
|
||||
* it will be called on exiting the ISR by atomIntExit().
|
||||
*/
|
||||
if (atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomMutexGet
|
||||
*
|
||||
* Take the lock on a mutex.
|
||||
*
|
||||
* This takes ownership of a mutex if it is not currently owned. Ownership
|
||||
* is held by this thread until a corresponding call to atomMutexPut() by
|
||||
* the same thread.
|
||||
*
|
||||
* Can be called recursively by the original locking thread (owner).
|
||||
* Recursive calls are counted, and ownership is not relinquished until
|
||||
* the number of unlock (atomMutexPut()) calls by the owner matches the
|
||||
* number of lock (atomMutexGet()) calls.
|
||||
*
|
||||
* No thread other than the owner can lock or unlock the mutex while it is
|
||||
* locked by another thread.
|
||||
*
|
||||
* Depending on the \c timeout value specified the call will do one of
|
||||
* the following if the mutex is already locked by another thread:
|
||||
*
|
||||
* \c timeout == 0 : Call will block until the mutex is available
|
||||
* \c timeout > 0 : Call will block until available up to the specified timeout
|
||||
* \c timeout == -1 : Return immediately if mutex is locked by another thread
|
||||
*
|
||||
* If the call needs to block and \c timeout is zero, it will block
|
||||
* indefinitely until the owning thread calls atomMutexPut() or
|
||||
* atomMutexDelete() is called on the mutex.
|
||||
*
|
||||
* If the call needs to block and \c timeout is non-zero, the call will only
|
||||
* block for the specified number of system ticks after which time, if the
|
||||
* thread was not already woken, the call will return with \c ATOM_TIMEOUT.
|
||||
*
|
||||
* If the call would normally block and \c timeout is -1, the call will
|
||||
* return immediately with \c ATOM_WOULDBLOCK.
|
||||
*
|
||||
* This function can only be called from thread context. A mutex has the
|
||||
* concept of an owner thread, so it is never valid to make a mutex call
|
||||
* from interrupt context when there is no thread to associate with.
|
||||
*
|
||||
* @param[in] mutex Pointer to mutex object
|
||||
* @param[in] timeout Max system ticks to block (0 = forever)
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_TIMEOUT Mutex timed out before being woken
|
||||
* @retval ATOM_WOULDBLOCK Called with timeout == -1 but count is zero
|
||||
* @retval ATOM_ERR_DELETED Mutex was deleted while suspended
|
||||
* @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue
|
||||
* @retval ATOM_ERR_TIMER Problem registering the timeout
|
||||
* @retval ATOM_ERR_OVF The recursive lock count would have overflowed (>255)
|
||||
*/
|
||||
uint8_t atomMutexGet (ATOM_MUTEX *mutex, int32_t timeout)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
MUTEX_TIMER timer_data;
|
||||
ATOM_TIMER timer_cb;
|
||||
ATOM_TCB *curr_tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if (mutex == NULL)
|
||||
{
|
||||
/* Bad mutex pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the current TCB */
|
||||
curr_tcb_ptr = atomCurrentContext();
|
||||
|
||||
/* Protect access to the mutex object and OS queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/**
|
||||
* Check we are at thread context. Because mutexes have the concept of
|
||||
* owner threads, it is never valid to call here from an ISR,
|
||||
* regardless of whether we will block.
|
||||
*/
|
||||
if (curr_tcb_ptr == NULL)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Not currently in thread context, can't suspend */
|
||||
status = ATOM_ERR_CONTEXT;
|
||||
}
|
||||
|
||||
/* Otherwise if mutex is owned by another thread, block the calling thread */
|
||||
else if ((mutex->owner != NULL) && (mutex->owner != curr_tcb_ptr))
|
||||
{
|
||||
/* If called with timeout >= 0, we should block */
|
||||
if (timeout >= 0)
|
||||
{
|
||||
/* Add current thread to the suspend list on this mutex */
|
||||
if (tcbEnqueuePriority (&mutex->suspQ, curr_tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* There was an error putting this thread on the suspend list */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set suspended status for the current thread */
|
||||
curr_tcb_ptr->suspended = TRUE;
|
||||
|
||||
/* Track errors */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Register a timer callback if requested */
|
||||
if (timeout)
|
||||
{
|
||||
/* Fill out the data needed by the callback to wake us up */
|
||||
timer_data.tcb_ptr = curr_tcb_ptr;
|
||||
timer_data.mutex_ptr = mutex;
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = atomMutexTimerCallback;
|
||||
timer_cb.cb_data = (POINTER)&timer_data;
|
||||
timer_cb.cb_ticks = timeout;
|
||||
|
||||
/**
|
||||
* Store the timer details in the TCB so that we can
|
||||
* cancel the timer callback if the mutex is put
|
||||
* before the timeout occurs.
|
||||
*/
|
||||
curr_tcb_ptr->suspend_timo_cb = &timer_cb;
|
||||
|
||||
/* Register a callback on timeout */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
/* Timer registration failed */
|
||||
status = ATOM_ERR_TIMER;
|
||||
|
||||
/* Clean up and return to the caller */
|
||||
(void)tcbDequeueEntry (&mutex->suspQ, curr_tcb_ptr);
|
||||
curr_tcb_ptr->suspended = FALSE;
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set no timeout requested */
|
||||
else
|
||||
{
|
||||
/* No need to cancel timeouts on this one */
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Check no errors have occurred */
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/**
|
||||
* Current thread now blocking, schedule in a new
|
||||
* one. We already know we are in thread context
|
||||
* so can call the scheduler from here.
|
||||
*/
|
||||
atomSched (FALSE);
|
||||
|
||||
/**
|
||||
* Normal atomMutexPut() wakeups will set ATOM_OK status,
|
||||
* while timeouts will set ATOM_TIMEOUT and mutex
|
||||
* deletions will set ATOM_ERR_DELETED. */
|
||||
status = curr_tcb_ptr->suspend_wake_status;
|
||||
|
||||
/**
|
||||
* If we were woken up by another thread relinquishing
|
||||
* the mutex and handing this thread ownership, then
|
||||
* the relinquishing thread will set status to ATOM_OK
|
||||
* and will make this thread the owner. Setting the
|
||||
* owner before waking the thread ensures that no other
|
||||
* thread can preempt and take ownership of the mutex
|
||||
* between this thread being made ready to run, and
|
||||
* actually being scheduled back in here.
|
||||
*/
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/**
|
||||
* Since this thread has just gained ownership, the
|
||||
* lock count is zero and should be incremented
|
||||
* once for this call.
|
||||
*/
|
||||
mutex->count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* timeout == -1, requested not to block and mutex is owned by another thread */
|
||||
CRITICAL_END();
|
||||
status = ATOM_WOULDBLOCK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Thread is not owned or is owned by us, we can claim ownership */
|
||||
|
||||
/* Increment the lock count, checking for count overflow */
|
||||
if (mutex->count == 255)
|
||||
{
|
||||
/* Don't increment, just return error status */
|
||||
status = ATOM_ERR_OVF;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Increment the count and return to the calling thread */
|
||||
mutex->count++;
|
||||
|
||||
/* If the mutex is not locked, mark the calling thread as the new owner */
|
||||
if (mutex->owner == NULL)
|
||||
{
|
||||
mutex->owner = curr_tcb_ptr;
|
||||
}
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomMutexPut
|
||||
*
|
||||
* Give back the lock on a mutex.
|
||||
*
|
||||
* This checks that the mutex is owned by the calling thread, and decrements
|
||||
* the recursive lock count. Once the lock count reaches zero, the lock is
|
||||
* considered relinquished and no longer owned by this thread.
|
||||
*
|
||||
* If the lock is relinquished and there are threads blocking on the mutex, the
|
||||
* call will wake up the highest priority thread suspended. Only one thread is
|
||||
* woken per call to atomMutexPut(). If multiple threads of the same priority
|
||||
* are suspended, they are woken in order of suspension (FIFO).
|
||||
*
|
||||
* This function can only be called from thread context. A mutex has the
|
||||
* concept of an owner thread, so it is never valid to make a mutex call
|
||||
* from interrupt context when there is no thread to associate with.
|
||||
*
|
||||
* @param[in] mutex Pointer to mutex object
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout for a woken thread
|
||||
* @retval ATOM_ERR_OWNERSHIP Attempt to unlock mutex not owned by this thread
|
||||
*/
|
||||
uint8_t atomMutexPut (ATOM_MUTEX * mutex)
|
||||
{
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *tcb_ptr, *curr_tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if (mutex == NULL)
|
||||
{
|
||||
/* Bad mutex pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the current TCB */
|
||||
curr_tcb_ptr = atomCurrentContext();
|
||||
|
||||
/* Protect access to the mutex object and OS queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Check if the calling thread owns this mutex */
|
||||
if (mutex->owner != curr_tcb_ptr)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Attempt to unlock by non-owning thread */
|
||||
status = ATOM_ERR_OWNERSHIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Lock is owned by this thread, decrement the recursive lock count */
|
||||
mutex->count--;
|
||||
|
||||
/* Once recursive lock count reaches zero, we relinquish ownership */
|
||||
if (mutex->count == 0)
|
||||
{
|
||||
/* Relinquish ownership */
|
||||
mutex->owner = NULL;
|
||||
|
||||
/* If any threads are blocking on this mutex, wake them now */
|
||||
if (mutex->suspQ)
|
||||
{
|
||||
/**
|
||||
* Threads are woken up in priority order, with a FIFO system
|
||||
* used on same priority threads. We always take the head,
|
||||
* ordering is taken care of by an ordered list enqueue.
|
||||
*/
|
||||
tcb_ptr = tcbDequeueHead (&mutex->suspQ);
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* There was a problem putting the thread on the ready queue */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set OK status to be returned to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_OK;
|
||||
|
||||
/* Set this thread as the new owner of the mutex */
|
||||
mutex->owner = tcb_ptr;
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if ((tcb_ptr->suspend_timo_cb != NULL)
|
||||
&& (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK))
|
||||
{
|
||||
/* There was a problem cancelling a timeout on this mutex */
|
||||
status = ATOM_ERR_TIMER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* The scheduler may now make a policy decision to
|
||||
* thread switch. We already know we are in thread
|
||||
* context so can call the scheduler from here.
|
||||
*/
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Relinquished ownership and no threads waiting.
|
||||
* Nothing to do.
|
||||
*/
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Decremented lock but still retain ownership due to
|
||||
* recursion. Nothing to do.
|
||||
*/
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomMutexTimerCallback
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Timeouts on suspended threads are notified by the timer system through
|
||||
* this generic callback. The timer system calls us back with a pointer to
|
||||
* the relevant \c MUTEX_TIMER object which is used to retrieve the
|
||||
* mutex details.
|
||||
*
|
||||
* @param[in] cb_data Pointer to a MUTEX_TIMER object
|
||||
*/
|
||||
static void atomMutexTimerCallback (POINTER cb_data)
|
||||
{
|
||||
MUTEX_TIMER *timer_data_ptr;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Get the MUTEX_TIMER structure pointer */
|
||||
timer_data_ptr = (MUTEX_TIMER *)cb_data;
|
||||
|
||||
/* Check parameter is valid */
|
||||
if (timer_data_ptr)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Set status to indicate to the waiting thread that it timed out */
|
||||
timer_data_ptr->tcb_ptr->suspend_wake_status = ATOM_TIMEOUT;
|
||||
|
||||
/* Flag as no timeout registered */
|
||||
timer_data_ptr->tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/* Remove this thread from the mutex's suspend list */
|
||||
(void)tcbDequeueEntry (&timer_data_ptr->mutex_ptr->suspQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
(void)tcbEnqueuePriority (&tcbReadyQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* Note that we don't call the scheduler now as it will be called
|
||||
* when we exit the ISR by atomIntExit().
|
||||
*/
|
||||
}
|
||||
}
|
||||
44
kernel/atommutex.h
Executable file
44
kernel/atommutex.h
Executable file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __ATOM_MUTEX_H
|
||||
#define __ATOM_MUTEX_H
|
||||
|
||||
typedef struct atom_mutex
|
||||
{
|
||||
ATOM_TCB * suspQ; /* Queue of threads suspended on this mutex */
|
||||
ATOM_TCB * owner; /* Thread which currently owns the lock */
|
||||
uint8_t count; /* Recursive count of locks by the owner */
|
||||
} ATOM_MUTEX;
|
||||
|
||||
extern uint8_t atomMutexCreate (ATOM_MUTEX *mutex);
|
||||
extern uint8_t atomMutexDelete (ATOM_MUTEX *mutex);
|
||||
extern uint8_t atomMutexGet (ATOM_MUTEX *mutex, int32_t timeout);
|
||||
extern uint8_t atomMutexPut (ATOM_MUTEX *mutex);
|
||||
|
||||
#endif /* __ATOM_MUTEX_H */
|
||||
879
kernel/atomqueue.c
Executable file
879
kernel/atomqueue.c
Executable file
@@ -0,0 +1,879 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Local data types */
|
||||
|
||||
typedef struct queue_timer
|
||||
{
|
||||
ATOM_TCB *tcb_ptr; /* Thread which is suspended with timeout */
|
||||
ATOM_QUEUE *queue_ptr; /* Queue the thread is interested in */
|
||||
ATOM_TCB **suspQ; /* TCB queue which thread is suspended on */
|
||||
} QUEUE_TIMER;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static uint8_t queue_remove (ATOM_QUEUE *qptr, uint8_t* msgptr);
|
||||
static uint8_t queue_insert (ATOM_QUEUE *qptr, uint8_t* msgptr);
|
||||
static void atomQueueTimerCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b atomQueueCreate
|
||||
*
|
||||
* Initialises a queue object.
|
||||
*
|
||||
* Must be called before calling any other queue library routines on a
|
||||
* queue. Objects can be deleted later using atomQueueDelete().
|
||||
*
|
||||
* Does not allocate storage, the caller provides the queue object.
|
||||
*
|
||||
* Callers pass in their own buffer area for storing the queue messages while
|
||||
* in transit between threads. The provided storage must be large enough to
|
||||
* store (\c unit_size * \c max_num_mgs) bytes. i.e. the storage area will be
|
||||
* used for up to \c max_num_msgs messages each of size \c unit_size.
|
||||
*
|
||||
* Queues use a fixed-size message.
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @param[in] qptr Pointer to queue object
|
||||
* @param[in] buff_ptr Pointer to buffer storage area
|
||||
* @param[in] unit_size Size in bytes of each queue message
|
||||
* @param[in] max_num_msgs Maximum number of messages in the queue
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
*/
|
||||
uint8_t atomQueueCreate (ATOM_QUEUE *qptr, uint8_t *buff_ptr, uint32_t unit_size, uint32_t max_num_msgs)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Parameter check */
|
||||
if ((qptr == NULL) || (buff_ptr == NULL))
|
||||
{
|
||||
/* Bad pointers */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else if ((unit_size == 0) || (max_num_msgs == 0))
|
||||
{
|
||||
/* Bad values */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Store the queue details */
|
||||
qptr->buff_ptr = buff_ptr;
|
||||
qptr->unit_size = unit_size;
|
||||
qptr->max_num_msgs = max_num_msgs;
|
||||
|
||||
/* Initialise the suspended threads queues */
|
||||
qptr->putSuspQ = NULL;
|
||||
qptr->getSuspQ = NULL;
|
||||
|
||||
/* Initialise the insert/remove pointers */
|
||||
qptr->insert_index = 0;
|
||||
qptr->remove_index = 0;
|
||||
qptr->num_msgs_stored = 0;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomQueueDelete
|
||||
*
|
||||
* Deletes a queue object.
|
||||
*
|
||||
* Any threads currently suspended on the queue will be woken up with
|
||||
* return status ATOM_ERR_DELETED. If called at thread context then the
|
||||
* scheduler will be called during this function which may schedule in one
|
||||
* of the woken threads depending on relative priorities.
|
||||
*
|
||||
* This function can be called from interrupt context, but loops internally
|
||||
* waking up all threads blocking on the queue, so the potential
|
||||
* execution cycles cannot be determined in advance.
|
||||
*
|
||||
* @param[in] qptr Pointer to queue object
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout on a woken thread
|
||||
*/
|
||||
uint8_t atomQueueDelete (ATOM_QUEUE *qptr)
|
||||
{
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *tcb_ptr;
|
||||
uint8_t woken_threads = FALSE;
|
||||
|
||||
/* Parameter check */
|
||||
if (qptr == NULL)
|
||||
{
|
||||
/* Bad pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default to success status unless errors occur during wakeup */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Wake up all suspended tasks */
|
||||
while (1)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Check if any threads are suspended */
|
||||
if (((tcb_ptr = tcbDequeueHead (&qptr->getSuspQ)) != NULL)
|
||||
|| ((tcb_ptr = tcbDequeueHead (&qptr->putSuspQ)) != NULL))
|
||||
{
|
||||
/* A thread is waiting on a suspend queue */
|
||||
|
||||
/* Return error status to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_ERR_DELETED;
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Quit the loop, returning error */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if (tcb_ptr->suspend_timo_cb)
|
||||
{
|
||||
/* Cancel the callback */
|
||||
if (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Quit the loop, returning error */
|
||||
status = ATOM_ERR_TIMER;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Request a reschedule */
|
||||
woken_threads = TRUE;
|
||||
}
|
||||
|
||||
/* No more suspended threads */
|
||||
else
|
||||
{
|
||||
/* Exit critical region and quit the loop */
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call scheduler if any threads were woken up */
|
||||
if (woken_threads == TRUE)
|
||||
{
|
||||
/**
|
||||
* Only call the scheduler if we are in thread context, otherwise
|
||||
* it will be called on exiting the ISR by atomIntExit().
|
||||
*/
|
||||
if (atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomQueueGet
|
||||
*
|
||||
* Attempt to retrieve a message from a queue.
|
||||
*
|
||||
* If the queue is currently empty, the call will do one of the following
|
||||
* depending on the \c timeout value specified:
|
||||
*
|
||||
* \c timeout == 0 : Call will block until a message is available
|
||||
* \c timeout > 0 : Call will block until a message or the specified timeout
|
||||
* \c timeout == -1 : Return immediately if no message is on the queue
|
||||
*
|
||||
* If a maximum timeout value is specified (\c timeout > 0), and no message
|
||||
* is present on the queue for the specified number of system ticks, the
|
||||
* call will return with \c ATOM_TIMEOUT.
|
||||
*
|
||||
* This function can only be called from interrupt context if the \c timeout
|
||||
* parameter is -1 (in which case it does not block).
|
||||
*
|
||||
* @param[in] qptr Pointer to queue object
|
||||
* @param[in] timeout Max system ticks to block (0 = forever, -1 = no block)
|
||||
* @param[out] msgptr Pointer to which the received message will be copied
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_TIMEOUT Queue wait timed out before being woken
|
||||
* @retval ATOM_WOULDBLOCK Called with timeout == -1 but queue was empty
|
||||
* @retval ATOM_ERR_DELETED Queue was deleted while suspended
|
||||
* @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue
|
||||
* @retval ATOM_ERR_TIMER Problem registering the timeout
|
||||
*/
|
||||
uint8_t atomQueueGet (ATOM_QUEUE *qptr, int32_t timeout, uint8_t *msgptr)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
QUEUE_TIMER timer_data;
|
||||
ATOM_TIMER timer_cb;
|
||||
ATOM_TCB *curr_tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if ((qptr == NULL) || (msgptr == NULL))
|
||||
{
|
||||
/* Bad pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Protect access to the queue object and OS queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* If no messages on the queue, block the calling thread */
|
||||
if (qptr->num_msgs_stored == 0)
|
||||
{
|
||||
/* If called with timeout >= 0, we should block */
|
||||
if (timeout >= 0)
|
||||
{
|
||||
/* Queue is empty, block the calling thread */
|
||||
|
||||
/* Get the current TCB */
|
||||
curr_tcb_ptr = atomCurrentContext();
|
||||
|
||||
/* Check we are actually in thread context */
|
||||
if (curr_tcb_ptr)
|
||||
{
|
||||
/* Add current thread to the list suspended on receives */
|
||||
if (tcbEnqueuePriority (&qptr->getSuspQ, curr_tcb_ptr) == ATOM_OK)
|
||||
{
|
||||
/* Set suspended status for the current thread */
|
||||
curr_tcb_ptr->suspended = TRUE;
|
||||
|
||||
/* Track errors */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Register a timer callback if requested */
|
||||
if (timeout)
|
||||
{
|
||||
/**
|
||||
* Fill out the data needed by the callback to
|
||||
* wake us up.
|
||||
*/
|
||||
timer_data.tcb_ptr = curr_tcb_ptr;
|
||||
timer_data.queue_ptr = qptr;
|
||||
timer_data.suspQ = &qptr->getSuspQ;
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = atomQueueTimerCallback;
|
||||
timer_cb.cb_data = (POINTER)&timer_data;
|
||||
timer_cb.cb_ticks = timeout;
|
||||
|
||||
/**
|
||||
* Store the timer details in the TCB so that we
|
||||
* can cancel the timer callback if the queue is
|
||||
* put before the timeout occurs.
|
||||
*/
|
||||
curr_tcb_ptr->suspend_timo_cb = &timer_cb;
|
||||
|
||||
/* Register a callback on timeout */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
/* Timer registration failed */
|
||||
status = ATOM_ERR_TIMER;
|
||||
|
||||
/* Clean up and return to the caller */
|
||||
(void)tcbDequeueEntry (&qptr->getSuspQ, curr_tcb_ptr);
|
||||
curr_tcb_ptr->suspended = FALSE;
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set no timeout requested */
|
||||
else
|
||||
{
|
||||
/* No need to cancel timeouts on this one */
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Check no errors occurred */
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/**
|
||||
* Current thread now blocking, schedule in a new
|
||||
* one. We already know we are in thread context
|
||||
* so can call the scheduler from here.
|
||||
*/
|
||||
atomSched (FALSE);
|
||||
|
||||
/**
|
||||
* Normal atomQueuePut() wakeups will set ATOM_OK
|
||||
* status, while timeouts will set ATOM_TIMEOUT
|
||||
* and queue deletions will set ATOM_ERR_DELETED.
|
||||
*/
|
||||
status = curr_tcb_ptr->suspend_wake_status;
|
||||
|
||||
/**
|
||||
* Check suspend_wake_status. If it is ATOM_OK
|
||||
* then we were woken because a message has been
|
||||
* put on the queue and we can now copy it out.
|
||||
* Otherwise we were woken because we timed out
|
||||
* waiting for a message, or the queue was
|
||||
* deleted, so we should just quit.
|
||||
*/
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START();
|
||||
|
||||
/* Copy the message out of the queue */
|
||||
status = queue_remove (qptr, msgptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There was an error putting this thread on the suspend list */
|
||||
CRITICAL_END ();
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not currently in thread context, can't suspend */
|
||||
CRITICAL_END ();
|
||||
status = ATOM_ERR_CONTEXT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* timeout == -1, requested not to block and queue is empty */
|
||||
CRITICAL_END();
|
||||
status = ATOM_WOULDBLOCK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No need to block, there is a message to copy out of the queue */
|
||||
status = queue_remove (qptr, msgptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* The scheduler may now make a policy decision to thread
|
||||
* switch if we are currently in thread context. If we are
|
||||
* in interrupt context it will be handled by atomIntExit().
|
||||
*/
|
||||
if (atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomQueuePut
|
||||
*
|
||||
* Attempt to put a message onto a queue.
|
||||
*
|
||||
* If the queue is currently full, the call will do one of the following
|
||||
* depending on the \c timeout value specified:
|
||||
*
|
||||
* \c timeout == 0 : Call will block until space is available
|
||||
* \c timeout > 0 : Call will block until space or the specified timeout
|
||||
* \c timeout == -1 : Return immediately if the queue is full
|
||||
*
|
||||
* If a maximum timeout value is specified (\c timeout > 0), and no space
|
||||
* is available on the queue for the specified number of system ticks, the
|
||||
* call will return with \c ATOM_TIMEOUT.
|
||||
*
|
||||
* This function can only be called from interrupt context if the \c timeout
|
||||
* parameter is -1 (in which case it does not block and may fail to post a
|
||||
* message if the queue is full).
|
||||
*
|
||||
* @param[in] qptr Pointer to queue object
|
||||
* @param[in] timeout Max system ticks to block (0 = forever, -1 = no block)
|
||||
* @param[out] msgptr Pointer from which the message should be copied out
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_WOULDBLOCK Called with timeout == -1 but queue was full
|
||||
* @retval ATOM_TIMEOUT Queue wait timed out before being woken
|
||||
* @retval ATOM_ERR_DELETED Queue was deleted while suspended
|
||||
* @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue
|
||||
* @retval ATOM_ERR_TIMER Problem registering the timeout
|
||||
*/
|
||||
uint8_t atomQueuePut (ATOM_QUEUE *qptr, int32_t timeout, uint8_t *msgptr)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
QUEUE_TIMER timer_data;
|
||||
ATOM_TIMER timer_cb;
|
||||
ATOM_TCB *curr_tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if ((qptr == NULL) || (msgptr == NULL))
|
||||
{
|
||||
/* Bad pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Protect access to the queue object and OS queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* If queue is full, block the calling thread */
|
||||
if (qptr->num_msgs_stored == qptr->max_num_msgs)
|
||||
{
|
||||
/* If called with timeout >= 0, we should block */
|
||||
if (timeout >= 0)
|
||||
{
|
||||
/* Queue is full, block the calling thread */
|
||||
|
||||
/* Get the current TCB */
|
||||
curr_tcb_ptr = atomCurrentContext();
|
||||
|
||||
/* Check we are actually in thread context */
|
||||
if (curr_tcb_ptr)
|
||||
{
|
||||
/* Add current thread to the suspend list on sends */
|
||||
if (tcbEnqueuePriority (&qptr->putSuspQ, curr_tcb_ptr) == ATOM_OK)
|
||||
{
|
||||
/* Set suspended status for the current thread */
|
||||
curr_tcb_ptr->suspended = TRUE;
|
||||
|
||||
/* Track errors */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Register a timer callback if requested */
|
||||
if (timeout)
|
||||
{
|
||||
/**
|
||||
* Fill out the data needed by the callback to
|
||||
* wake us up.
|
||||
*/
|
||||
timer_data.tcb_ptr = curr_tcb_ptr;
|
||||
timer_data.queue_ptr = qptr;
|
||||
timer_data.suspQ = &qptr->putSuspQ;
|
||||
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = atomQueueTimerCallback;
|
||||
timer_cb.cb_data = (POINTER)&timer_data;
|
||||
timer_cb.cb_ticks = timeout;
|
||||
|
||||
/**
|
||||
* Store the timer details in the TCB so that we
|
||||
* can cancel the timer callback if a message is
|
||||
* removed from the queue before the timeout
|
||||
* occurs.
|
||||
*/
|
||||
curr_tcb_ptr->suspend_timo_cb = &timer_cb;
|
||||
|
||||
/* Register a callback on timeout */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
/* Timer registration failed */
|
||||
status = ATOM_ERR_TIMER;
|
||||
|
||||
/* Clean up and return to the caller */
|
||||
(void)tcbDequeueEntry (&qptr->putSuspQ, curr_tcb_ptr);
|
||||
curr_tcb_ptr->suspended = FALSE;
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set no timeout requested */
|
||||
else
|
||||
{
|
||||
/* No need to cancel timeouts on this one */
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Check timer registration was successful */
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/**
|
||||
* Current thread now blocking, schedule in a new
|
||||
* one. We already know we are in thread context
|
||||
* so can call the scheduler from here.
|
||||
*/
|
||||
atomSched (FALSE);
|
||||
|
||||
/**
|
||||
* Normal atomQueueGet() wakeups will set ATOM_OK
|
||||
* status, while timeouts will set ATOM_TIMEOUT
|
||||
* and queue deletions will set ATOM_ERR_DELETED.
|
||||
*/
|
||||
status = curr_tcb_ptr->suspend_wake_status;
|
||||
|
||||
/**
|
||||
* Check suspend_wake_status. If it is ATOM_OK
|
||||
* then we were woken because a message has been
|
||||
* removed from the queue and we can now add ours.
|
||||
* Otherwise we were woken because we timed out
|
||||
* waiting for a message, or the queue was
|
||||
* deleted, so we should just quit.
|
||||
*/
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START();
|
||||
|
||||
/* Copy the message into the queue */
|
||||
status = queue_insert (qptr, msgptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There was an error putting this thread on the suspend list */
|
||||
CRITICAL_END ();
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not currently in thread context, can't suspend */
|
||||
CRITICAL_END ();
|
||||
status = ATOM_ERR_CONTEXT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* timeout == -1, cannot block. Just return queue is full */
|
||||
CRITICAL_END();
|
||||
status = ATOM_WOULDBLOCK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No need to block, there is space to copy into the queue */
|
||||
status = queue_insert (qptr, msgptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* The scheduler may now make a policy decision to thread
|
||||
* switch if we are currently in thread context. If we are
|
||||
* in interrupt context it will be handled by atomIntExit().
|
||||
*/
|
||||
if (atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomQueueTimerCallback
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Timeouts on suspended threads are notified by the timer system through
|
||||
* this generic callback. The timer system calls us back with a pointer to
|
||||
* the relevant \c QUEUE_TIMER object which is used to retrieve the
|
||||
* queue details.
|
||||
*
|
||||
* @param[in] cb_data Pointer to a QUEUE_TIMER object
|
||||
*/
|
||||
static void atomQueueTimerCallback (POINTER cb_data)
|
||||
{
|
||||
QUEUE_TIMER *timer_data_ptr;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Get the QUEUE_TIMER structure pointer */
|
||||
timer_data_ptr = (QUEUE_TIMER *)cb_data;
|
||||
|
||||
/* Check parameter is valid */
|
||||
if (timer_data_ptr)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Set status to indicate to the waiting thread that it timed out */
|
||||
timer_data_ptr->tcb_ptr->suspend_wake_status = ATOM_TIMEOUT;
|
||||
|
||||
/* Flag as no timeout registered */
|
||||
timer_data_ptr->tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/**
|
||||
* Remove this thread from the queue's suspend list. Handles threads
|
||||
* suspended on the receive list as well as the send list.
|
||||
*/
|
||||
(void)tcbDequeueEntry (timer_data_ptr->suspQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
(void)tcbEnqueuePriority (&tcbReadyQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* Note that we don't call the scheduler now as it will be called
|
||||
* when we exit the ISR by atomIntExit().
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b queue_remove
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Removes a message from a queue. Assumes that there is a message present,
|
||||
* which is already checked by the calling functions with interrupts locked
|
||||
* out.
|
||||
*
|
||||
* Also wakes up a suspended thread if there are any waiting to send on the
|
||||
* queue.
|
||||
*
|
||||
* Assumes interrupts are already locked out.
|
||||
*
|
||||
* @param[in] qptr Pointer to an ATOM_QUEUE object
|
||||
* @param[in] msgptr Destination pointer for the message to be copied into
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout
|
||||
*/
|
||||
static uint8_t queue_remove (ATOM_QUEUE *qptr, uint8_t* msgptr)
|
||||
{
|
||||
uint8_t status;
|
||||
ATOM_TCB *tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if ((qptr == NULL) || (msgptr == NULL))
|
||||
{
|
||||
/* Bad pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There is a message on the queue, copy it out */
|
||||
memcpy (msgptr, (qptr->buff_ptr + qptr->remove_index), qptr->unit_size);
|
||||
qptr->remove_index += qptr->unit_size;
|
||||
qptr->num_msgs_stored--;
|
||||
|
||||
/* Check if the remove index should now wrap to the beginning */
|
||||
if (qptr->remove_index >= (qptr->unit_size * qptr->max_num_msgs))
|
||||
qptr->remove_index = 0;
|
||||
|
||||
/**
|
||||
* If there are threads waiting to send, wake one up now. Waiting
|
||||
* threads are woken up in priority order, with same-priority
|
||||
* threads woken up in FIFO order.
|
||||
*/
|
||||
tcb_ptr = tcbDequeueHead (&qptr->putSuspQ);
|
||||
if (tcb_ptr)
|
||||
{
|
||||
/* Move the waiting thread to the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) == ATOM_OK)
|
||||
{
|
||||
/* Set OK status to be returned to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_OK;
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if ((tcb_ptr->suspend_timo_cb != NULL)
|
||||
&& (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK))
|
||||
{
|
||||
/* There was a problem cancelling a timeout */
|
||||
status = ATOM_ERR_TIMER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* There was a problem putting the thread on the ready
|
||||
* queue.
|
||||
*/
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There were no threads waiting to send */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b queue_insert
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Inserts a message onto a queue. Assumes that the queue has space for one
|
||||
* message, which has already been checked by the calling function with
|
||||
* interrupts locked out.
|
||||
*
|
||||
* Also wakes up a suspended thread if there are any waiting to receive on the
|
||||
* queue.
|
||||
*
|
||||
* Assumes interrupts are already locked out.
|
||||
*
|
||||
* @param[in] qptr Pointer to an ATOM_QUEUE object
|
||||
* @param[in] msgptr Source pointer for the message to be copied out of
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout
|
||||
*/
|
||||
static uint8_t queue_insert (ATOM_QUEUE *qptr, uint8_t* msgptr)
|
||||
{
|
||||
uint8_t status;
|
||||
ATOM_TCB *tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if ((qptr == NULL) || (msgptr == NULL))
|
||||
{
|
||||
/* Bad pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There is space in the queue, copy it in */
|
||||
memcpy ((qptr->buff_ptr + qptr->insert_index), msgptr, qptr->unit_size);
|
||||
qptr->insert_index += qptr->unit_size;
|
||||
qptr->num_msgs_stored++;
|
||||
|
||||
/* Check if the insert index should now wrap to the beginning */
|
||||
if (qptr->insert_index >= (qptr->unit_size * qptr->max_num_msgs))
|
||||
qptr->insert_index = 0;
|
||||
|
||||
/**
|
||||
* If there are threads waiting to receive, wake one up now. Waiting
|
||||
* threads are woken up in priority order, with same-priority
|
||||
* threads woken up in FIFO order.
|
||||
*/
|
||||
tcb_ptr = tcbDequeueHead (&qptr->getSuspQ);
|
||||
if (tcb_ptr)
|
||||
{
|
||||
/* Move the waiting thread to the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) == ATOM_OK)
|
||||
{
|
||||
/* Set OK status to be returned to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_OK;
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if ((tcb_ptr->suspend_timo_cb != NULL)
|
||||
&& (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK))
|
||||
{
|
||||
/* There was a problem cancelling a timeout */
|
||||
status = ATOM_ERR_TIMER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* There was a problem putting the thread on the ready
|
||||
* queue.
|
||||
*/
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There were no threads waiting to send */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
49
kernel/atomqueue.h
Executable file
49
kernel/atomqueue.h
Executable file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __ATOM_QUEUE_H
|
||||
#define __ATOM_QUEUE_H
|
||||
|
||||
typedef struct atom_queue
|
||||
{
|
||||
ATOM_TCB * putSuspQ; /* Queue of threads waiting to send */
|
||||
ATOM_TCB * getSuspQ; /* Queue of threads waiting to receive */
|
||||
uint8_t * buff_ptr; /* Pointer to queue data area */
|
||||
uint32_t unit_size; /* Size of each message */
|
||||
uint32_t max_num_msgs; /* Max number of storable messages */
|
||||
uint32_t insert_index; /* Next byte index to insert into */
|
||||
uint32_t remove_index; /* Next byte index to remove from */
|
||||
uint32_t num_msgs_stored;/* Number of messages stored */
|
||||
} ATOM_QUEUE;
|
||||
|
||||
extern uint8_t atomQueueCreate (ATOM_QUEUE *qptr, uint8_t *buff_ptr, uint32_t unit_size, uint32_t max_num_msgs);
|
||||
extern uint8_t atomQueueDelete (ATOM_QUEUE *qptr);
|
||||
extern uint8_t atomQueueGet (ATOM_QUEUE *qptr, int32_t timeout, uint8_t *msgptr);
|
||||
extern uint8_t atomQueuePut (ATOM_QUEUE *qptr, int32_t timeout, uint8_t *msgptr);
|
||||
|
||||
#endif /* __ATOM_QUEUE_H */
|
||||
618
kernel/atomsem.c
Executable file
618
kernel/atomsem.c
Executable file
@@ -0,0 +1,618 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Local data types */
|
||||
|
||||
typedef struct sem_timer
|
||||
{
|
||||
ATOM_TCB *tcb_ptr; /* Thread which is suspended with timeout */
|
||||
ATOM_SEM *sem_ptr; /* Semaphore the thread is suspended on */
|
||||
} SEM_TIMER;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static void atomSemTimerCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSemCreate
|
||||
*
|
||||
* Initialises a semaphore object.
|
||||
*
|
||||
* Must be called before calling any other semaphore library routines on a
|
||||
* semaphore. Objects can be deleted later using atomSemDelete().
|
||||
*
|
||||
* Does not allocate storage, the caller provides the semaphore object.
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @param[in] sem Pointer to semaphore object
|
||||
* @param[in] initial_count Initial count value
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
*/
|
||||
uint8_t atomSemCreate (ATOM_SEM *sem, uint8_t initial_count)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Parameter check */
|
||||
if (sem == NULL)
|
||||
{
|
||||
/* Bad semaphore pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the initial count */
|
||||
sem->count = initial_count;
|
||||
|
||||
/* Initialise the suspended threads queue */
|
||||
sem->suspQ = NULL;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSemDelete
|
||||
*
|
||||
* Deletes a semaphore object.
|
||||
*
|
||||
* Any threads currently suspended on the semaphore will be woken up with
|
||||
* return status ATOM_ERR_DELETED. If called at thread context then the
|
||||
* scheduler will be called during this function which may schedule in one
|
||||
* of the woken threads depending on relative priorities.
|
||||
*
|
||||
* This function can be called from interrupt context, but loops internally
|
||||
* waking up all threads blocking on the semaphore, so the potential
|
||||
* execution cycles cannot be determined in advance.
|
||||
*
|
||||
* @param[in] sem Pointer to semaphore object
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout on a woken thread
|
||||
*/
|
||||
uint8_t atomSemDelete (ATOM_SEM *sem)
|
||||
{
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *tcb_ptr;
|
||||
uint8_t woken_threads = FALSE;
|
||||
|
||||
/* Parameter check */
|
||||
if (sem == NULL)
|
||||
{
|
||||
/* Bad semaphore pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default to success status unless errors occur during wakeup */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Wake up all suspended tasks */
|
||||
while (1)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Check if any threads are suspended */
|
||||
tcb_ptr = tcbDequeueHead (&sem->suspQ);
|
||||
|
||||
/* A thread is suspended on the semaphore */
|
||||
if (tcb_ptr)
|
||||
{
|
||||
/* Return error status to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_ERR_DELETED;
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Quit the loop, returning error */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if (tcb_ptr->suspend_timo_cb)
|
||||
{
|
||||
/* Cancel the callback */
|
||||
if (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Quit the loop, returning error */
|
||||
status = ATOM_ERR_TIMER;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Request a reschedule */
|
||||
woken_threads = TRUE;
|
||||
}
|
||||
|
||||
/* No more suspended threads */
|
||||
else
|
||||
{
|
||||
/* Exit critical region and quit the loop */
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call scheduler if any threads were woken up */
|
||||
if (woken_threads == TRUE)
|
||||
{
|
||||
/**
|
||||
* Only call the scheduler if we are in thread context, otherwise
|
||||
* it will be called on exiting the ISR by atomIntExit().
|
||||
*/
|
||||
if (atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSemGet
|
||||
*
|
||||
* Perform a get operation on a semaphore.
|
||||
*
|
||||
* This decrements the current count value for the semaphore and returns.
|
||||
* If the count value is already zero then the call will block until the
|
||||
* count is incremented by another thread, or until the specified \c timeout
|
||||
* is reached. Blocking threads will also be woken if the semaphore is
|
||||
* deleted by another thread while blocking.
|
||||
*
|
||||
* Depending on the \c timeout value specified the call will do one of
|
||||
* the following if the count value is zero:
|
||||
*
|
||||
* \c timeout == 0 : Call will block until the count is non-zero
|
||||
* \c timeout > 0 : Call will block until non-zero up to the specified timeout
|
||||
* \c timeout == -1 : Return immediately if the count is zero
|
||||
*
|
||||
* If the call needs to block and \c timeout is zero, it will block
|
||||
* indefinitely until atomSemPut() or atomSemDelete() is called on the
|
||||
* semaphore.
|
||||
*
|
||||
* If the call needs to block and \c timeout is non-zero, the call will only
|
||||
* block for the specified number of system ticks after which time, if the
|
||||
* thread was not already woken, the call will return with \c ATOM_TIMEOUT.
|
||||
*
|
||||
* If the call would normally block and \c timeout is -1, the call will
|
||||
* return immediately with \c ATOM_WOULDBLOCK.
|
||||
*
|
||||
* This function can only be called from interrupt context if the \c timeout
|
||||
* parameter is -1 (in which case it does not block).
|
||||
*
|
||||
* @param[in] sem Pointer to semaphore object
|
||||
* @param[in] timeout Max system ticks to block (0 = forever)
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_TIMEOUT Semaphore timed out before being woken
|
||||
* @retval ATOM_WOULDBLOCK Called with timeout == -1 but count is zero
|
||||
* @retval ATOM_ERR_DELETED Semaphore was deleted while suspended
|
||||
* @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue
|
||||
* @retval ATOM_ERR_TIMER Problem registering the timeout
|
||||
*/
|
||||
uint8_t atomSemGet (ATOM_SEM *sem, int32_t timeout)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
SEM_TIMER timer_data;
|
||||
ATOM_TIMER timer_cb;
|
||||
ATOM_TCB *curr_tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if (sem == NULL)
|
||||
{
|
||||
/* Bad semaphore pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Protect access to the semaphore object and OS queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* If count is zero, block the calling thread */
|
||||
if (sem->count == 0)
|
||||
{
|
||||
/* If called with timeout >= 0, we should block */
|
||||
if (timeout >= 0)
|
||||
{
|
||||
/* Count is zero, block the calling thread */
|
||||
|
||||
/* Get the current TCB */
|
||||
curr_tcb_ptr = atomCurrentContext();
|
||||
|
||||
/* Check we are actually in thread context */
|
||||
if (curr_tcb_ptr)
|
||||
{
|
||||
/* Add current thread to the suspend list on this semaphore */
|
||||
if (tcbEnqueuePriority (&sem->suspQ, curr_tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* There was an error putting this thread on the suspend list */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set suspended status for the current thread */
|
||||
curr_tcb_ptr->suspended = TRUE;
|
||||
|
||||
/* Track errors */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Register a timer callback if requested */
|
||||
if (timeout)
|
||||
{
|
||||
/* Fill out the data needed by the callback to wake us up */
|
||||
timer_data.tcb_ptr = curr_tcb_ptr;
|
||||
timer_data.sem_ptr = sem;
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = atomSemTimerCallback;
|
||||
timer_cb.cb_data = (POINTER)&timer_data;
|
||||
timer_cb.cb_ticks = timeout;
|
||||
|
||||
/**
|
||||
* Store the timer details in the TCB so that we can
|
||||
* cancel the timer callback if the semaphore is put
|
||||
* before the timeout occurs.
|
||||
*/
|
||||
curr_tcb_ptr->suspend_timo_cb = &timer_cb;
|
||||
|
||||
/* Register a callback on timeout */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
/* Timer registration failed */
|
||||
status = ATOM_ERR_TIMER;
|
||||
|
||||
/* Clean up and return to the caller */
|
||||
(void)tcbDequeueEntry (&sem->suspQ, curr_tcb_ptr);
|
||||
curr_tcb_ptr->suspended = FALSE;
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set no timeout requested */
|
||||
else
|
||||
{
|
||||
/* No need to cancel timeouts on this one */
|
||||
curr_tcb_ptr->suspend_timo_cb = NULL;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Check no errors have occurred */
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/**
|
||||
* Current thread now blocking, schedule in a new
|
||||
* one. We already know we are in thread context
|
||||
* so can call the scheduler from here.
|
||||
*/
|
||||
atomSched (FALSE);
|
||||
|
||||
/**
|
||||
* Normal atomSemPut() wakeups will set ATOM_OK status,
|
||||
* while timeouts will set ATOM_TIMEOUT and semaphore
|
||||
* deletions will set ATOM_ERR_DELETED.
|
||||
*/
|
||||
status = curr_tcb_ptr->suspend_wake_status;
|
||||
|
||||
/**
|
||||
* If we have been woken up with ATOM_OK then
|
||||
* another thread incremented the semaphore and
|
||||
* handed control to this thread. In theory the
|
||||
* the posting thread increments the counter and
|
||||
* as soon as this thread wakes up we decrement
|
||||
* the counter here, but to prevent another
|
||||
* thread preempting this thread and decrementing
|
||||
* the semaphore before this section was
|
||||
* scheduled back in, we emulate the increment
|
||||
* and decrement by not incrementing in the
|
||||
* atomSemPut() and not decrementing here. The
|
||||
* count remains zero throughout preventing other
|
||||
* threads preempting before we decrement the
|
||||
* count again.
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Not currently in thread context, can't suspend */
|
||||
status = ATOM_ERR_CONTEXT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* timeout == -1, requested not to block and count is zero */
|
||||
CRITICAL_END();
|
||||
status = ATOM_WOULDBLOCK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Count is non-zero, just decrement it and return to calling thread */
|
||||
sem->count--;
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSemPut
|
||||
*
|
||||
* Perform a put operation on a semaphore.
|
||||
*
|
||||
* This increments the current count value for the semaphore and returns.
|
||||
*
|
||||
* If the count value was previously zero and there are threads blocking on the
|
||||
* semaphore, the call will wake up the highest priority thread suspended. Only
|
||||
* one thread is woken per call to atomSemPut(). If multiple threads of the
|
||||
* same priority are suspended, they are woken in order of suspension (FIFO).
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @param[in] sem Pointer to semaphore object
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_OVF The semaphore count would have overflowed (>255)
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
||||
* @retval ATOM_ERR_TIMER Problem cancelling a timeout for a woken thread
|
||||
*/
|
||||
uint8_t atomSemPut (ATOM_SEM * sem)
|
||||
{
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *tcb_ptr;
|
||||
|
||||
/* Check parameters */
|
||||
if (sem == NULL)
|
||||
{
|
||||
/* Bad semaphore pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Protect access to the semaphore object and OS queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* If any threads are blocking on the semaphore, wake up one */
|
||||
if (sem->suspQ)
|
||||
{
|
||||
/**
|
||||
* Threads are woken up in priority order, with a FIFO system
|
||||
* used on same priority threads. We always take the head,
|
||||
* ordering is taken care of by an ordered list enqueue.
|
||||
*/
|
||||
tcb_ptr = tcbDequeueHead (&sem->suspQ);
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* There was a problem putting the thread on the ready queue */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set OK status to be returned to the waiting thread */
|
||||
tcb_ptr->suspend_wake_status = ATOM_OK;
|
||||
|
||||
/* If there's a timeout on this suspension, cancel it */
|
||||
if ((tcb_ptr->suspend_timo_cb != NULL)
|
||||
&& (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK))
|
||||
{
|
||||
/* There was a problem cancelling a timeout on this semaphore */
|
||||
status = ATOM_ERR_TIMER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Flag as no timeout registered */
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* The scheduler may now make a policy decision to thread
|
||||
* switch if we are currently in thread context. If we are
|
||||
* in interrupt context it will be handled by atomIntExit().
|
||||
*/
|
||||
if (atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* If no threads waiting, just increment the count and return */
|
||||
else
|
||||
{
|
||||
/* Check for count overflow */
|
||||
if (sem->count == 255)
|
||||
{
|
||||
/* Don't increment, just return error status */
|
||||
status = ATOM_ERR_OVF;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Increment the count and return success */
|
||||
sem->count++;
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSemResetCount
|
||||
*
|
||||
* Set a new count value on a semaphore.
|
||||
*
|
||||
* Care must be taken when using this function, as there may be threads
|
||||
* suspended on the semaphore. In general it should only be used once a
|
||||
* semaphore is out of use.
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @param[in] sem Pointer to semaphore object
|
||||
* @param[in] count New count value
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameter
|
||||
*/
|
||||
uint8_t atomSemResetCount (ATOM_SEM *sem, uint8_t count)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Parameter check */
|
||||
if (sem == NULL)
|
||||
{
|
||||
/* Bad semaphore pointer */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the count */
|
||||
sem->count = count;
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSemTimerCallback
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Timeouts on suspended threads are notified by the timer system through
|
||||
* this generic callback. The timer system calls us back with a pointer to
|
||||
* the relevant \c SEM_TIMER object which is used to retrieve the
|
||||
* semaphore details.
|
||||
*
|
||||
* @param[in] cb_data Pointer to a SEM_TIMER object
|
||||
*/
|
||||
static void atomSemTimerCallback (POINTER cb_data)
|
||||
{
|
||||
SEM_TIMER *timer_data_ptr;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Get the SEM_TIMER structure pointer */
|
||||
timer_data_ptr = (SEM_TIMER *)cb_data;
|
||||
|
||||
/* Check parameter is valid */
|
||||
if (timer_data_ptr)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Set status to indicate to the waiting thread that it timed out */
|
||||
timer_data_ptr->tcb_ptr->suspend_wake_status = ATOM_TIMEOUT;
|
||||
|
||||
/* Flag as no timeout registered */
|
||||
timer_data_ptr->tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/* Remove this thread from the semaphore's suspend list */
|
||||
(void)tcbDequeueEntry (&timer_data_ptr->sem_ptr->suspQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
(void)tcbEnqueuePriority (&tcbReadyQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* Note that we don't call the scheduler now as it will be called
|
||||
* when we exit the ISR by atomIntExit().
|
||||
*/
|
||||
}
|
||||
}
|
||||
45
kernel/atomsem.h
Executable file
45
kernel/atomsem.h
Executable file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_SEM_H
|
||||
#define __ATOM_SEM_H
|
||||
|
||||
typedef struct atom_sem
|
||||
{
|
||||
ATOM_TCB * suspQ; /* Queue of threads suspended on this semaphore */
|
||||
uint8_t count; /* Semaphore count */
|
||||
} ATOM_SEM;
|
||||
|
||||
extern uint8_t atomSemCreate (ATOM_SEM *sem, uint8_t initial_count);
|
||||
extern uint8_t atomSemDelete (ATOM_SEM *sem);
|
||||
extern uint8_t atomSemGet (ATOM_SEM *sem, int32_t timeout);
|
||||
extern uint8_t atomSemPut (ATOM_SEM *sem);
|
||||
extern uint8_t atomSemResetCount (ATOM_SEM *sem, uint8_t count);
|
||||
|
||||
#endif /* __ATOM_SEM_H */
|
||||
463
kernel/atomtimer.c
Executable file
463
kernel/atomtimer.c
Executable file
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "atom.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Data types */
|
||||
|
||||
/* Delay callbacks data structure */
|
||||
typedef struct delay_timer
|
||||
{
|
||||
ATOM_TCB *tcb_ptr; /* Thread which is suspended with timeout */
|
||||
|
||||
} DELAY_TIMER;
|
||||
|
||||
|
||||
/* Global data */
|
||||
|
||||
/* Local data */
|
||||
|
||||
/** Pointer to the head of the outstanding timers queue */
|
||||
static ATOM_TIMER *timer_queue = NULL;
|
||||
|
||||
/** Current system tick count */
|
||||
static uint32_t system_ticks = 0;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void atomTimerCallbacks (void);
|
||||
static void atomTimerDelayCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimerRegister
|
||||
*
|
||||
* Register a timer callback.
|
||||
*
|
||||
* Callers should fill out and pass in a timer descriptor, containing
|
||||
* the number of system ticks until they would like a callback, together
|
||||
* with a callback function and optional parameter. The number of ticks
|
||||
* must be greater than zero.
|
||||
*
|
||||
* On the relevant system tick count, the callback function will be
|
||||
* called.
|
||||
*
|
||||
* These timers are used by some of the OS library routines, but they
|
||||
* can also be used by application code requiring timer facilities at
|
||||
* system tick resolution.
|
||||
*
|
||||
* This function can be called from interrupt context, but loops internally
|
||||
* through the time list, so the potential execution cycles cannot be
|
||||
* determined in advance.
|
||||
*
|
||||
* @param[in] timer_ptr Pointer to timer descriptor
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
*/
|
||||
uint8_t atomTimerRegister (ATOM_TIMER *timer_ptr)
|
||||
{
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Parameter check */
|
||||
if ((timer_ptr == NULL) || (timer_ptr->cb_func == NULL)
|
||||
|| (timer_ptr->cb_ticks == 0))
|
||||
{
|
||||
/* Return error */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Protect the list */
|
||||
CRITICAL_START ();
|
||||
|
||||
/*
|
||||
* Enqueue in the list of timers.
|
||||
*
|
||||
* The list is not ordered, all timers are inserted at the start
|
||||
* of the list. On each system tick increment the list is walked
|
||||
* and the remaining ticks count for that timer is decremented.
|
||||
* Once the remaining ticks reaches zero, the timer callback is
|
||||
* made.
|
||||
*/
|
||||
if (timer_queue == NULL)
|
||||
{
|
||||
/* List is empty, insert new head */
|
||||
timer_ptr->next_timer = NULL;
|
||||
timer_queue = timer_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* List has at least one entry, enqueue new timer before */
|
||||
timer_ptr->next_timer = timer_queue;
|
||||
timer_queue = timer_ptr;
|
||||
}
|
||||
|
||||
/* End of list protection */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimerCancel
|
||||
*
|
||||
* Cancel a timer callback previously registered using atomTimerRegister().
|
||||
*
|
||||
* This function can be called from interrupt context, but loops internally
|
||||
* through the time list, so the potential execution cycles cannot be
|
||||
* determined in advance.
|
||||
*
|
||||
* @param[in] timer_ptr Pointer to timer to cancel
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
* @retval ATOM_ERR_NOT_FOUND Timer registration was not found
|
||||
*/
|
||||
uint8_t atomTimerCancel (ATOM_TIMER *timer_ptr)
|
||||
{
|
||||
uint8_t status = ATOM_ERR_NOT_FOUND;
|
||||
ATOM_TIMER *prev_ptr, *next_ptr;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Parameter check */
|
||||
if (timer_ptr == NULL)
|
||||
{
|
||||
/* Return error */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Protect the list */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Walk the list to find the relevant timer */
|
||||
prev_ptr = next_ptr = timer_queue;
|
||||
while (next_ptr)
|
||||
{
|
||||
/* Is this entry the one we're looking for? */
|
||||
if (next_ptr == timer_ptr)
|
||||
{
|
||||
if (next_ptr == timer_queue)
|
||||
{
|
||||
/* We're removing the list head */
|
||||
timer_queue = next_ptr->next_timer;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We're removing a mid or tail TCB */
|
||||
prev_ptr->next_timer = next_ptr->next_timer;
|
||||
}
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move on to the next in the list */
|
||||
prev_ptr = next_ptr;
|
||||
next_ptr = next_ptr->next_timer;
|
||||
|
||||
}
|
||||
|
||||
/* End of list protection */
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimeGet
|
||||
*
|
||||
* Returns the current system tick time.
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @retval Current system tick count
|
||||
|
||||
*/
|
||||
uint32_t atomTimeGet(void)
|
||||
{
|
||||
return (system_ticks);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimeSet
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Sets the current system tick time.
|
||||
*
|
||||
* Currently only required for automated test suite to test
|
||||
* clock behaviour.
|
||||
*
|
||||
* This function can be called from interrupt context.
|
||||
*
|
||||
* @param[in] new_time New system tick time value
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomTimeSet(uint32_t new_time)
|
||||
{
|
||||
system_ticks = new_time;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b 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
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomTimerTick (void)
|
||||
{
|
||||
/* Only do anything if the OS is started */
|
||||
if (atomOSStarted)
|
||||
{
|
||||
/* Increment the system tick count */
|
||||
system_ticks++;
|
||||
|
||||
/* Check for any callbacks that are due */
|
||||
atomTimerCallbacks ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimerDelay
|
||||
*
|
||||
* Suspend a thread for the given number of system ticks.
|
||||
*
|
||||
* Note that the wakeup time is the number of ticks from the current system
|
||||
* tick, therefore, for a one tick delay, the thread may be woken up at any
|
||||
* time between the atomTimerDelay() call and the next system tick. For
|
||||
* a minimum number of ticks, you should specify minimum number of ticks + 1.
|
||||
*
|
||||
* This function can only be called from thread context.
|
||||
*
|
||||
* @param[in] ticks Number of system ticks to delay (must be > 0)
|
||||
*
|
||||
* @retval ATOM_OK Successful delay
|
||||
* @retval ATOM_ERR_PARAM Bad parameter (ticks must be non-zero)
|
||||
* @retval ATOM_ERR_CONTEXT Not called from thread context
|
||||
*/
|
||||
uint8_t atomTimerDelay (uint32_t ticks)
|
||||
{
|
||||
ATOM_TCB *curr_tcb_ptr;
|
||||
ATOM_TIMER timer_cb;
|
||||
DELAY_TIMER timer_data;
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
|
||||
/* Get the current TCB */
|
||||
curr_tcb_ptr = atomCurrentContext();
|
||||
|
||||
/* Parameter check */
|
||||
if (ticks == 0)
|
||||
{
|
||||
/* Return error */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* Check we are actually in thread context */
|
||||
else if (curr_tcb_ptr == NULL)
|
||||
{
|
||||
/* Not currently in thread context, can't suspend */
|
||||
status = ATOM_ERR_CONTEXT;
|
||||
}
|
||||
|
||||
/* Otherwise safe to proceed */
|
||||
else
|
||||
{
|
||||
/* Protect the system queues */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Set suspended status for the current thread */
|
||||
curr_tcb_ptr->suspended = TRUE;
|
||||
|
||||
/* Register the timer callback */
|
||||
|
||||
/* Fill out the data needed by the callback to wake us up */
|
||||
timer_data.tcb_ptr = curr_tcb_ptr;
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = atomTimerDelayCallback;
|
||||
timer_cb.cb_data = (POINTER)&timer_data;
|
||||
timer_cb.cb_ticks = ticks;
|
||||
|
||||
/* Store the timeout callback details, though we don't use it */
|
||||
curr_tcb_ptr->suspend_timo_cb = &timer_cb;
|
||||
|
||||
/* Register the callback */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Timer registration didn't work, won't get a callback */
|
||||
status = ATOM_ERR_TIMER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Successful timer registration */
|
||||
status = ATOM_OK;
|
||||
|
||||
/* Current thread should now block, schedule in another */
|
||||
atomSched (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimerCallbacks
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Find any callbacks that are due and call them up.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void atomTimerCallbacks (void)
|
||||
{
|
||||
ATOM_TIMER *prev_ptr, *next_ptr;
|
||||
|
||||
/*
|
||||
* Walk the list decrementing each timer's remaining ticks count and
|
||||
* looking for due callbacks.
|
||||
*/
|
||||
prev_ptr = next_ptr = timer_queue;
|
||||
while (next_ptr)
|
||||
{
|
||||
/* Is this entry due? */
|
||||
if (--(next_ptr->cb_ticks) == 0)
|
||||
{
|
||||
/* Remove the entry from the timer list */
|
||||
if (next_ptr == timer_queue)
|
||||
{
|
||||
/* We're removing the list head */
|
||||
timer_queue = next_ptr->next_timer;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We're removing a mid or tail timer */
|
||||
prev_ptr->next_timer = next_ptr->next_timer;
|
||||
}
|
||||
|
||||
/* Call the registered callback */
|
||||
if (next_ptr->cb_func)
|
||||
{
|
||||
next_ptr->cb_func (next_ptr->cb_data);
|
||||
}
|
||||
|
||||
/* Do not update prev_ptr, we have just removed this one */
|
||||
|
||||
}
|
||||
|
||||
/* Entry is not due, leave it in there with its count decremented */
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Update prev_ptr to this entry. We will need it if we want
|
||||
* to remove a mid or tail timer.
|
||||
*/
|
||||
prev_ptr = next_ptr;
|
||||
}
|
||||
|
||||
/* Move on to the next in the list */
|
||||
next_ptr = next_ptr->next_timer;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomTimerDelayCallback
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Callback for atomTimerDelay() calls. Wakes up the sleeping threads.
|
||||
*
|
||||
* @param[in] cb_data Callback parameter (DELAY_TIMER ptr for sleeping thread)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void atomTimerDelayCallback (POINTER cb_data)
|
||||
{
|
||||
DELAY_TIMER *timer_data_ptr;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Get the DELAY_TIMER structure pointer */
|
||||
timer_data_ptr = (DELAY_TIMER *)cb_data;
|
||||
|
||||
/* Check parameter is valid */
|
||||
if (timer_data_ptr)
|
||||
{
|
||||
/* Enter critical region */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Put the thread on the ready queue */
|
||||
(void)tcbEnqueuePriority (&tcbReadyQ, timer_data_ptr->tcb_ptr);
|
||||
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* Don't call the scheduler yet. The ISR exit routine will do this
|
||||
* in case there are other callbacks to be made, which may also make
|
||||
* threads ready.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
60
kernel/atomtimer.h
Executable file
60
kernel/atomtimer.h
Executable file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_TIMER_H
|
||||
#define __ATOM_TIMER_H
|
||||
|
||||
#include "atomuser.h"
|
||||
|
||||
/* Callback function prototype */
|
||||
typedef void ( * TIMER_CB_FUNC ) ( POINTER cb_data ) ;
|
||||
|
||||
/* Data structures */
|
||||
|
||||
/* Timer descriptor */
|
||||
typedef struct atom_timer
|
||||
{
|
||||
TIMER_CB_FUNC cb_func; /* Callback function */
|
||||
POINTER cb_data; /* Pointer to callback parameter/data */
|
||||
uint32_t cb_ticks; /* Ticks until callback */
|
||||
|
||||
/* Internal data */
|
||||
struct atom_timer *next_timer; /* Next timer in doubly-linked list */
|
||||
|
||||
} ATOM_TIMER;
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
extern uint8_t atomTimerRegister (ATOM_TIMER *timer_ptr);
|
||||
extern uint8_t atomTimerCancel (ATOM_TIMER *timer_ptr);
|
||||
extern uint8_t atomTimerDelay (uint32_t ticks);
|
||||
extern uint32_t atomTimeGet (void);
|
||||
extern void atomTimeSet (uint32_t new_time);
|
||||
|
||||
#endif /* __ATOM_TIMER_H */
|
||||
60
kernel/atomuser-template.h
Executable file
60
kernel/atomuser-template.h
Executable file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_USER_H
|
||||
#define __ATOM_USER_H
|
||||
|
||||
|
||||
/* Required number of system ticks per second (normally 100 for 10ms tick) */
|
||||
#define SYSTEM_TICKS_PER_SEC 100
|
||||
|
||||
|
||||
/**
|
||||
* Architecture-specific types.
|
||||
* Uses the stdint.h naming convention, so if stdint.h is available on the
|
||||
* platform it is simplest to include it from this header.
|
||||
*/
|
||||
#define uint8_t unsigned char
|
||||
#define uint16_t unsigned short
|
||||
#define uint32_t unsigned long
|
||||
#define uint64_t unsigned long long
|
||||
#define int8_t char
|
||||
#define int16_t short
|
||||
#define int32_t long
|
||||
#define int64_t long long
|
||||
#define POINTER void *
|
||||
|
||||
|
||||
/* Critical region protection */
|
||||
#define CRITICAL_STORE uint8_t sreg
|
||||
#define CRITICAL_START() sreg = SREG; cli();
|
||||
#define CRITICAL_END() SREG = sreg
|
||||
|
||||
|
||||
#endif /* __ATOM_USER_H */
|
||||
1161
ports/avr/Doxyfile
Normal file
1161
ports/avr/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
103
ports/avr/Makefile
Normal file
103
ports/avr/Makefile
Normal file
@@ -0,0 +1,103 @@
|
||||
############
|
||||
# Settings #
|
||||
############
|
||||
|
||||
# Build all test applications:
|
||||
# make
|
||||
#
|
||||
# Program a test application using UISP (appname => test app e.g. sems1):
|
||||
# make program app=appname
|
||||
|
||||
# Location of build tools and atomthreads sources
|
||||
KERNEL_DIR=../../kernel
|
||||
TESTS_DIR=../../tests
|
||||
CC=/usr/bin/avr-gcc
|
||||
OBJCOPY=/usr/bin/avr-objcopy
|
||||
UISP=/usr/bin/uisp
|
||||
UISP_DEV=/dev/ttyUSB0
|
||||
PART=atmega16
|
||||
|
||||
# Directory for built objects
|
||||
BUILD_DIR=build
|
||||
|
||||
# Port/application object files
|
||||
APP_OBJECTS = atomport.o uart.o tests-main.o
|
||||
APP_ASM_OBJECTS = atomport-asm.o
|
||||
|
||||
# Kernel object files
|
||||
KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o
|
||||
|
||||
# Collection of built objects (excluding test applications)
|
||||
ALL_OBJECTS = $(APP_OBJECTS) $(APP_ASM_OBJECTS) $(KERNEL_OBJECTS)
|
||||
BUILT_OBJECTS = $(patsubst %,$(BUILD_DIR)/%,$(ALL_OBJECTS))
|
||||
|
||||
# Test object files (dealt with separately as only one per application build)
|
||||
TEST_OBJECTS = $(notdir $(patsubst %.c,%.o,$(wildcard $(TESTS_DIR)/*.c)))
|
||||
|
||||
# Target application filenames (.elf and .hex) for each test object
|
||||
TEST_ELFS = $(patsubst %.o,%.elf,$(TEST_OBJECTS))
|
||||
TEST_HEXS = $(patsubst %.o,%.hex,$(TEST_OBJECTS))
|
||||
|
||||
# Search build/output directory for dependencies
|
||||
vpath %.o ./$(BUILD_DIR)
|
||||
vpath %.elf ./$(BUILD_DIR)
|
||||
vpath %.hex ./$(BUILD_DIR)
|
||||
|
||||
# GCC flags
|
||||
CFLAGS=-g -mmcu=$(PART) -Wall -Werror
|
||||
|
||||
|
||||
#################
|
||||
# Build targets #
|
||||
#################
|
||||
|
||||
# All tests
|
||||
all: $(BUILD_DIR) $(TEST_HEXS) Makefile
|
||||
|
||||
# Make build/output directory
|
||||
$(BUILD_DIR):
|
||||
mkdir $(BUILD_DIR)
|
||||
|
||||
# Test HEX files (one application build for each test)
|
||||
$(TEST_HEXS): %.hex: %.elf
|
||||
@echo Building $@
|
||||
$(OBJCOPY) -j .text -j .data -O ihex $(BUILD_DIR)/$< $(BUILD_DIR)/$@
|
||||
|
||||
# Test ELF files (one application build for each test)
|
||||
$(TEST_ELFS): %.elf: %.o $(KERNEL_OBJECTS) $(APP_OBJECTS) $(APP_ASM_OBJECTS)
|
||||
$(CC) $(CFLAGS) $(BUILD_DIR)/$(notdir $<) $(BUILT_OBJECTS) --output $(BUILD_DIR)/$@ -Wl,-Map,$(BUILD_DIR)/$(basename $@).map
|
||||
|
||||
# Kernel objects builder
|
||||
$(KERNEL_OBJECTS): %.o: $(KERNEL_DIR)/%.c
|
||||
$(CC) -c $(CFLAGS) -I. $< -o $(BUILD_DIR)/$(notdir $@)
|
||||
|
||||
# Test objects builder
|
||||
$(TEST_OBJECTS): %.o: $(TESTS_DIR)/%.c
|
||||
$(CC) -c $(CFLAGS) -I. -I$(KERNEL_DIR) $< -o $(BUILD_DIR)/$(notdir $@)
|
||||
|
||||
# Application C objects builder
|
||||
$(APP_OBJECTS): %.o: ./%.c
|
||||
$(CC) -c $(CFLAGS) -I. -I$(KERNEL_DIR) -I$(TESTS_DIR) $< -o $(BUILD_DIR)/$(notdir $@)
|
||||
|
||||
# Application asm objects builder
|
||||
$(APP_ASM_OBJECTS): %.o: ./%.s
|
||||
$(CC) -c $(CFLAGS) -x assembler-with-cpp -I. -I$(KERNEL_DIR) $< -o $(BUILD_DIR)/$(notdir $@)
|
||||
|
||||
# .lst file builder
|
||||
%.lst: %.c
|
||||
$(CC) $(CFLAGS) -I. -I$(KERNEL_DIR) -I$(TESTS_DIR) -Wa,-al $< > $@
|
||||
|
||||
# Clean
|
||||
clean:
|
||||
rm -f *.o *.elf *.map *.hex *.bin *.lst
|
||||
rm -rf doxygen-kernel
|
||||
rm -rf doxygen-avr
|
||||
rm -rf build
|
||||
|
||||
# Send to STK500
|
||||
program : $(BUILD_DIR)/$(app).hex
|
||||
$(UISP) -dprog=stk500 -dserial=$(UISP_DEV) -dpart=$(PART) --erase --upload --verify if=$(BUILD_DIR)/$(app).hex
|
||||
|
||||
doxygen:
|
||||
doxygen $(KERNEL_DIR)/Doxyfile
|
||||
doxygen ./Doxyfile
|
||||
362
ports/avr/atomport-asm.s
Normal file
362
ports/avr/atomport-asm.s
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Atomthreads Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
|
||||
.section .text
|
||||
|
||||
/*
|
||||
* \b archContextSwitch
|
||||
*
|
||||
* Architecture-specific context switch routine.
|
||||
*
|
||||
* Note that interrupts are always locked out when this routine is
|
||||
* called. For cooperative switches, the scheduler will have entered
|
||||
* a critical region. For preemptions (called from an ISR), the
|
||||
* ISR will have disabled interrupts on entry.
|
||||
*
|
||||
* Note that this function might have been coded in C, but gcc
|
||||
* was generating prologue and epilogue code to handle the parameters.
|
||||
* Worse, with the naked attribute set it generated half of the
|
||||
* prologue/epilogue. Rather than work around the gcc code generation,
|
||||
* which may change from compiler version to compiler version, we
|
||||
* just write this function in asm, where we have absolute control
|
||||
* over the code generation. Important during register saves/restores.
|
||||
*
|
||||
* @param[in] old_tcb_ptr Pointer to the thread being scheduled out
|
||||
* @param[in] new_tcb_ptr Pointer to the thread being scheduled in
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
* void archContextSwitch (ATOM_TCB *old_tcb_ptr, ATOM_TCB *new_tcb_ptr)
|
||||
*/
|
||||
.global archContextSwitch
|
||||
archContextSwitch:
|
||||
|
||||
/**
|
||||
* Parameter locations:
|
||||
* old_tcb_ptr = R25-R24
|
||||
* new_tcb_ptr = R23-R22
|
||||
*/
|
||||
|
||||
/**
|
||||
* If this is a cooperative context switch (a thread has called us
|
||||
* to schedule itself out), gcc will have saved any of the
|
||||
* registers R18-R27 and R30-R31 which it does not want us to clobber.
|
||||
* Any registers of that set which it did not need to save are safe
|
||||
* not to be saved by us anyway. Hence for cooperative context
|
||||
* switches we only need to save those registers which gcc expects
|
||||
* us _not_ to modify, that is R2-R17 and R28-R29.
|
||||
*
|
||||
* If we were called from an interrupt routine (because a thread
|
||||
* is being preemptively scheduled out), the situation is exactly
|
||||
* the same. Any ISR which calls out to a subroutine will have
|
||||
* similarly saved those registers which it needs us not to
|
||||
* clobber. In the case of an interrupt, that is every single
|
||||
* register of the set R18-R27 and R30-R31. (gcc cannot establish
|
||||
* which of those registers actually need to be saved because
|
||||
* the information is not available to an ISR). Again, we only
|
||||
* need to save the registers R2-R17 and R28-29, because these
|
||||
* are expected to be unclobbered by a subroutine.
|
||||
*
|
||||
* Note that in addition to saving R18-R27 and R30-R31, gcc also
|
||||
* saves R0, R1 and SREG when entering ISRs. In the case of a
|
||||
* cooperative context switch, it is not necessary to save these.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save registers R2-R17, R28-R29.
|
||||
*/
|
||||
push r2
|
||||
push r3
|
||||
push r4
|
||||
push r5
|
||||
push r6
|
||||
push r7
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
push r16
|
||||
push r17
|
||||
push r28
|
||||
push r29
|
||||
|
||||
/**
|
||||
* Save the final stack pointer to the TCB. The parameter pointing to
|
||||
* the old TCB is still untouched in R25-R24. We have saved R16/R17
|
||||
* and R28/R29 so we can use them for our own purposes now. We must be
|
||||
* careful not to use R23-R22, however, as these still contain the
|
||||
* other parameter, old_tcb_ptr.
|
||||
*/
|
||||
in r16,_SFR_IO_ADDR(SPL) /* Get the current SP into general regs */
|
||||
in r17,_SFR_IO_ADDR(SPH) /* R16/R17 which are now free to use. */
|
||||
|
||||
mov r28,r24 /* Move old_tcb_ptr param into the Y-regs so we */
|
||||
mov r29,r25 /* can access the TCB via a pointer. */
|
||||
|
||||
st Y,r16 /* Store SPH/SPL to new_tcb_ptr->tcb_save_ptr which */
|
||||
std Y+1,r17 /* is conveniently the first member of the TCB. */
|
||||
|
||||
|
||||
/**
|
||||
* At this point, all of the current thread's context has been saved
|
||||
* so we no longer care about keeping the contents of any registers.
|
||||
*
|
||||
* The stack frame if this is a cooperative switch looks as follows:
|
||||
*
|
||||
* <Any of R18-R27 and R30-R31 that the calling function saves>
|
||||
* <Return address to calling function>
|
||||
* <R2>
|
||||
* <R3>
|
||||
* ||
|
||||
* <R16>
|
||||
* <R17>
|
||||
* <R28>
|
||||
* <R29>
|
||||
*
|
||||
* The stack frame if this was a preemptive switch looks as follows:
|
||||
*
|
||||
* <R1> // saved by ISR
|
||||
* <R0> //
|
||||
* <SREG> //
|
||||
* <R18> //
|
||||
* <R19> //
|
||||
* || //
|
||||
* <R26> //
|
||||
* <R27> //
|
||||
* <R30> //
|
||||
* <R31> //
|
||||
* <Return address to ISR>
|
||||
* <Any stacking and return addresses between ISR and this call>
|
||||
* <R2>
|
||||
* <R3>
|
||||
* ||
|
||||
* <R16>
|
||||
* <R17>
|
||||
* <R28>
|
||||
* <R29>
|
||||
*
|
||||
*
|
||||
* In addition, the thread's stack pointer (after context-save) is
|
||||
* stored in the thread's TCB.
|
||||
*/
|
||||
|
||||
/**
|
||||
* We are now ready to restore the new thread's context. We switch
|
||||
* our stack pointer to the new thread's stack pointer, and pop
|
||||
* all of its context off the stack. When we have finished popping
|
||||
* all registers (R2-R17 and R28-R29), we are ready to return.
|
||||
*
|
||||
* Note that any further registers that needed to be saved for the
|
||||
* thread will be restored on exiting this function. If the new
|
||||
* thread previously scheduled itself out cooperatively, the
|
||||
* original calling function will restore any registers it chose
|
||||
* to save. If the new thread was preempted, we will return to the
|
||||
* ISR which will restore all other system registers, before
|
||||
* returning to the interrupted thread.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the new thread's stack pointer off the TCB (new_tcb_ptr).
|
||||
* new_tcb_ptr is still stored in the parameter registers, R23-R22.
|
||||
* We are free to use any other registers, however, as we haven't
|
||||
* yet popped any of the new thread's context off its stack.
|
||||
*/
|
||||
mov r28,r22 /* Move new_tcb_ptr into the Y-regs so we */
|
||||
mov r29,r23 /* can access the TCB via a pointer. */
|
||||
|
||||
ld r16,Y /* Load new_tcb_ptr->sp_save_ptr into R16/R17. */
|
||||
ldd r17,Y+1 /* It is conveniently the first member of the TCB. */
|
||||
|
||||
out _SFR_IO_ADDR(SPL),r16 /* Set our stack pointer to the new thread's */
|
||||
out _SFR_IO_ADDR(SPH),r17 /* stack pointer, from its TCB. */
|
||||
|
||||
/**
|
||||
* Restore registers R2-R17, R28-R29.
|
||||
*/
|
||||
pop r29
|
||||
pop r28
|
||||
pop r17
|
||||
pop r16
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop r7
|
||||
pop r6
|
||||
pop r5
|
||||
pop r4
|
||||
pop r3
|
||||
pop r2
|
||||
|
||||
/**
|
||||
* The return address on the stack will now be the new thread's return
|
||||
* address - i.e. although we just entered this function from a
|
||||
* function called by the old thread, now that we have restored the new
|
||||
* thread's context, we actually return from this function to wherever
|
||||
* the new thread was when it was scheduled out. This could be either a
|
||||
* regular C routine if the new thread previously scheduled itself out
|
||||
* cooperatively, or it could be an ISR if this new thread was
|
||||
* previously preempted (on exiting the ISR, execution will return to
|
||||
* wherever the new thread was originally interrupted).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Note that we always just perform a RET here. Although we may
|
||||
* come in from an ISR and leave through a regular C routine for
|
||||
* another thread (and visa versa) this is OK, because we don't
|
||||
* actually need to perform a RETI to tell the processor the
|
||||
* interrupt is finished. The only extra thing that RETI does is to
|
||||
* set the I bit (interrupts enabled). If we enter from a regular
|
||||
* thread context, but leave through an ISR return address and a RETI,
|
||||
* that just returns and handily enables interrupts for us. Similarly
|
||||
* if we enter from an ISR and leave back into some thread context
|
||||
* calls, interrupts will remain disabled through the regular RET
|
||||
* calls, and we will reenable interrupts in the CRITICAL_END() call
|
||||
* when we unlock interrupts.
|
||||
*/
|
||||
|
||||
ret
|
||||
|
||||
|
||||
/**
|
||||
* \b archFirstThreadRestore
|
||||
*
|
||||
* Architecture-specific function to restore and start the first thread.
|
||||
* This is called by atomOSStart() when the OS is starting.
|
||||
*
|
||||
* This function will be largely similar to the latter half of
|
||||
* archContextSwitch(). Its job is to restore the context for the
|
||||
* first thread, and finally enable interrupts.
|
||||
*
|
||||
* It expects to see the context saved in the same way as if the
|
||||
* thread has been previously scheduled out, and had its context
|
||||
* saved. That is, archThreadContextInit() will have been called
|
||||
* first (via atomThreadCreate()) to create a "fake" context save
|
||||
* area, containing the relevant register-save values for a thread
|
||||
* restore.
|
||||
*
|
||||
* Note that you can create more than one thread before starting
|
||||
* the OS - only one thread is restored using this function, so
|
||||
* all other threads are actually restored by archContextSwitch().
|
||||
* This is another reminder that the initial context set up by
|
||||
* archThreadContextInit() must look the same whether restored by
|
||||
* archFirstThreadRestore() or archContextSwitch().
|
||||
*
|
||||
* @param[in] new_tcb_ptr Pointer to the thread being scheduled in
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
* void archFirstThreadRestore (ATOM_TCB *new_tcb_ptr)
|
||||
*/
|
||||
.global archFirstThreadRestore
|
||||
archFirstThreadRestore:
|
||||
|
||||
/**
|
||||
* Parameter locations:
|
||||
* new_tcb_ptr = R25-R24
|
||||
*/
|
||||
|
||||
/**
|
||||
* First thread restores in the AVR port expect to see R2-R17 and
|
||||
* R28-R29 stored as context. The context will look exactly like it
|
||||
* would had a thread cooperatively scheduled itself out. That is,
|
||||
* these registers will be stored on the stack, and above those will
|
||||
* be the return address of the calling function. In this case we
|
||||
* will have set up this "fake" context in archThreadContextInit(),
|
||||
* and above these registers will be the return address of the thread
|
||||
* entry point. A "ret" or "reti" instruction will therefore direct
|
||||
* the processor to the thread entry point, by popping this "return
|
||||
* address" off the stack.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the new thread's stack pointer off the TCB (new_tcb_ptr).
|
||||
* new_tcb_ptr is stored in the parameter registers, R25-R24.
|
||||
* We are free to use any other registers, however, as we haven't
|
||||
* yet popped any of the new thread's context off its stack.
|
||||
*/
|
||||
mov r28,r24 /* Move new_tcb_ptr into the Y-regs so we */
|
||||
mov r29,r25 /* can access the TCB via a pointer. */
|
||||
|
||||
ld r16,Y /* Load new_tcb_ptr->sp_save_ptr into R16/R17. */
|
||||
ldd r17,Y+1 /* It is conveniently the first member of the TCB. */
|
||||
|
||||
out _SFR_IO_ADDR(SPL),r16 /* Set our stack pointer to the new thread's */
|
||||
out _SFR_IO_ADDR(SPH),r17 /* stack pointer, from its TCB. */
|
||||
|
||||
/**
|
||||
* Restore registers R2-R17, R28-R29.
|
||||
*/
|
||||
pop r29
|
||||
pop r28
|
||||
pop r17
|
||||
pop r16
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop r7
|
||||
pop r6
|
||||
pop r5
|
||||
pop r4
|
||||
pop r3
|
||||
pop r2
|
||||
|
||||
/**
|
||||
* The "return address" left on the stack now will be the new
|
||||
* thread's entry point. RETI will take us there as if we had
|
||||
* actually been there before calling this subroutine, whereas
|
||||
* the return address was actually set up by archThreadContextInit().
|
||||
*
|
||||
* As discussed above, this function is responsible for enabling
|
||||
* interrupts once all context has been restored. We can do this
|
||||
* using a single RETI instruction (return and enable interrupts),
|
||||
* but it is also safe at this point to have two separate
|
||||
* instructions:
|
||||
* sei // enable interrupts
|
||||
* ret // return to new thread entry point
|
||||
*/
|
||||
reti
|
||||
|
||||
|
||||
329
ports/avr/atomport.c
Normal file
329
ports/avr/atomport.c
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomport.h"
|
||||
|
||||
|
||||
/** Forward declarations */
|
||||
static void thread_shell (void);
|
||||
|
||||
|
||||
/**
|
||||
* \b thread_shell
|
||||
*
|
||||
* Shell routine which is used to call all thread entry points.
|
||||
*
|
||||
* This routine is called whenever a new thread is starting, and is
|
||||
* responsible for taking the entry point parameter off the TCB
|
||||
* and passing this into the thread entry point, as well as enabling
|
||||
* interrupts. This is an optional function for a port, as it is
|
||||
* also possible to do this with a regular context restore if the
|
||||
* appropriate registers are saved for context switches (parameter
|
||||
* and interrupt enable). Alternatively a flag could be used to
|
||||
* notify archFirstThreadRestore() and archContextSwitch()
|
||||
* that they should this time restore the contents of the parameter
|
||||
* registers and enable interrupts. After restoring a thread first
|
||||
* time the context restore routines need not perform those
|
||||
* operations again. This is discussed in more detail below.
|
||||
*
|
||||
* When starting new threads, ports must pass in the entry point
|
||||
* function parameter, and enable interrupts. This can be handled by
|
||||
* the first context restore for new threads if they are saved as
|
||||
* part of the thread's context. However for the AVR port we have
|
||||
* chosen to save only the minimum registers required for context
|
||||
* switches. This reduces the cycle time required for all context
|
||||
* switches during operation. This means, however, that we don't save
|
||||
* the parameter registers R25/R24 or the SREG. These would otherwise
|
||||
* be used when restoring a thread for the first time to pass the
|
||||
* parameter to the entry point, and enabling interrupts in the SREG.
|
||||
*
|
||||
* A few of the possible ways round this are to:
|
||||
*
|
||||
* a) Save R25/R24 and SREG with the normal context - thus increasing
|
||||
* the processor cycles required on each context switch.
|
||||
* b) Use a thread shell routine which is used to start all threads.
|
||||
* This routine can then pass the parameter and enable interrupts,
|
||||
* without incurring any overhead on all context switches.
|
||||
* c) Store a flag in a new thread's TCB to notify the normal context
|
||||
* switch routine that it must (the first time a thread is restored
|
||||
* only) pass the parameter, and enable interrupts.
|
||||
*
|
||||
* We have chosen to implement (b) in this case, as it does not affect
|
||||
* normal context switch times just for the benefit of the first restore,
|
||||
* and it does not incur extra complication to the thread restore
|
||||
* routines through handling special cases. A thread shell is also handy
|
||||
* for providing port users with a place to do any initialisation that
|
||||
* must be done for each thread (e.g. opening stdio files etc).
|
||||
*
|
||||
* Other ports are free to implement whatever scheme they wish. In
|
||||
* particular if you save all necessary registers on a context switch
|
||||
* then you need not worry about any special requirements for
|
||||
* starting threads for the first time.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void thread_shell (void)
|
||||
{
|
||||
ATOM_TCB *curr_tcb;
|
||||
|
||||
/* Get the TCB of the thread being started */
|
||||
curr_tcb = atomCurrentContext();
|
||||
|
||||
/**
|
||||
* Enable interrupts - these will not be enabled when a thread
|
||||
* is first restored.
|
||||
*/
|
||||
sei();
|
||||
|
||||
/* Call the thread entry point */
|
||||
if (curr_tcb && curr_tcb->entry_point)
|
||||
{
|
||||
curr_tcb->entry_point(curr_tcb->entry_param);
|
||||
}
|
||||
|
||||
/* Not reached - threads should never return from the entry point */
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b archThreadContextInit
|
||||
*
|
||||
* Architecture-specific thread context initialisation routine.
|
||||
*
|
||||
* This function must set up a thread's context ready for restoring
|
||||
* and running the thread via archFirstThreadRestore() or
|
||||
* archContextSwitch().
|
||||
*
|
||||
* The layout required to fill the correct register values is
|
||||
* described in archContextSwitch(). Note that not all registers
|
||||
* are restored by archContextSwitch() and archFirstThreadRestore()
|
||||
* as this port takes advantage of the fact that not all registers
|
||||
* must be stored by gcc-avr C subroutines. This means that we don't
|
||||
* provide start values for those registers, as they are "don't cares".
|
||||
*
|
||||
* Because we don't actually save the parameter registers (R25-R24)
|
||||
* for this particular architecture, we use a separate thread shell.
|
||||
* The thread shell is always used as the thread entry point stored
|
||||
* in the thread context, and it does the actual calling of the
|
||||
* proper thread entry point, passing the thread entry parameter.
|
||||
* This allows us to pass the entry parameter without actually
|
||||
* storing it on the stack (the thread shell routine takes the
|
||||
* entry point and parameter from the thread's TCB). On other ports
|
||||
* you may instead choose to store the entry point and parameter
|
||||
* in the thread context and use no thread shell routine.
|
||||
*
|
||||
* Similarly we use the thread shell in this case to enable interrupts.
|
||||
* When a thread is restored and started for the first time, it must
|
||||
* also enable interrupts. This might be done by setting up the
|
||||
* appropriate value in the SREG register for enabled interrupts, which
|
||||
* would then be restored when the thread is first started. But to
|
||||
* reduce register-saves we do not save SREG on the AVR port, and
|
||||
* instead we use the thread shell routine to enable interrupts the
|
||||
* first time a thread is started.
|
||||
*
|
||||
* @param[in] tcb_ptr Pointer to the TCB of the thread being created
|
||||
* @param[in] stack_top Pointer to the top of the new thread's stack
|
||||
* @param[in] entry_point Pointer to the thread entry point function
|
||||
* @param[in] entry_param Parameter to be passed to the thread entry point
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(uint32_t), uint32_t entry_param)
|
||||
{
|
||||
uint8_t *stack_ptr;
|
||||
|
||||
/** Start at stack top */
|
||||
stack_ptr = (uint8_t *)stack_top;
|
||||
|
||||
/**
|
||||
* After restoring all of the context registers, the thread restore
|
||||
* routines will perform a RET or RETI which expect to find the
|
||||
* address of the calling routine on the stack. In this case (the
|
||||
* first time a thread is run) we "return" to the entry point for
|
||||
* the thread. That is, we store the thread entry point in the
|
||||
* place that RET and RETI will look for the return address: the
|
||||
* stack.
|
||||
*
|
||||
* Note that we are using the thread_shell() routine to start all
|
||||
* threads, so we actually store the address of thread_shell()
|
||||
* here. Other ports may store the real thread entry point here
|
||||
* and call it directly from the thread restore routines.
|
||||
*
|
||||
* Because we are filling the stack from top to bottom, this goes
|
||||
* on the stack first (at the top).
|
||||
*/
|
||||
*stack_ptr-- = (uint8_t)((uint16_t)thread_shell & 0xFF);
|
||||
*stack_ptr-- = (uint8_t)(((uint16_t)thread_shell >> 8) & 0xFF);
|
||||
|
||||
/**
|
||||
* For the AVR port the parameter registers (R25-R24) are not
|
||||
* saved and restored by the context switch routines. This means
|
||||
* that they cannot be used to pass a parameter to the entry
|
||||
* point the first time a thread is restored. Rather than incur
|
||||
* the overhead of saving them (just for the benefit of starting
|
||||
* threads) we can either use a flag here to notify the context
|
||||
* restore routines (archThreadFirstRestore() and
|
||||
* archContextSwitch()) that they should restore R25-R24 this
|
||||
* one time, or we can use a thread shell routine which replaces
|
||||
* that actual thread entry point. In this case we use a thread
|
||||
* shell which is responsible for passing the parameters to the
|
||||
* actual thread entry point, without adding extra processing
|
||||
* to the context switch routines.
|
||||
*
|
||||
* Other ports may wish to store entry_param in the appropriate
|
||||
* parameter registers when creating a thread's context,
|
||||
* particularly if that port saves those registers anyway.
|
||||
*
|
||||
* Similarly, although interrupts must be enabled when starting
|
||||
* new threads, we also defer this to the thread shell because
|
||||
* we don't save the SREG contents for normal context switches.
|
||||
* Other ports may choose to context switch the relevant
|
||||
* interrupt enable register, so that the first context switch
|
||||
* is able to enable interrupts during its normal context
|
||||
* restore.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Store starting register values for R2-R17, R28-R29
|
||||
*/
|
||||
*stack_ptr-- = 0x00; /* R2 */
|
||||
*stack_ptr-- = 0x00; /* R3 */
|
||||
*stack_ptr-- = 0x00; /* R4 */
|
||||
*stack_ptr-- = 0x00; /* R5 */
|
||||
*stack_ptr-- = 0x00; /* R6 */
|
||||
*stack_ptr-- = 0x00; /* R7 */
|
||||
*stack_ptr-- = 0x00; /* R8 */
|
||||
*stack_ptr-- = 0x00; /* R9 */
|
||||
*stack_ptr-- = 0x00; /* R10 */
|
||||
*stack_ptr-- = 0x00; /* R11 */
|
||||
*stack_ptr-- = 0x00; /* R12 */
|
||||
*stack_ptr-- = 0x00; /* R13 */
|
||||
*stack_ptr-- = 0x00; /* R14 */
|
||||
*stack_ptr-- = 0x00; /* R15 */
|
||||
*stack_ptr-- = 0x00; /* R16 */
|
||||
*stack_ptr-- = 0x00; /* R17 */
|
||||
*stack_ptr-- = 0x00; /* R28 */
|
||||
*stack_ptr-- = 0x00; /* R29 */
|
||||
|
||||
/**
|
||||
* All thread context has now been initialised. Save the current
|
||||
* stack pointer to the thread's TCB so it knows where to start
|
||||
* looking when the thread is started.
|
||||
*/
|
||||
tcb_ptr->sp_save_ptr = stack_ptr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b avrInitSystemTickTimer
|
||||
*
|
||||
* Initialise the system tick timer. Uses the AVR's timer1 facility.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void avrInitSystemTickTimer ( void )
|
||||
{
|
||||
/* Set timer 1 compare match value for configured system tick,
|
||||
* with a prescaler of 256. We will get a compare match 1A
|
||||
* interrupt on every system tick, in which we must call the
|
||||
* OS's system tick handler. */
|
||||
OCR1A = (AVR_CPU_HZ / 256 / SYSTEM_TICKS_PER_SEC);
|
||||
|
||||
/* Enable compare match 1A interrupt */
|
||||
TIMSK = _BV(OCIE1A);
|
||||
|
||||
/* Set prescaler 256 */
|
||||
TCCR1B = _BV(CS12) | _BV(WGM12);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* System tick ISR.
|
||||
*
|
||||
* This is responsible for regularly calling the OS system tick handler.
|
||||
* The system tick handler checks if any timer callbacks are necessary,
|
||||
* and runs the scheduler.
|
||||
*
|
||||
* The compiler automatically saves all registers necessary before calling
|
||||
* out to a C routine. This will be (at least) R0, R1, SREG, R18-R27 and
|
||||
* R30/R31.
|
||||
*
|
||||
* The system may decide to schedule in a new thread during the call to
|
||||
* atomTimerTick(), in which case around half of the thread's context will
|
||||
* already have been saved here, ready for when we return here when the
|
||||
* interrupted thread is scheduled back in. The remaining context will be
|
||||
* saved by the context switch routine.
|
||||
*
|
||||
* As with all interrupts, the ISR should call atomIntEnter() and
|
||||
* atomIntExit() on entry and exit. This serves two purposes:
|
||||
*
|
||||
* a) To notify the OS that it is running in interrupt context
|
||||
* b) To defer the scheduler until after the ISR is completed
|
||||
*
|
||||
* We defer all scheduling decisions until after the ISR has completed
|
||||
* in case the interrupt handler makes more than one thread ready.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
ISR (TIMER1_COMPA_vect)
|
||||
{
|
||||
/* Call the interrupt entry routine */
|
||||
atomIntEnter();
|
||||
|
||||
/* Call the OS system tick handler */
|
||||
atomTimerTick();
|
||||
|
||||
/* Call the interrupt exit routine */
|
||||
atomIntExit(TRUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Default (no handler installed) ISR.
|
||||
*
|
||||
* Installs a default handler to be called if any interrupts occur for
|
||||
* which we have not registered an ISR. This is empty and has only been
|
||||
* included to handle user-created code which may enable interrupts. The
|
||||
* core OS does not enable any interrupts other than the system timer
|
||||
* tick interrupt.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
ISR (BADISR_vect)
|
||||
{
|
||||
/* Empty */
|
||||
}
|
||||
39
ports/avr/atomport.h
Normal file
39
ports/avr/atomport.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_PORT_H
|
||||
#define __ATOM_PORT_H
|
||||
|
||||
/* CPU Frequency */
|
||||
#define AVR_CPU_HZ 1000000
|
||||
|
||||
/* Function prototypes */
|
||||
void avrInitSystemTickTimer ( void );
|
||||
|
||||
#endif /* __ATOM_PORT_H */
|
||||
59
ports/avr/atomuser.h
Normal file
59
ports/avr/atomuser.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_USER_H
|
||||
#define __ATOM_USER_H
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
/* Portable uint8_t and friends available from stdint.h on this platform */
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* Required number of system ticks per second (normally 100 for 10ms tick) */
|
||||
#define SYSTEM_TICKS_PER_SEC 100
|
||||
|
||||
|
||||
/**
|
||||
* Architecture-specific types.
|
||||
* Most of these are available from stdint.h on this platform, which is
|
||||
* included above.
|
||||
*/
|
||||
#define POINTER void *
|
||||
|
||||
|
||||
/* Critical region protection */
|
||||
#define CRITICAL_STORE uint8_t sreg
|
||||
#define CRITICAL_START() sreg = SREG; cli();
|
||||
#define CRITICAL_END() SREG = sreg
|
||||
|
||||
|
||||
#endif /* __ATOM_USER_H */
|
||||
6
ports/avr/gdb.txt
Normal file
6
ports/avr/gdb.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
delete
|
||||
file atomapp.elf
|
||||
target remote localhost:1212
|
||||
load
|
||||
break main_thread_func
|
||||
cont
|
||||
238
ports/avr/tests-main.c
Normal file
238
ports/avr/tests-main.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomport.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomtimer.h"
|
||||
|
||||
#include "uart.h"
|
||||
|
||||
|
||||
/* Constants */
|
||||
|
||||
/*
|
||||
* Idle thread stack size
|
||||
*
|
||||
* This needs to be large enough to handle any interrupt handlers
|
||||
* and callbacks called by interrupt handlers (e.g. user-created
|
||||
* timer callbacks) as well as the saving of all context when
|
||||
* switching away from this thread.
|
||||
*
|
||||
* In this case, the idle stack is allocated on the BSS via the
|
||||
* idle_thread_stack[] byte array.
|
||||
*/
|
||||
#define IDLE_STACK_SIZE_BYTES 128
|
||||
|
||||
|
||||
/*
|
||||
* Startup code stack size
|
||||
*
|
||||
* This defines the size of stack allowed for the main() startup
|
||||
* code before the OS is actually started. This needs to be large
|
||||
* enough to manage the atomOSInit(), atomOSStart() and
|
||||
* atomThreadCreate() calls which occur before the OS is started.
|
||||
*
|
||||
* In this case we use the default startup stack location used by
|
||||
* avr-gcc of the top of RAM (defined as RAMEND). After the OS
|
||||
* is started this allocation is no longer required,therefore
|
||||
* you could alternatively use some location which you know that
|
||||
* your application will not use until the OS is started. Note
|
||||
* that you cannot use the idle thread or main thread stack here
|
||||
* because the stack contexts of these threads are initialised
|
||||
* during OS creation.
|
||||
*
|
||||
* Instead of reusing some application area, here we set aside
|
||||
* 64 bytes of RAM for this purpose, because we call out to
|
||||
* several different test applications, and do not know of any
|
||||
* particular application locations which will be free to use.
|
||||
*/
|
||||
#define STARTUP_STACK_SIZE_BYTES 64
|
||||
|
||||
|
||||
/*
|
||||
* Main thread stack size
|
||||
*
|
||||
* Here we utilise the space starting at 64 bytes below the startup
|
||||
* stack for the Main application thread. Note that this is not a
|
||||
* required OS kernel thread - you will replace this with your own
|
||||
* application thread.
|
||||
*
|
||||
* In this case the Main thread is responsible for calling out to the
|
||||
* test routines. Once a test routine has finished, the thread remains
|
||||
* running and loops printing out an error message (if the test failed)
|
||||
* or flashes a LED once per second (if the test passed).
|
||||
*
|
||||
* The Main thread stack generally needs to be larger than the idle
|
||||
* thread stack, as not only does it need to store interrupt handler
|
||||
* stack saves and context switch saves, but the application main thread
|
||||
* will generally be carrying out more nested function calls and require
|
||||
* stack for application code local variables etc.
|
||||
*
|
||||
* With all OS tests implemented to date on the AVR, the Main thread
|
||||
* stack has not exceeded 147 bytes. Care must be taken to ensure that
|
||||
* the data section, BSS section, and 64 byte startup section leave
|
||||
* enough free space for the main thread. You can use the avr-size
|
||||
* command to view the size of the BSS and data sections in your
|
||||
* application ELF files. For example if you require a 196 byte main
|
||||
* thread stack, then the data, BSS and startup stack combined must
|
||||
* not exceed RAMSIZE-196 bytes.
|
||||
*/
|
||||
|
||||
|
||||
/* Local data */
|
||||
|
||||
/* Application threads' TCBs */
|
||||
static ATOM_TCB main_tcb;
|
||||
|
||||
/* Idle thread's stack area */
|
||||
static uint8_t idle_thread_stack[IDLE_STACK_SIZE_BYTES];
|
||||
|
||||
/* STDIO stream */
|
||||
static FILE uart_stdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
|
||||
/* Forward declarations */
|
||||
static void main_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b main
|
||||
*
|
||||
* Program entry point.
|
||||
*
|
||||
* Sets up the AVR hardware resources (system tick timer interrupt) necessary
|
||||
* for the OS to be started. Creates an application thread and starts the OS.
|
||||
*/
|
||||
int main ( void )
|
||||
{
|
||||
int8_t status;
|
||||
|
||||
/**
|
||||
* Note: to protect OS structures and data during initialisation,
|
||||
* interrupts must remain disabled until the first thread
|
||||
* has been restored. They are reenabled at the very end of
|
||||
* the first thread restore, at which point it is safe for a
|
||||
* reschedule to take place.
|
||||
*/
|
||||
|
||||
/* Initialise the OS before creating our threads */
|
||||
status = atomOSInit(&idle_thread_stack[IDLE_STACK_SIZE_BYTES - 1]);
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/* Enable the system tick timer */
|
||||
avrInitSystemTickTimer();
|
||||
|
||||
/* Create an application thread */
|
||||
status = atomThreadCreate(&main_tcb,
|
||||
TEST_THREAD_PRIO, main_thread_func, 0,
|
||||
(POINTER)(RAMEND-STARTUP_STACK_SIZE_BYTES));
|
||||
if (status == ATOM_OK)
|
||||
{
|
||||
/**
|
||||
* First application thread successfully created. It is
|
||||
* now possible to start the OS. Execution will not return
|
||||
* from atomOSStart(), which will restore the context of
|
||||
* our application thread and start executing it.
|
||||
*
|
||||
* Note that interrupts are still disabled at this point.
|
||||
* They will be enabled as we restore and execute our first
|
||||
* thread in archFirstThreadRestore().
|
||||
*/
|
||||
atomOSStart();
|
||||
}
|
||||
}
|
||||
|
||||
while (1)
|
||||
;
|
||||
|
||||
/* There was an error starting the OS if we reach here */
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b main_thread_func
|
||||
*
|
||||
* Entry point for main application thread.
|
||||
*
|
||||
* This is the first thread that will be executed when the OS is started.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void main_thread_func (uint32_t data)
|
||||
{
|
||||
uint32_t test_status;
|
||||
|
||||
/* Enable all LEDs (STK500-specific) */
|
||||
DDRB = 0xFF;
|
||||
PORTB = 0xFF;
|
||||
|
||||
/* Initialise UART (9600bps) */
|
||||
if (uart_init(9600) != 0)
|
||||
{
|
||||
/* Error initialising UART */
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect stdout via the UART. Note that the UART write routine
|
||||
* is protected via a semaphore, so the OS must be started before
|
||||
* use of the UART.
|
||||
*/
|
||||
stdout = &uart_stdout;
|
||||
|
||||
/* Put a message out on the UART */
|
||||
printf_P(PSTR("Go\n"));
|
||||
|
||||
/* Start test. All tests use the same start API. */
|
||||
test_status = test_start();
|
||||
|
||||
/* Test finished, sleep forever */
|
||||
while (1)
|
||||
{
|
||||
/* Log test status */
|
||||
if (test_status == 0)
|
||||
{
|
||||
/* Toggle a LED (STK500-specific) */
|
||||
PORTB ^= (1 << 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf_P (PSTR("Fail%d\n"), atomTimeGet());
|
||||
}
|
||||
|
||||
/* Sleep for one second and log status again */
|
||||
atomTimerDelay(SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
85
ports/avr/uart.c
Normal file
85
ports/avr/uart.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo, UART implementation
|
||||
*
|
||||
* $Id: uart.c,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "atommutex.h"
|
||||
#include "atomport.h"
|
||||
#include "uart.h"
|
||||
|
||||
/*
|
||||
* Semaphore for single-threaded access to UART device
|
||||
*/
|
||||
static ATOM_MUTEX uart_mutex;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the UART to 9600 Bd, tx/rx, 8N1.
|
||||
*/
|
||||
int
|
||||
uart_init(uint32_t baudrate)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Set up the UART device with the selected baudrate */
|
||||
#if AVR_CPU_HZ < 2000000UL && defined(U2X)
|
||||
UCSRA = _BV(U2X); /* improve baud rate error by using 2x clk */
|
||||
UBRRL = (AVR_CPU_HZ / (8UL * baudrate)) - 1;
|
||||
#else
|
||||
UBRRL = (AVR_CPU_HZ / (16UL * baudrate)) - 1;
|
||||
#endif
|
||||
UCSRB = _BV(TXEN) | _BV(RXEN); /* tx/rx enable */
|
||||
|
||||
/* Create a mutex for single-threaded putchar() access */
|
||||
if (atomMutexCreate (&uart_mutex) != ATOM_OK)
|
||||
{
|
||||
status = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = 0;
|
||||
}
|
||||
|
||||
/* Finished */
|
||||
return (status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send character c down the UART Tx, wait until tx holding register
|
||||
* is empty.
|
||||
*/
|
||||
int
|
||||
uart_putchar(char c, FILE *stream)
|
||||
{
|
||||
|
||||
/* Block on private access to the UART */
|
||||
if (atomMutexGet(&uart_mutex, 0) == ATOM_OK)
|
||||
{
|
||||
/* Convert \n to \r\n */
|
||||
if (c == '\n')
|
||||
uart_putchar('\r', stream);
|
||||
|
||||
/* Wait until the UART is ready then send the character out */
|
||||
loop_until_bit_is_set(UCSRA, UDRE);
|
||||
UDR = c;
|
||||
|
||||
/* Return mutex access */
|
||||
atomMutexPut(&uart_mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
ports/avr/uart.h
Normal file
25
ports/avr/uart.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo, UART declarations
|
||||
*
|
||||
* $Id: uart.h,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include "atom.h"
|
||||
|
||||
|
||||
/*
|
||||
* Perform UART startup initialization.
|
||||
*/
|
||||
int uart_init(uint32_t baudrate);
|
||||
|
||||
/*
|
||||
* Send one character to the UART.
|
||||
*/
|
||||
int uart_putchar(char c, FILE *stream);
|
||||
60
tests/atomtests.h
Normal file
60
tests/atomtests.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ATOM_TESTS_H
|
||||
#define __ATOM_TESTS_H
|
||||
|
||||
/* Include Atomthreads kernel API */
|
||||
#include "atom.h"
|
||||
|
||||
/* Prerequisite include for ATOMLOG() macro (via printf) */
|
||||
#include <stdio.h>
|
||||
|
||||
/* Logger macro for viewing test results */
|
||||
#define ATOMLOG printf_P
|
||||
|
||||
/*
|
||||
* String location macro: for platforms which need to place strings in
|
||||
* alternative locations, e.g. on avr-gcc strings can be placed in
|
||||
* program space, saving SRAM. On most platforms this can expand to
|
||||
* empty.
|
||||
*/
|
||||
#define _STR(x) PSTR(x)
|
||||
|
||||
/* Default thread stack size (in bytes) */
|
||||
#define TEST_THREAD_STACK_SIZE 128
|
||||
|
||||
/* Default thread priority */
|
||||
#define TEST_THREAD_PRIO 16
|
||||
|
||||
/* API for starting each test */
|
||||
extern uint32_t test_start (void);
|
||||
|
||||
|
||||
#endif /* __ATOM_TESTS_H */
|
||||
120
tests/kern1.c
Normal file
120
tests/kern1.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start kernel test.
|
||||
*
|
||||
* This tests the handling of bad parameters within the public kernel APIs.
|
||||
*
|
||||
* Other than during initialisation, the only API that takes parameters
|
||||
* which require checking (and that application code might call) is the
|
||||
* thread create API.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* atomThreadCreate: Pass a bad TCB pointer */
|
||||
if (atomThreadCreate (NULL, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
&test_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Bad TCB check\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* atomThreadCreate: Pass a bad entry point */
|
||||
if (atomThreadCreate (&tcb1, TEST_THREAD_PRIO, NULL, 0,
|
||||
&test_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Bad entry check\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* atomThreadCreate: Pass a bad stack pointer */
|
||||
if (atomThreadCreate (&tcb1, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Bad stack ptr check\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
198
tests/kern2.c
Normal file
198
tests/kern2.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start kernel test.
|
||||
*
|
||||
* This is a basic test of the thread context-switch functionality. It
|
||||
* creates twenty local (byte) variables and schedules the thread out
|
||||
* for one second. If context-switch save/restore is not implemented
|
||||
* correctly, you might expect one or more of these local variables to
|
||||
* be corrupted by the time the thread is scheduled back in.
|
||||
*
|
||||
* Note that this is a fairly unsophisticated test, and a lot depends on
|
||||
* how the compiler deals with the variables, as well as what code is
|
||||
* executed while the thread is scheduled out. It should flag up any
|
||||
* major problems with the context-switch, however.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint8_t one = 1;
|
||||
uint8_t two = 2;
|
||||
uint8_t three = 3;
|
||||
uint8_t four = 4;
|
||||
uint8_t five = 5;
|
||||
uint8_t six = 6;
|
||||
uint8_t seven = 7;
|
||||
uint8_t eight = 8;
|
||||
uint8_t nine = 9;
|
||||
uint8_t ten = 10;
|
||||
uint8_t eleven = 11;
|
||||
uint8_t twelve = 12;
|
||||
uint8_t thirteen = 13;
|
||||
uint8_t fourteen = 14;
|
||||
uint8_t fifteen = 15;
|
||||
uint8_t sixteen = 16;
|
||||
uint8_t seventeen = 17;
|
||||
uint8_t eighteen = 18;
|
||||
uint8_t nineteen = 19;
|
||||
uint8_t twenty = 20;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Sleep for one second */
|
||||
atomTimerDelay(SYSTEM_TICKS_PER_SEC);
|
||||
|
||||
/* Check all variables contain expected values */
|
||||
if (one != 1)
|
||||
{
|
||||
ATOMLOG (_STR("1(%d)\n"), one);
|
||||
failures++;
|
||||
}
|
||||
if (two != 2)
|
||||
{
|
||||
ATOMLOG (_STR("2(%d)\n"), two);
|
||||
failures++;
|
||||
}
|
||||
if (three != 3)
|
||||
{
|
||||
ATOMLOG (_STR("3(%d)\n"), three);
|
||||
failures++;
|
||||
}
|
||||
if (four != 4)
|
||||
{
|
||||
ATOMLOG (_STR("4(%d)\n"), four);
|
||||
failures++;
|
||||
}
|
||||
if (five != 5)
|
||||
{
|
||||
ATOMLOG (_STR("5(%d)\n"), five);
|
||||
failures++;
|
||||
}
|
||||
if (six != 6)
|
||||
{
|
||||
ATOMLOG (_STR("6(%d)\n"), six);
|
||||
failures++;
|
||||
}
|
||||
if (seven != 7)
|
||||
{
|
||||
ATOMLOG (_STR("7(%d)\n"), seven);
|
||||
failures++;
|
||||
}
|
||||
if (eight != 8)
|
||||
{
|
||||
ATOMLOG (_STR("8(%d)\n"), eight);
|
||||
failures++;
|
||||
}
|
||||
if (nine != 9)
|
||||
{
|
||||
ATOMLOG (_STR("9(%d)\n"), nine);
|
||||
failures++;
|
||||
}
|
||||
if (ten != 10)
|
||||
{
|
||||
ATOMLOG (_STR("10(%d)\n"), ten);
|
||||
failures++;
|
||||
}
|
||||
if (eleven != 11)
|
||||
{
|
||||
ATOMLOG (_STR("11(%d)\n"), eleven);
|
||||
failures++;
|
||||
}
|
||||
if (twelve != 12)
|
||||
{
|
||||
ATOMLOG (_STR("12(%d)\n"), twelve);
|
||||
failures++;
|
||||
}
|
||||
if (thirteen != 13)
|
||||
{
|
||||
ATOMLOG (_STR("13(%d)\n"), thirteen);
|
||||
failures++;
|
||||
}
|
||||
if (fourteen != 14)
|
||||
{
|
||||
ATOMLOG (_STR("14(%d)\n"), fourteen);
|
||||
failures++;
|
||||
}
|
||||
if (fifteen != 15)
|
||||
{
|
||||
ATOMLOG (_STR("15(%d)\n"), fifteen);
|
||||
failures++;
|
||||
}
|
||||
if (sixteen != 16)
|
||||
{
|
||||
ATOMLOG (_STR("16(%d)\n"), sixteen);
|
||||
failures++;
|
||||
}
|
||||
if (seventeen != 17)
|
||||
{
|
||||
ATOMLOG (_STR("17(%d)\n"), seventeen);
|
||||
failures++;
|
||||
}
|
||||
if (eighteen != 18)
|
||||
{
|
||||
ATOMLOG (_STR("18(%d)\n"), eighteen);
|
||||
failures++;
|
||||
}
|
||||
if (nineteen != 19)
|
||||
{
|
||||
ATOMLOG (_STR("19(%d)\n"), nineteen);
|
||||
failures++;
|
||||
}
|
||||
if (twenty != 20)
|
||||
{
|
||||
ATOMLOG (_STR("20(%d)\n"), twenty);
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
213
tests/kern3.c
Normal file
213
tests/kern3.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TCB tcb1, tcb2;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test global data (one per thread) */
|
||||
static volatile int running_flag[2];
|
||||
static volatile int sleep_request[2];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start kernel test.
|
||||
*
|
||||
* This tests the scheduling of threads at different priorities, and
|
||||
* preemption of lower priority threads by higher priority threads.
|
||||
*
|
||||
* Much of this functionality is already tested implicitly by the
|
||||
* semaphore, mutex tests etc but we repeat it here within the kernel
|
||||
* tests for completeness.
|
||||
*
|
||||
* Two threads are created at different priorities, with each thread
|
||||
* setting a running flag whenever it runs. We check that when the
|
||||
* higher priority thread is ready to run, only the higher priority
|
||||
* thread's running flag is set (even though the lower priority
|
||||
* thread should also be setting it at this time). This checks that
|
||||
* the scheduler is correctly prioritising thread execution.
|
||||
*
|
||||
* The test also exercises preemption, by disabling setting of the
|
||||
* running flag in the higher priority thread for a period. During
|
||||
* this time the higher priority thread repeatedly sleeps for one
|
||||
* system tick then wakes up to check the sleep-request flag again.
|
||||
* Every time the higher priority thread wakes up, it has preempted
|
||||
* the lower priority thread (which is always running). By ensuring
|
||||
* that the higher priority thread is able to start running again
|
||||
* after one of these periods (through checking the running flag)
|
||||
* we prove that the preemption has worked.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Initialise global data */
|
||||
running_flag[0] = running_flag[1] = FALSE;
|
||||
sleep_request[0] = sleep_request[1] = FALSE;
|
||||
|
||||
/* Create low priority thread */
|
||||
if (atomThreadCreate (&tcb1, 253, test_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Bad thread create\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create high priority thread */
|
||||
else if (atomThreadCreate (&tcb2, 252, test_thread_func, 1,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Bad thread create\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Repeat test a few times */
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
/* Make the higher priority thread sleep */
|
||||
sleep_request[1] = TRUE;
|
||||
|
||||
/* Sleep a little to make sure the thread sees the sleep request */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Reset the running flag for both threads */
|
||||
running_flag[0] = running_flag[1] = FALSE;
|
||||
|
||||
/* Sleep a little to give any running threads time to set their flag */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check only the low priority thread has run since we reset the flags */
|
||||
if ((running_flag[0] != TRUE) || (running_flag[1] != FALSE))
|
||||
{
|
||||
ATOMLOG (_STR("Lo%d %d/%d\n"), i, running_flag[0], running_flag[1]);
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We have confirmed that only the ready thread has been running.
|
||||
* Now check that if we wake up the high priority thread, the
|
||||
* low priority one stops running and only the high priority one
|
||||
* does.
|
||||
*/
|
||||
|
||||
/* Tell the higher priority thread to stop sleeping */
|
||||
sleep_request[1] = FALSE;
|
||||
|
||||
/* Reset the running flag for both threads */
|
||||
running_flag[0] = running_flag[1] = FALSE;
|
||||
|
||||
/* Sleep a little to give any running threads time to set their flag */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check only the high priority thread has run since we reset the flags */
|
||||
if ((running_flag[1] != TRUE) || (running_flag[0] != FALSE))
|
||||
{
|
||||
ATOMLOG (_STR("Hi%d/%d\n"), running_flag[0], running_flag[1]);
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We have confirmed that the high priority thread has preempted the
|
||||
* low priority thread, and remain running while never scheduling
|
||||
* the lower one back in.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Thread ID (0 = low prio, 1 = high prio)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
int thread_id;
|
||||
|
||||
/* Pull out thread ID */
|
||||
thread_id = (int)data;
|
||||
|
||||
/* Run forever */
|
||||
while (1)
|
||||
{
|
||||
/* If this thread is requested to sleep, sleep until told to stop */
|
||||
if (sleep_request[thread_id])
|
||||
{
|
||||
atomTimerDelay (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise set running flag for this thread */
|
||||
running_flag[thread_id] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
241
tests/kern4.c
Normal file
241
tests/kern4.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test global data (one per thread) */
|
||||
static uint32_t volatile last_time;
|
||||
static int volatile last_thread_id;
|
||||
static volatile int switch_cnt;
|
||||
static uint32_t volatile failure_cnt[4];
|
||||
static int volatile test_started;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start kernel test.
|
||||
*
|
||||
* This tests the round-robin timeslicing of same priority threads.
|
||||
*
|
||||
* Four threads are created (over and above the main test thread).
|
||||
* The main test thread sleeps for the duration of the entire test.
|
||||
* While the test is ongoing, all four threads are running
|
||||
* continuously at the same priority. They each check that whenever
|
||||
* the system tick (atomTimeGet()) changes, it has moved on 1 tick
|
||||
* since the last time the tick was checked, and that the previous
|
||||
* thread is the one created before itself. In the case of the first
|
||||
* thread created, the previous thread should be the last thread
|
||||
* created. This proves that on every tick the four threads get a
|
||||
* schedule timeslice equally, and in the same order throughout the
|
||||
* duration of the test.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Initialise global data */
|
||||
last_time = 0;
|
||||
last_thread_id = -1;
|
||||
failure_cnt[0] = failure_cnt[1] = failure_cnt[2] = failure_cnt[3] = 0;
|
||||
|
||||
/* Set test as not started until all threads are ready to go */
|
||||
test_started = FALSE;
|
||||
|
||||
/*
|
||||
* Create all four threads at the same priority as each other.
|
||||
* They are given a lower priority than this thread, however,
|
||||
* to ensure that once this thread wakes up to stop the test it
|
||||
* can do so without confusing the scheduling tests by having
|
||||
* a spell in which this thread was run.
|
||||
*/
|
||||
if (atomThreadCreate (&tcb1, TEST_THREAD_PRIO + 1, test_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Bad thread create\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomThreadCreate (&tcb2, TEST_THREAD_PRIO + 1, test_thread_func, 1,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Bad thread create\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomThreadCreate (&tcb3, TEST_THREAD_PRIO + 1, test_thread_func, 2,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Bad thread create\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomThreadCreate (&tcb4, TEST_THREAD_PRIO + 1, test_thread_func, 3,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Bad thread create\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Start the test */
|
||||
test_started = TRUE;
|
||||
|
||||
/* Sleep for 5 seconds during test */
|
||||
atomTimerDelay (5 * SYSTEM_TICKS_PER_SEC);
|
||||
|
||||
/* Stop the test */
|
||||
test_started = FALSE;
|
||||
|
||||
/* Sleep for tests to complete */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Count any failures from test threads */
|
||||
failures += failure_cnt[0] + failure_cnt[1] + failure_cnt[2] + failure_cnt[3];
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Thread ID (0 to 3)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
int thread_id, expected_thread;
|
||||
int time_error, thread_error;
|
||||
uint32_t new_time;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Pull out thread ID */
|
||||
thread_id = (int)data;
|
||||
|
||||
/* Run forever */
|
||||
while (1)
|
||||
{
|
||||
/* Check if test is currently in operation */
|
||||
if (test_started)
|
||||
{
|
||||
/*
|
||||
* If the system time has ticked over, check that the currently
|
||||
* running thread is not the one that was running last tick.
|
||||
*/
|
||||
|
||||
/* Default to no error this time */
|
||||
time_error = thread_error = FALSE;
|
||||
|
||||
/* Do the whole operation with interrupts locked out */
|
||||
CRITICAL_START();
|
||||
|
||||
/* Check if time has ticked over */
|
||||
new_time = atomTimeGet();
|
||||
|
||||
/* Only perform the check if this is not the first thread to run */
|
||||
if ((last_time != 0) && (last_thread_id != -1))
|
||||
{
|
||||
/* Check if the time has ticked over */
|
||||
if (new_time != last_time)
|
||||
{
|
||||
/* Check time only ticked over by 1 */
|
||||
if ((new_time - last_time) != 1)
|
||||
{
|
||||
time_error = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are expecting the previous thread to be our thread_id
|
||||
* minus one.
|
||||
*/
|
||||
expected_thread = thread_id - 1;
|
||||
if (expected_thread == -1)
|
||||
{
|
||||
expected_thread = 3;
|
||||
}
|
||||
|
||||
/* Check that the last thread was the expected one */
|
||||
if (last_thread_id != expected_thread)
|
||||
{
|
||||
thread_error = TRUE;
|
||||
}
|
||||
|
||||
/* Increment the switch count */
|
||||
switch_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store the currently-running thread as the last-running */
|
||||
last_thread_id = thread_id;
|
||||
last_time = new_time;
|
||||
|
||||
/* Finished with the interrupt lockout */
|
||||
CRITICAL_END();
|
||||
|
||||
/* If we got an error above, increment the total failure count */
|
||||
if (test_started && (thread_error || time_error))
|
||||
{
|
||||
failure_cnt[thread_id]++;
|
||||
ATOMLOG(_STR("T%d\n"), thread_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
331
tests/mutex1.c
Normal file
331
tests/mutex1.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atommutex.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1, tcb2;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test result tracking */
|
||||
static volatile int g_result;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test1_thread_func (uint32_t data);
|
||||
static void test2_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This test exercises the mutex creation and deletion APIs, including
|
||||
* waking threads blocking on a mutex if the mutex is deleted.
|
||||
* Deletion wakeups are tested twice: once for a thread which is blocking
|
||||
* with a timeout and once for a thread which is blocking with no timeout.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test creation and deletion of mutexes: good values */
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
if (atomMutexCreate (&mutex1) == ATOM_OK)
|
||||
{
|
||||
if (atomMutexDelete (&mutex1) == ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error deleting mutex\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test creation and deletion of mutexes: creation checks */
|
||||
if (atomMutexCreate (NULL) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad mutex creation checks\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test creation and deletion of mutexes: deletion checks */
|
||||
if (atomMutexDelete (NULL) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad mutex deletion checks\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test wakeup of threads on mutex deletion (thread blocking with no timeout) */
|
||||
g_result = 0;
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Take the mutex so that the test thread will block */
|
||||
else if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test1_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created a mutex and taken ownership ourselves. We
|
||||
* want to see that the other thread is woken up if its mutex
|
||||
* is deleted. This is indicated through g_result being set.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on mutex1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on mutex1 now, delete mutex1 */
|
||||
if (atomMutexDelete(&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed mutex1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Mutex1 deleted. The thread should now wake up and set g_result. */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test wakeup of threads on semaphore deletion (thread blocking with timeout) */
|
||||
g_result = 0;
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Take the mutex so that the test thread will block */
|
||||
else if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test2_thread_func, 0,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created a mutex and taken ownership ourselves. We
|
||||
* want to see that the other thread is woken up if its mutex
|
||||
* is deleted. This is indicated through g_result being set.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on mutex1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on mutex1 now, delete mutex1 */
|
||||
if (atomMutexDelete(&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed mutex1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Mutex1 deleted. The thread should now wake up and set g_result. */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test1_thread_func
|
||||
*
|
||||
* Entry point for test thread 1.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test1_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/*
|
||||
* Wait on mutex1 with no timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomMutexGet(&mutex1, 0);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test2_thread_func
|
||||
*
|
||||
* Entry point for test thread 2.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test2_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/*
|
||||
* Wait on mutex1 with timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomMutexGet(&mutex1, (5 * SYSTEM_TICKS_PER_SEC));
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test2 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
299
tests/mutex2.c
Normal file
299
tests/mutex2.c
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atommutex.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1, mutex2;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test result tracking */
|
||||
static volatile int g_result, g_owned;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This test exercises the atomMutexGet() and atomMutexPut() APIs including
|
||||
* forcing the various error indications which can be returned from the
|
||||
* APIs to ensure that handling for these corner cases have been correctly
|
||||
* implemented.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint8_t status;
|
||||
ATOM_TIMER timer_cb;
|
||||
int count;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test parameter checks */
|
||||
if (atomMutexGet (NULL, 0) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Get param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomMutexPut (NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Put param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test atomMutexGet() can not be called from interrupt context */
|
||||
g_result = 0;
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test mutex1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = testCallback;
|
||||
timer_cb.cb_data = NULL;
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
|
||||
/* Request the timer callback to run in one second */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error registering timer\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait two seconds for g_result to be set indicating success */
|
||||
else
|
||||
{
|
||||
atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Context check failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete the test mutex */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Mutex1 delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create mutex1 which will be owned by us */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test mutex 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create mutex2 which will be owned by another thread */
|
||||
else if (atomMutexCreate (&mutex2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test mutex 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create a test thread, the sole purpose of which is to own mutex2 */
|
||||
g_owned = 0;
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Sleep until the test thread owns mutex2 */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_owned == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Thread own fail\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test wait on mutex with timeout - should timeout while owned by another thread */
|
||||
if ((status = atomMutexGet (&mutex2, SYSTEM_TICKS_PER_SEC)) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Get %d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
|
||||
/* Test wait on mutex with no blocking - should return that owned by another thread */
|
||||
if ((status = atomMutexGet (&mutex2, -1)) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
ATOMLOG (_STR("Wouldblock err %d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test wait on mutex with no blocking when mutex is available */
|
||||
if (atomMutexGet (&mutex1, -1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Relinquish ownership of mutex1 */
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error posting mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test for lock count overflows with too many gets */
|
||||
count = 255;
|
||||
while (count--)
|
||||
{
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error getting mutex1\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The lock count should overflow this time */
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_ERR_OVF)
|
||||
{
|
||||
ATOMLOG (_STR("Error tracking overflow\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
|
||||
/* Delete the test mutexes */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error deleting mutex1\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomMutexDelete (&mutex2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error deleting mutex2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Attempt an atomMutexGet() on mutex1 from interrupt context.
|
||||
* Should receive an ATOM_ERR_CONTEXT error. Sets g_result if passes.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
/* Check the return value from atomMutexGet() */
|
||||
if (atomMutexGet(&mutex1, 0) == ATOM_ERR_CONTEXT)
|
||||
{
|
||||
/* Received the error we expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Did not get expected error, don't set g_result signifying fail */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/*
|
||||
* Take mutex2 so that main thread can test mutex APIs on a mutex
|
||||
* which it does not own.
|
||||
*/
|
||||
status = atomMutexGet(&mutex2, 0);
|
||||
if (status != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Mutex get (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We took ownership of mutex2, set g_owned to notify success */
|
||||
g_owned = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
263
tests/mutex3.c
Normal file
263
tests/mutex3.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atommutex.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
/* Data updated by threads */
|
||||
static volatile uint8_t wake_cnt;
|
||||
static volatile uint8_t wake_order[4];
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* With multiple threads blocking on a single mutex, this test confirms that
|
||||
* they are woken in order when the mutex is released. The correct order for
|
||||
* waking is that the higher priority threads are woken first, followed by the
|
||||
* lower priority threads. Where multiple threads of the same priority are
|
||||
* waiting, the threads are woken in FIFO order (the order in which they started
|
||||
* waiting on the mutex).
|
||||
*
|
||||
* To test this we create four threads which all wait on a single mutex.
|
||||
* One pair of threads are running at high priority, with the other pair at a
|
||||
* lower priority:
|
||||
*
|
||||
* Thread 1: low prio thread A
|
||||
* Thread 2: low prio thread B
|
||||
* Thread 3: high prio thread A
|
||||
* Thread 4: high prio thread B
|
||||
*
|
||||
* The threads are forced to start blocking on the same mutex in the
|
||||
* above order.
|
||||
*
|
||||
* We expect to see them woken up in the following order:
|
||||
* 3, 4, 1, 2
|
||||
*
|
||||
* This proves the multiple blocking thread ordering in terms of both
|
||||
* the priority-queueing and same-priority-FIFO-queueing.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create mutex */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test mutex 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Take ownership of the mutex so all threads will block to begin with */
|
||||
else if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Get error\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Start the threads */
|
||||
else
|
||||
{
|
||||
/* Create Thread 1 (lower priority thread A) */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO+1, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the mutex */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 2 (lower priority thread B) */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO+1, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the mutex */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 3 (higher priority thread A) */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the mutex */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 4 (higher priority thread B) */
|
||||
if (atomThreadCreate(&tcb4, TEST_THREAD_PRIO, test_thread_func, 4,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the mutex */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* All four threads will now be blocking on mutex1 */
|
||||
|
||||
/*
|
||||
* Initialise wake count, used by threads to determine
|
||||
* what order they were woken in.
|
||||
*/
|
||||
wake_cnt = 0;
|
||||
|
||||
/*
|
||||
* Release the mutex. This will wake up one of the threads blocking
|
||||
* on it. That thread will take ownership of the mutex, and note the
|
||||
* order at which it was woken, before releasing the mutex. This in
|
||||
* turn will wake up the next thread blocking on the mutex until all
|
||||
* four test threads have taken and released the mutex, noting their
|
||||
* wake order.
|
||||
*/
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Post fail\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Sleep to give all four threads time to complete */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC / 4);
|
||||
|
||||
/* All four threads now woken up, check they woke in correct order */
|
||||
if ((wake_order[0] != 3) && (wake_order[1] != 4)
|
||||
&& (wake_order[2] != 1) && (wake_order[3] != 2))
|
||||
{
|
||||
ATOMLOG (_STR("Bad order %d,%d,%d,%d\n"),
|
||||
wake_order[0], wake_order[1], wake_order[2], wake_order[3]);
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete mutex, test finished */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* four test threads, with the thread number/ID (1-4) passed as the entry
|
||||
* point parameter.
|
||||
*
|
||||
* @param[in] data Thread number (1,2,3,4)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t thread_id;
|
||||
|
||||
/* Thread ID is passed through the function parameter */
|
||||
thread_id = (uint8_t)data;
|
||||
|
||||
/*
|
||||
* Wait for mutex1 to be posted. At creation of all test threads the mutex
|
||||
* is owned by the parent thread, so all four threads will block here.
|
||||
*/
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Get fail\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Store our thread ID in the array using the current
|
||||
* wake_cnt order. The threads are holding ownership
|
||||
* of a mutex here, which provides protection for this
|
||||
* global data.
|
||||
*/
|
||||
wake_order[wake_cnt++] = thread_id;
|
||||
|
||||
/* Release the mutex so that the next thread wakes up */
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Put fail\n"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
266
tests/mutex4.c
Normal file
266
tests/mutex4.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atommutex.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Number of test loops for stress-test */
|
||||
#define NUM_TEST_LOOPS 10000
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_SEM sem1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/*
|
||||
* Global failure count (can be updated by test threads but is
|
||||
* protected by an interrupt lockout).
|
||||
*/
|
||||
static volatile int g_failures;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* Stress-tests mutex Get and Put operations. Four threads are created which are
|
||||
* continually Getting and Putting the same mutex, with no time delays between
|
||||
* each Get/Put.
|
||||
*
|
||||
* @retval Number of g_failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
int finish_cnt;
|
||||
|
||||
/* Default to zero g_failures */
|
||||
g_failures = 0;
|
||||
|
||||
/* Create mutex to stress */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
g_failures++;
|
||||
}
|
||||
/* Create sem to receive thread-finished notification */
|
||||
else if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating sem\n"));
|
||||
g_failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Take ownership of the mutex to ensure all threads wait for now */
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
g_failures++;
|
||||
}
|
||||
|
||||
/* Create Thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 2 */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 3 */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 4 */
|
||||
if (atomThreadCreate(&tcb4, TEST_THREAD_PRIO, test_thread_func, 4,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Release ownership of the mutex to kick the threads off */
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error putting mutex\n"));
|
||||
g_failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* All four threads will now be performing Gets/Puts on mutex1.
|
||||
* When they have finished they will post sem1, so we wait
|
||||
* until sem1 is posted four times.
|
||||
*/
|
||||
finish_cnt = 0;
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
* Attempt to Get sem1. When we have managed to get
|
||||
* the semaphore four times, it must have been posted
|
||||
* by all four threads.
|
||||
*/
|
||||
if (atomSemGet (&sem1, 0) == ATOM_OK)
|
||||
{
|
||||
/* Increment our count of finished threads */
|
||||
finish_cnt++;
|
||||
|
||||
/* Check if all four threads have now posted sem1 */
|
||||
if (finish_cnt == 4)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete OS objects, test finished */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (g_failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), g_failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return g_failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* four test threads.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint32_t loop_cnt;
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Run a Get/Put pair many times */
|
||||
loop_cnt = NUM_TEST_LOOPS;
|
||||
while (loop_cnt--)
|
||||
{
|
||||
if ((status = atomMutexGet (&mutex1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting mutex, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
else if ((status = atomMutexPut (&mutex1)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting mutex, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post sem1 to notify the main thread we're finished */
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem1 putfail\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
271
tests/mutex5.c
Normal file
271
tests/mutex5.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atommutex.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Global shared data protected by mutex */
|
||||
static volatile int shared_data;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This tests basic usage of a mutex. Whichever thread holds the
|
||||
* mutex can modify the global variable "shared_data".
|
||||
*
|
||||
* The main thread first takes the mutex, then creates a second
|
||||
* thread. The second thread should block on the mutex until the
|
||||
* main thread releases it. The test checks that the global
|
||||
* "shared_data" is not modified by the second thread until the
|
||||
* main thread releases the mutex.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create mutex */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Initialise the shared_data to zero */
|
||||
shared_data = 0;
|
||||
|
||||
/* Take the mutex to ensure only this thread can modify shared_data */
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create second thread */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and should block on
|
||||
* the mutex until we release it. We wait a while and check that
|
||||
* shared_data has not been modified.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* Sleep for a while to give the second thread a chance to
|
||||
* modify shared_data, thought it shouldn't until we
|
||||
* release the mutex.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check shared data. The second thread always sets it to one. */
|
||||
if (shared_data != 0)
|
||||
{
|
||||
ATOMLOG (_STR("Shared data modified\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check successful so far */
|
||||
if (failures == 0)
|
||||
{
|
||||
/*
|
||||
* Release the mutex, which will allow the second thread to
|
||||
* wake and start modifying shared_data.
|
||||
*/
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed release\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait a little while then check that shared_data has
|
||||
* been modified.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (shared_data != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Expected modify\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release and take the mutex again a few times to ensure
|
||||
* that the mutex continues to protect shared_data.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* Take the mutex again, to prevent second thread accessing
|
||||
* shared_data.
|
||||
*/
|
||||
if (atomMutexGet (&mutex1, SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Retake %d\n"), i);
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Set shared_data to 0 and wait to ensure that the
|
||||
* second thread doesn't modify it while we have the
|
||||
* mutex again.
|
||||
*/
|
||||
shared_data = 0;
|
||||
|
||||
/* Wait a while to give second thread potential to run */
|
||||
atomTimerDelay(SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/*
|
||||
* Check that shared_data has not been modified while we
|
||||
* own the mutex.
|
||||
*/
|
||||
if (shared_data != 0)
|
||||
{
|
||||
/* Thread is still modifying the data */
|
||||
ATOMLOG (_STR("Still modifying\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the mutex, which will allow the second thread to
|
||||
* wake and start modifying shared_data again.
|
||||
*/
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed release\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete mutex, test finished */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Repeatedly attempt to get the mutex and set shared_data to 1 */
|
||||
while (1)
|
||||
{
|
||||
/* Block on the mutex */
|
||||
if ((status = atomMutexGet (&mutex1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting mutex, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Got the mutex */
|
||||
else
|
||||
{
|
||||
/* Set shared_data to signify that we think we have the mutex */
|
||||
shared_data = 1;
|
||||
|
||||
/* Release the mutex allowing the main thread to take it again */
|
||||
if ((status = atomMutexPut (&mutex1)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting mutex, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Loop forever - we only reach here on error */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
262
tests/mutex6.c
Normal file
262
tests/mutex6.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atommutex.h"
|
||||
|
||||
|
||||
/* Number of times to lock the mutex during test */
|
||||
#define TEST_LOCK_CNT 250
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Global shared data protected by mutex */
|
||||
static volatile int shared_data;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This tests the lock count of a mutex. The mutex object should
|
||||
* count the number of times a thread has locked the mutex and
|
||||
* not fully release it for use by another thread until it has
|
||||
* been released the same number of times it was locked.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create mutex */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Initialise the shared_data to zero */
|
||||
shared_data = 0;
|
||||
|
||||
/* Take the mutex several times */
|
||||
for (i = 0; i < TEST_LOCK_CNT; i++)
|
||||
{
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Create second thread */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and should block on
|
||||
* the mutex until we release it. We wait a while and check that
|
||||
* shared_data has not been modified.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* Sleep for a while to give the second thread a chance to
|
||||
* modify shared_data, thought it shouldn't until we
|
||||
* release the mutex enough times.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check shared data. The second thread always sets it to one. */
|
||||
if (shared_data != 0)
|
||||
{
|
||||
ATOMLOG (_STR("Shared data modified\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check successful so far */
|
||||
if (failures == 0)
|
||||
{
|
||||
/*
|
||||
* Release the mutex TEST_LOCK_CNT-1 times, after which we
|
||||
* should still own the mutex (until we release one more time).
|
||||
*/
|
||||
for (i = 0; i < TEST_LOCK_CNT-1; i++)
|
||||
{
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed release\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait a little while then check that shared_data has
|
||||
* not been modified (we should still own the mutex).
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (shared_data != 0)
|
||||
{
|
||||
ATOMLOG (_STR("Expected unmodified\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the mutex one more time, after which we should no
|
||||
* longer own the mutex (and wake up the second thread).
|
||||
*/
|
||||
if (atomMutexPut (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed release\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait a little while then check that shared_data has
|
||||
* been modified by the second thread.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (shared_data != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Expected modified\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally attempt to release the mutex one more time, while
|
||||
* we no longer own the mutex. Either the second thread will
|
||||
* have ownership of it, or no thread will have ownership.
|
||||
* In both cases we expect to get an ownership error when we
|
||||
* attempt to release it.
|
||||
*/
|
||||
if (atomMutexPut (&mutex1) != ATOM_ERR_OWNERSHIP)
|
||||
{
|
||||
ATOMLOG (_STR("Failed locked+1 release\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete mutex, test finished */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Repeatedly attempt to get the mutex and set shared_data to 1 */
|
||||
while (1)
|
||||
{
|
||||
/* Block on the mutex */
|
||||
if ((status = atomMutexGet (&mutex1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting mutex, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Got the mutex */
|
||||
else
|
||||
{
|
||||
/* Set shared_data to signify that we think we have the mutex */
|
||||
shared_data = 1;
|
||||
|
||||
/* Release the mutex allowing the main thread to take it again */
|
||||
if ((status = atomMutexPut (&mutex1)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting mutex, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Loop forever - we only reach here on error */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
236
tests/mutex7.c
Normal file
236
tests/mutex7.c
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atommutex.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Global shared data */
|
||||
static volatile int shared_data;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This tests the ownership checks of the mutex library. Only threads
|
||||
* which own a mutex can release it. It should not be possible to
|
||||
* release a mutex if it is not owned by any thread, is owned by a
|
||||
* different thread, or at interrupt context. We test here that all
|
||||
* three cases are trapped.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
ATOM_TIMER timer_cb;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create mutex */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Initialise the shared_data to zero */
|
||||
shared_data = 0;
|
||||
|
||||
/* Attempt to release the mutex when not owned by any thread */
|
||||
if (atomMutexPut (&mutex1) != ATOM_ERR_OWNERSHIP)
|
||||
{
|
||||
ATOMLOG (_STR("Release error\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create second thread */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and should take ownership
|
||||
* of the mutex. We wait a while and check that shared_data has been
|
||||
* modified, which proves to us that the thread has taken the mutex.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (shared_data != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Shared data unmodified\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Check successful so far */
|
||||
if (failures == 0)
|
||||
{
|
||||
/*
|
||||
* Attempt to release the mutex again now that it is owned
|
||||
* by another thread.
|
||||
*/
|
||||
if (atomMutexPut (&mutex1) != ATOM_ERR_OWNERSHIP)
|
||||
{
|
||||
ATOMLOG (_STR("Release error 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Finally check that the mutex cannot be released from an ISR */
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = testCallback;
|
||||
timer_cb.cb_data = NULL;
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
|
||||
/* Request the timer callback to run in one second */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error registering timer\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait two seconds for shared_date to be set to 2
|
||||
* indicating success. This happens if the timer
|
||||
* callback received the expected ownership error
|
||||
* when attempting to release the mutex.
|
||||
*/
|
||||
else
|
||||
{
|
||||
atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC);
|
||||
if (shared_data != 2)
|
||||
{
|
||||
ATOMLOG (_STR("Context check failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete mutex, test finished */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Block on the mutex */
|
||||
if ((status = atomMutexGet (&mutex1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting mutex, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
}
|
||||
|
||||
/* Got the mutex */
|
||||
else
|
||||
{
|
||||
/* Set shared_data to signify that we think we have the mutex */
|
||||
shared_data = 1;
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Attempt an atomMutexPut() on mutex1 from interrupt context.
|
||||
* Should receive an ATOM_ERR_OWNERSHIP error. Sets shared_data
|
||||
* to 2 if passes.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
/* Check the return value from atomMutexPut() */
|
||||
if (atomMutexPut(&mutex1) == ATOM_ERR_OWNERSHIP)
|
||||
{
|
||||
/* Received the error we expected, set shared_data to notify success */
|
||||
shared_data = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Did not get expected error, don't set shared_data signifying fail */
|
||||
}
|
||||
|
||||
}
|
||||
200
tests/mutex8.c
Normal file
200
tests/mutex8.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atommutex.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test results */
|
||||
static volatile int pass_flag[3];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This test verifies the mutex deletion API, by deleting a mutex
|
||||
* on which multiple threads are blocking, and checking that all three
|
||||
* are woken up with an appropriate error code.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Initialise pass status for all three threads to FALSE */
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
pass_flag[i] = FALSE;
|
||||
}
|
||||
|
||||
/* Test wakeup of three threads on mutex deletion */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* Take the mutex to ensure that all three test threads will block */
|
||||
if (atomMutexGet (&mutex1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 2 */
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 3 */
|
||||
else if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test threads now created */
|
||||
else
|
||||
{
|
||||
/* Wait a while for threads to start blocking on mutex1 */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Delete mutex1 now that all three threads should be blocking */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait a while for all three threads to wake up */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check that all three threads have passed */
|
||||
if ((pass_flag[0] != TRUE) || (pass_flag[1] != TRUE) || (pass_flag[2] != TRUE))
|
||||
{
|
||||
ATOMLOG (_STR("Thread fail\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test threads.
|
||||
*
|
||||
* @param[in] data Thread ID (0-2)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
int thread_id;
|
||||
|
||||
/* Pull out the passed thread ID */
|
||||
thread_id = (int)data;
|
||||
|
||||
/*
|
||||
* Wait on mutex1 with timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomMutexGet(&mutex1, (5 * SYSTEM_TICKS_PER_SEC));
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set pass_flag to notify success */
|
||||
pass_flag[thread_id] = TRUE;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
187
tests/mutex9.c
Normal file
187
tests/mutex9.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atommutex.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_MUTEX mutex1;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Global shared data protected by mutex */
|
||||
static volatile int shared_data;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start mutex test.
|
||||
*
|
||||
* This tests timeouts on a mutex. We make a thread block with timeout
|
||||
* on a mutex, and test that sufficient time has actually passed as
|
||||
* was requested by the timeout parameter.
|
||||
*
|
||||
* The main thread creates a second thread which will immediately take
|
||||
* ownership of the mutex. The test checks that the correct timeout
|
||||
* occurs when the first thread blocks on the mutex which is already
|
||||
* owned (by the second thread).
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t start_time, end_time;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create mutex */
|
||||
if (atomMutexCreate (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Initialise the shared_data to zero */
|
||||
shared_data = 0;
|
||||
|
||||
/* Create second thread */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and should take ownership
|
||||
* of the mutex. We wait a while and check that shared_data has been
|
||||
* modified, which proves to us that the thread has taken the mutex.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (shared_data != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Shared data unmodified\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Check successful so far */
|
||||
if (failures == 0)
|
||||
{
|
||||
/* Take note of the start time */
|
||||
start_time = atomTimeGet();
|
||||
|
||||
/* Block on the mutex with two second timeout */
|
||||
if (atomMutexGet (&mutex1, 2 * SYSTEM_TICKS_PER_SEC) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Take note of the end time */
|
||||
end_time = atomTimeGet();
|
||||
|
||||
/* Now check that two seconds have passed */
|
||||
if ((end_time < (start_time + (2 * SYSTEM_TICKS_PER_SEC)))
|
||||
|| (end_time > (start_time + (2 * SYSTEM_TICKS_PER_SEC) + 1)))
|
||||
{
|
||||
ATOMLOG (_STR("Bad time\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete mutex, test finished */
|
||||
if (atomMutexDelete (&mutex1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Block on the mutex */
|
||||
if ((status = atomMutexGet (&mutex1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting mutex, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
}
|
||||
|
||||
/* Got the mutex */
|
||||
else
|
||||
{
|
||||
/* Set shared_data to signify that we think we have the mutex */
|
||||
shared_data = 1;
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
158
tests/queue1.c
Normal file
158
tests/queue1.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 16
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This test exercises the queue creation and deletion APIs.
|
||||
*
|
||||
* Testing of deletion while threads are actually blocking in
|
||||
* queue APIs is tested in queue2.c and queue3.c.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test creation and deletion of queues: good values */
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) == ATOM_OK)
|
||||
{
|
||||
if (atomQueueDelete (&queue1) == ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error deleting queue\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating queue\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test creation and deletion of queues: creation checks */
|
||||
if (atomQueueCreate (NULL, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad queue ptr check\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueueCreate (&queue1, NULL, sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad buff ptr check\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], 0, QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad size check\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), 0) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad entries check\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test creation and deletion of queues: deletion checks */
|
||||
if (atomQueueDelete (NULL) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad queue deletion checks\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
218
tests/queue10.c
Normal file
218
tests/queue10.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint32_t queue1_storage[QUEUE_ENTRIES];
|
||||
|
||||
|
||||
/* Test message values (more values than can fit in an entire 8 message queue) */
|
||||
uint32_t test_values[] =
|
||||
{
|
||||
0x12345678,
|
||||
0xFF000000,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xF000000F,
|
||||
0x0F0000F0,
|
||||
0x00F00F00,
|
||||
0x000FF000,
|
||||
0x87654321,
|
||||
0xABCD0000,
|
||||
0x0000CDEF
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This tests basic operation of queues.
|
||||
*
|
||||
* Messages are posted to and received from the queue and checked
|
||||
* against expected values. To ensure correct ordering, queue posts
|
||||
* and receives are done in blocks of different amounts, such that
|
||||
* there will already be different numbers of messages in the queue
|
||||
* whenever messages are posted and received.
|
||||
*
|
||||
* We test using 4-byte messages.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures, tx_count, rx_count;
|
||||
uint32_t msg;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create test queue */
|
||||
if (atomQueueCreate (&queue1, (uint8_t *)&queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test queue\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* Reset tx/rx counts */
|
||||
tx_count = rx_count = 0;
|
||||
|
||||
/* Post 2 messages to the queue */
|
||||
for (; tx_count < 2; tx_count++)
|
||||
{
|
||||
msg = test_values[tx_count];
|
||||
if (atomQueuePut (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed post\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive 1 message from the queue */
|
||||
for (; rx_count < 2; rx_count++)
|
||||
{
|
||||
if (atomQueueGet (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (msg != test_values[rx_count])
|
||||
{
|
||||
ATOMLOG (_STR("Val%d\n"), rx_count);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post 3 messages to the queue */
|
||||
for (; tx_count < 5; tx_count++)
|
||||
{
|
||||
msg = test_values[tx_count];
|
||||
if (atomQueuePut (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed post\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive 2 messages from the queue */
|
||||
for (; rx_count < 3; rx_count++)
|
||||
{
|
||||
if (atomQueueGet (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (msg != test_values[rx_count])
|
||||
{
|
||||
ATOMLOG (_STR("Val%d\n"), rx_count);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post 5 messages to the queue */
|
||||
for (; tx_count < 10; tx_count++)
|
||||
{
|
||||
msg = test_values[tx_count];
|
||||
if (atomQueuePut (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed post\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive 3 messages from the queue */
|
||||
for (; rx_count < 6; rx_count++)
|
||||
{
|
||||
if (atomQueueGet (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (msg != test_values[rx_count])
|
||||
{
|
||||
ATOMLOG (_STR("Val%d\n"), rx_count);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post 2 messages to the queue */
|
||||
for (; tx_count < 12; tx_count++)
|
||||
{
|
||||
msg = test_values[tx_count];
|
||||
if (atomQueuePut (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed post\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive 6 messages from the queue */
|
||||
for (; rx_count < 12; rx_count++)
|
||||
{
|
||||
if (atomQueueGet (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (msg != test_values[rx_count])
|
||||
{
|
||||
ATOMLOG (_STR("Val%d\n"), rx_count);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
277
tests/queue2.c
Normal file
277
tests/queue2.c
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 16
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static ATOM_TCB tcb1, tcb2;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
|
||||
|
||||
/* Test result tracking */
|
||||
static volatile int g_result;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test1_thread_func (uint32_t data);
|
||||
static void test2_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This test exercises queue deletion, waking threads blocking on a queue
|
||||
* in atomQueueGet() if the queue is deleted.
|
||||
*
|
||||
* Deletion wakeups are tested twice: once for a thread which is blocking
|
||||
* in atomQueueGet() with a timeout and once for a thread which is
|
||||
* blocking in atomQueueGet() with no timeout.
|
||||
*
|
||||
* Deletion of threads blocking in atomQueuePut() are tested in queue3.c.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test wakeup of threads on queue deletion (thread blocking with no timeout) */
|
||||
g_result = 0;
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test queue\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create a test thread that will block because the queue is empty */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test1_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created an empty queue. We want to see that the other
|
||||
* thread is woken up if its queue is deleted. This is indicated
|
||||
* through g_result being set.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on queue1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on queue1 now, delete queue1 */
|
||||
if (atomQueueDelete(&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed queue1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue1 deleted. The thread should now wake up and set g_result. */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test wakeup of threads on semaphore deletion (thread blocking with timeout) */
|
||||
g_result = 0;
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test queue\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create a test thread that will block because the queue is empty */
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test2_thread_func, 0,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created an empty queue. We want to see that the other
|
||||
* thread is woken up if its queue is deleted. This is indicated
|
||||
* through g_result being set.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on queue1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on queue1 now, delete queue1 */
|
||||
if (atomQueueDelete(&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed queue1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue1 deleted. The thread should now wake up and set g_result. */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test1_thread_func
|
||||
*
|
||||
* Entry point for test thread 1.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test1_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status, msg;
|
||||
|
||||
/*
|
||||
* Wait on queue1 with no timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomQueueGet(&queue1, 0, &msg);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test2_thread_func
|
||||
*
|
||||
* Entry point for test thread 2.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test2_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status, msg;
|
||||
|
||||
/*
|
||||
* Wait on queue1 with timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomQueueGet(&queue1, (5 * SYSTEM_TICKS_PER_SEC), &msg);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test2 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
317
tests/queue3.c
Normal file
317
tests/queue3.c
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 16
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static ATOM_TCB tcb1, tcb2;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
|
||||
|
||||
/* Test result tracking */
|
||||
static volatile int g_result;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test1_thread_func (uint32_t data);
|
||||
static void test2_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This test exercises queue deletion, waking threads blocking on a queue
|
||||
* in atomQueuePut() if the queue is deleted.
|
||||
*
|
||||
* Deletion wakeups are tested twice: once for a thread which is blocking
|
||||
* in atomQueuePut() with a timeout and once for a thread which is
|
||||
* blocking in atomQueuePut() with no timeout.
|
||||
*
|
||||
* Deletion of threads blocking in atomQueueGet() are tested in queue2.c.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures, i;
|
||||
uint8_t msg;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Set a test value for posting to the queue */
|
||||
msg = 0x66;
|
||||
|
||||
/* Test wakeup of threads on queue deletion (thread blocking with no timeout) */
|
||||
g_result = 0;
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test queue\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Successful queue creation */
|
||||
else
|
||||
{
|
||||
/* Fill up all entries */
|
||||
for (i = 0; i < QUEUE_ENTRIES; i++)
|
||||
{
|
||||
if (atomQueuePut (&queue1, 0, &msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error filling queue\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a test thread that will block because the queue is full */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test1_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created and filled a queue. We want to see that the other
|
||||
* thread is woken up if its queue is deleted. This is indicated
|
||||
* through g_result being set.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on queue1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on queue1 now, delete queue1 */
|
||||
if (atomQueueDelete(&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed queue1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue1 deleted. The thread should now wake up and set g_result. */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test wakeup of threads on semaphore deletion (thread blocking with timeout) */
|
||||
g_result = 0;
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test queue\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Successful queue creation */
|
||||
else
|
||||
{
|
||||
/* Fill up all entries */
|
||||
for (i = 0; i < QUEUE_ENTRIES; i++)
|
||||
{
|
||||
if (atomQueuePut (&queue1, 0, &msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error filling queue\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a test thread that will block because the queue is full */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test2_thread_func, 0,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created and filled a queue. We want to see that the other
|
||||
* thread is woken up if its queue is deleted. This is indicated
|
||||
* through g_result being set.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on queue1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on queue1 now, delete queue1 */
|
||||
if (atomQueueDelete(&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed queue1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue1 deleted. The thread should now wake up and set g_result. */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test1_thread_func
|
||||
*
|
||||
* Entry point for test thread 1.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test1_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status, msg;
|
||||
|
||||
/* Set a test value for posting to the queue */
|
||||
msg = 0x66;
|
||||
|
||||
/*
|
||||
* Post to queue1 with no timeout. The queue should be full so
|
||||
* we are expecting to block. We should then be woken up by the
|
||||
* main thread while blocking.
|
||||
*/
|
||||
status = atomQueuePut(&queue1, 0, &msg);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test2_thread_func
|
||||
*
|
||||
* Entry point for test thread 2.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test2_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status, msg;
|
||||
|
||||
/* Set a test value for posting to the queue */
|
||||
msg = 0x66;
|
||||
|
||||
/*
|
||||
* Post to queue1 with timeout. The queue should be full so
|
||||
* we are expecting to block. We should then be woken up by the
|
||||
* main thread while blocking.
|
||||
*/
|
||||
status = atomQueuePut(&queue1, (5 * SYSTEM_TICKS_PER_SEC), &msg);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test2 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
293
tests/queue4.c
Normal file
293
tests/queue4.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Number of queue entries */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1, queue2;
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
static uint8_t queue2_storage[QUEUE_ENTRIES];
|
||||
|
||||
|
||||
/* Test result tracking */
|
||||
static volatile int g_result;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallbackGet (POINTER cb_data);
|
||||
static void testCallbackPut (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This test exercises the atomQueueGet() and atomQueuePut() APIs
|
||||
* particularly forcing the various error indications which can be
|
||||
* returned from the APIs to ensure that handling for these corner
|
||||
* cases has been correctly implemented.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint8_t msg;
|
||||
ATOM_TIMER timer_cb;
|
||||
int count;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create two test queues: queue1 is empty, queue2 is full */
|
||||
|
||||
/* Empty queue1 creation */
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Queue1 create\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Full queue2 creation */
|
||||
if (atomQueueCreate (&queue2, &queue2_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Queue2 create\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fill the queue */
|
||||
msg = 0x66;
|
||||
for (count = 0; count < QUEUE_ENTRIES; count++)
|
||||
{
|
||||
/* Add one message at a time */
|
||||
if (atomQueuePut (&queue2, 0, &msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Queue2 put\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test parameter checks */
|
||||
if (atomQueueGet (NULL, 0, &msg) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Get queue param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueueGet (&queue1, 0, NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Get msg param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueuePut (NULL, 0, &msg) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Put queue param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueuePut (&queue1, 0, NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Put msg param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test atomQueueGet() can not be called from interrupt context */
|
||||
g_result = 0;
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = testCallbackGet;
|
||||
timer_cb.cb_data = NULL;
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
|
||||
/* Request the timer callback to run in one second */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error registering timer\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait two seconds for g_result to be set indicating success */
|
||||
else
|
||||
{
|
||||
atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Get context check failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test atomQueuePut() can not be called from interrupt context */
|
||||
g_result = 0;
|
||||
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = testCallbackPut;
|
||||
timer_cb.cb_data = NULL;
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
|
||||
/* Request the timer callback to run in one second */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error registering timer\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait two seconds for g_result to be set indicating success */
|
||||
else
|
||||
{
|
||||
atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC);
|
||||
if (g_result != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Put context check failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test ATOM_TIMEOUT is returned for Get/Put calls with timeout */
|
||||
|
||||
/* Attempt atomQueueGet() on empty queue to force timeout */
|
||||
if (atomQueueGet (&queue1, SYSTEM_TICKS_PER_SEC, &msg) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Timeout q1 failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Attempt atomQueuePut() on full queue to force timeout */
|
||||
msg = 0x66;
|
||||
if (atomQueuePut (&queue2, SYSTEM_TICKS_PER_SEC, &msg) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Timeout q2 failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test ATOM_WOULDBLOCK is returned for Get/Put calls with -1 timeout */
|
||||
|
||||
/* Attempt atomQueueGet() on empty queue to force block */
|
||||
if (atomQueueGet (&queue1, -1, &msg) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
ATOMLOG (_STR("Timeout q1 failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Attempt atomQueuePut() on full queue to force block */
|
||||
msg = 0x66;
|
||||
if (atomQueuePut (&queue2, -1, &msg) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
ATOMLOG (_STR("Timeout q2 failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the test queues */
|
||||
if (atomQueueDelete (&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error deleting q1\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomQueueDelete (&queue2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error deleting q2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallbackGet
|
||||
*
|
||||
* Attempt an atomQueueGet() on (empty) queue1 from interrupt context.
|
||||
* Should receive an ATOM_ERR_CONTEXT error. Sets g_result if passes.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallbackGet (POINTER cb_data)
|
||||
{
|
||||
uint8_t msg;
|
||||
|
||||
/* Check the return value from atomQueueGet() */
|
||||
if (atomQueueGet(&queue1, 0, &msg) == ATOM_ERR_CONTEXT)
|
||||
{
|
||||
/* Received the error we expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Did not get expected error, don't set g_result signifying fail */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallbackPut
|
||||
*
|
||||
* Attempt an atomQueuePut() on (full) queue2 from interrupt context.
|
||||
* Should receive an ATOM_ERR_CONTEXT error. Sets g_result if passes.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallbackPut (POINTER cb_data)
|
||||
{
|
||||
uint8_t msg;
|
||||
|
||||
/* Check the return value from atomQueuePut() */
|
||||
msg = 0x66;
|
||||
if (atomQueuePut(&queue2, 0, &msg) == ATOM_ERR_CONTEXT)
|
||||
{
|
||||
/* Received the error we expected, set g_result to notify success */
|
||||
g_result = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Did not get expected error, don't set g_result signifying fail */
|
||||
}
|
||||
|
||||
}
|
||||
276
tests/queue5.c
Normal file
276
tests/queue5.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomqueue.h"
|
||||
|
||||
|
||||
/* Number of queue entries */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
/* Data updated by threads */
|
||||
static volatile uint8_t wake_cnt;
|
||||
static volatile uint8_t wake_order[4];
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* With multiple threads blocking on a single queue, this test confirms that
|
||||
* they are woken in order when the queue is posted. The correct order for
|
||||
* waking is that the higher priority threads are woken first, followed by the
|
||||
* lower priority threads. Where multiple threads of the same priority are
|
||||
* waiting, the threads are woken in FIFO order (the order in which they started
|
||||
* waiting on the queue).
|
||||
*
|
||||
* To test this we create four threads which all wait on a single queue.
|
||||
* One pair of threads are running at high priority, with the other pair at a
|
||||
* lower priority:
|
||||
*
|
||||
* Thread 1: low prio thread A
|
||||
* Thread 2: low prio thread B
|
||||
* Thread 3: high prio thread A
|
||||
* Thread 4: high prio thread B
|
||||
*
|
||||
* The threads are forced to start blocking on the same queue in the
|
||||
* above order.
|
||||
*
|
||||
* We expect to see them woken up in the following order:
|
||||
* 3, 4, 1, 2
|
||||
*
|
||||
* This proves the multiple blocking thread ordering in terms of both
|
||||
* the priority-queueing and same-priority-FIFO-queueing.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures, count;
|
||||
uint8_t msg;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create empty queue */
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test q1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Start the threads */
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The test threads all start by calling atomQueueGet() to receive
|
||||
* a message from the queue. Because the queue is empty, all test
|
||||
* threads should immediately block on the queue (until a message
|
||||
* is posted to it).
|
||||
*/
|
||||
|
||||
/* Create Thread 1 (lower priority thread A) */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO+1, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the queue */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 2 (lower priority thread B) */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO+1, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the queue */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 3 (higher priority thread A) */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the queue */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 4 (higher priority thread B) */
|
||||
if (atomThreadCreate(&tcb4, TEST_THREAD_PRIO, test_thread_func, 4,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the queue */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* All four threads will now be blocking on queue1 */
|
||||
|
||||
/*
|
||||
* Initialise wake count, used by threads to determine
|
||||
* what order they were woken in.
|
||||
*/
|
||||
wake_cnt = 0;
|
||||
|
||||
/* Loop waking all four threads */
|
||||
for (count = 0; count < 4; count++)
|
||||
{
|
||||
/*
|
||||
* Post a message to the queue. This will wake up one of the threads
|
||||
* blocking on it (because it is currently empty). That thread will
|
||||
* wake up, note the order at which it was woken, then go to sleep
|
||||
* forever leaving the queue empty again. This is done four times so
|
||||
* that all four threads are woken, noting their wake order.
|
||||
*/
|
||||
msg = 0x66;
|
||||
if (atomQueuePut (&queue1, 0, &msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Post fail\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sleep to give the thread time to wake up and modify the shared
|
||||
* global data wake_cnt and wake_order[]. We deliberately do not
|
||||
* use a mutex for protecting access to this shared data, as we
|
||||
* are testing the queue module in isolation here.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC / 4);
|
||||
|
||||
}
|
||||
|
||||
/* All four threads now woken up, check they woke in correct order */
|
||||
if ((wake_order[0] != 3) && (wake_order[1] != 4)
|
||||
&& (wake_order[2] != 1) && (wake_order[3] != 2))
|
||||
{
|
||||
ATOMLOG (_STR("Bad order %d,%d,%d,%d\n"),
|
||||
wake_order[0], wake_order[1], wake_order[2], wake_order[3]);
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete queue, test finished */
|
||||
if (atomQueueDelete (&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* four test threads, with the thread number/ID (1-4) passed as the entry
|
||||
* point parameter.
|
||||
*
|
||||
* @param[in] data Thread number (1,2,3,4)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t thread_id;
|
||||
uint8_t msg;
|
||||
|
||||
/* Thread ID is passed through the function parameter */
|
||||
thread_id = (uint8_t)data;
|
||||
|
||||
/*
|
||||
* Wait for a message to appear on queue1. At creation of all test
|
||||
* threads the queue is empty, so all four threads will block here.
|
||||
*/
|
||||
if (atomQueueGet (&queue1, 0, &msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Get fail\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Store our thread ID in the array using the current
|
||||
* wake_cnt order. The threads are woken with large
|
||||
* pauses between, which provides protection for this
|
||||
* global data. This allows us to test queues without
|
||||
* assuming a working implementation of a mutex (or
|
||||
* similar protection mechanism).
|
||||
*/
|
||||
wake_order[wake_cnt++] = thread_id;
|
||||
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
241
tests/queue6.c
Normal file
241
tests/queue6.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint32_t queue1_storage[QUEUE_ENTRIES];
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test message values (more values than can fit in an entire 8 message queue) */
|
||||
uint32_t test_values[] =
|
||||
{
|
||||
0x12345678,
|
||||
0xFF000000,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xF000000F,
|
||||
0x0F0000F0,
|
||||
0x00F00F00,
|
||||
0x000FF000,
|
||||
0x87654321,
|
||||
0xABCD0000,
|
||||
0x0000CDEF
|
||||
};
|
||||
|
||||
/* Test result tracking */
|
||||
static volatile int g_result;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test1_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This tests basic operation of queues.
|
||||
*
|
||||
* The main test thread creates a second thread and posts
|
||||
* a series of messages to the second thread. The message
|
||||
* values are checked against the expected values.
|
||||
*
|
||||
* We test using 4-byte messages.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures, count;
|
||||
int num_entries;
|
||||
uint32_t msg;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
g_result = 0;
|
||||
|
||||
/* Create test queue */
|
||||
if (atomQueueCreate (&queue1, (uint8_t *)&queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test queue\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create a test thread that will block because the queue is empty */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO + 1, test1_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created an empty queue and a thread which should now
|
||||
* be blocking on the queue. The test thread is lower priority
|
||||
* than us.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on queue1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Post all entries in the test array to the queue.
|
||||
* Because the second thread is lower priority than
|
||||
* us, we will post 8 messages until the queue is
|
||||
* full without waking up the second thread at all.
|
||||
* At that point, we will block and the second
|
||||
* thread will remove one message from the queue.
|
||||
* With a spare entry in the queue, this thread
|
||||
* will wake up again and post another message.
|
||||
* This will continue until this thread has posted
|
||||
* all messages, at which point the second thread
|
||||
* will drain all remaining messages from the
|
||||
* queue.
|
||||
*
|
||||
* Through this scheme we are able to test posting
|
||||
* to the queue at all possible fill levels.
|
||||
*/
|
||||
num_entries = sizeof(test_values) / sizeof(test_values[0]);
|
||||
for (count = 0; count < num_entries; count++)
|
||||
{
|
||||
/* Increment through and post all test values to the queue */
|
||||
msg = test_values[count];
|
||||
if (atomQueuePut (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed post\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sleep a while for the second thread to finish */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
|
||||
/* Check that the second thread has found all test values */
|
||||
if (g_result != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Bad test vals\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test1_thread_func
|
||||
*
|
||||
* Entry point for test thread 1.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test1_thread_func (uint32_t data)
|
||||
{
|
||||
uint32_t msg;
|
||||
int num_entries, count, failures;
|
||||
|
||||
/* Default to no errors */
|
||||
failures = 0;
|
||||
|
||||
/*
|
||||
* Loop receiving messages until we have received the number of
|
||||
* values in the test array.
|
||||
*/
|
||||
num_entries = sizeof(test_values) / sizeof(test_values[0]);
|
||||
for (count = 0; count < num_entries; count++)
|
||||
{
|
||||
/* Receive a value from the queue */
|
||||
if (atomQueueGet (&queue1, 0, (uint8_t *)&msg) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Check that we received the expected value */
|
||||
else if (msg != test_values[count])
|
||||
{
|
||||
ATOMLOG (_STR("Val%d\n"), count);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set g_result to indicate success if we had no failures.
|
||||
* Thread-protection is not required on g_result because it
|
||||
* is only ever set by this thread.
|
||||
*/
|
||||
if (failures == 0)
|
||||
{
|
||||
/* No failures */
|
||||
g_result = 1;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
201
tests/queue7.c
Normal file
201
tests/queue7.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
static ATOM_TCB tcb1, tcb2, tcb3;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test results */
|
||||
static volatile int pass_flag[3];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This test verifies the queue deletion API, by deleting a queue
|
||||
* on which multiple threads are blocking, and checking that all three
|
||||
* are woken up with an appropriate error code.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Initialise pass status for all three threads to FALSE */
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
pass_flag[i] = FALSE;
|
||||
}
|
||||
|
||||
/* Test wakeup of three threads on queue deletion */
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating Q\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* The queue is empty so all three test threads will block */
|
||||
|
||||
/* Create test thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 2 */
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 3 */
|
||||
else if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test threads now created */
|
||||
else
|
||||
{
|
||||
/* Wait a while for threads to start blocking on queue1 */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Delete queue1 now that all three threads should be blocking */
|
||||
if (atomQueueDelete (&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait a while for all three threads to wake up */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check that all three threads have passed */
|
||||
if ((pass_flag[0] != TRUE) || (pass_flag[1] != TRUE) || (pass_flag[2] != TRUE))
|
||||
{
|
||||
ATOMLOG (_STR("Thread fail\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test threads.
|
||||
*
|
||||
* @param[in] data Thread ID (0-2)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t msg;
|
||||
int thread_id;
|
||||
|
||||
/* Pull out the passed thread ID */
|
||||
thread_id = (int)data;
|
||||
|
||||
/*
|
||||
* Wait on queue1 with timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomQueueGet(&queue1, (5 * SYSTEM_TICKS_PER_SEC), &msg);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set pass_flag to notify success */
|
||||
pass_flag[thread_id] = TRUE;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
118
tests/queue8.c
Normal file
118
tests/queue8.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomqueue.h"
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* This tests timeouts on a queue. We make a thread block with timeout
|
||||
* on a queue, and test that sufficient time has actually passed as
|
||||
* was requested by the timeout parameter.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t start_time, end_time;
|
||||
uint8_t msg;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create queue */
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating Q\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* The queue is empty so atomQueueGet() calls will block */
|
||||
|
||||
/* Take note of the start time */
|
||||
start_time = atomTimeGet();
|
||||
|
||||
/* Block on the queue with two second timeout */
|
||||
if (atomQueueGet (&queue1, 2 * SYSTEM_TICKS_PER_SEC, &msg) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Failed get\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Take note of the end time */
|
||||
end_time = atomTimeGet();
|
||||
|
||||
/* Now check that two seconds have passed */
|
||||
if ((end_time < (start_time + (2 * SYSTEM_TICKS_PER_SEC)))
|
||||
|| (end_time > (start_time + (2 * SYSTEM_TICKS_PER_SEC) + 1)))
|
||||
{
|
||||
ATOMLOG (_STR("Bad time\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete queue, test finished */
|
||||
if (atomQueueDelete (&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
265
tests/queue9.c
Normal file
265
tests/queue9.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomqueue.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Number of test loops for stress-test */
|
||||
#define NUM_TEST_LOOPS 10000
|
||||
|
||||
|
||||
/* Test queue size */
|
||||
#define QUEUE_ENTRIES 8
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_QUEUE queue1;
|
||||
static uint8_t queue1_storage[QUEUE_ENTRIES];
|
||||
static ATOM_SEM sem1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/*
|
||||
* Global failure count (can be updated by test threads but is
|
||||
* protected by an interrupt lockout).
|
||||
*/
|
||||
static volatile int g_failures;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start queue test.
|
||||
*
|
||||
* Stress-tests queue Get and Put operations. Four threads are created which are
|
||||
* continually Putting and Getting the same queue, with no time delays between
|
||||
* each Get/Put. Because all threads are at the same priority this ensures that
|
||||
* on timeslices when threads are rescheduled there are several context-switch
|
||||
* points, while the threads may be part-way through queue API calls.
|
||||
*
|
||||
* @retval Number of g_failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
int finish_cnt;
|
||||
|
||||
/* Default to zero g_failures */
|
||||
g_failures = 0;
|
||||
|
||||
/* Create queue to stress */
|
||||
if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating Q\n"));
|
||||
g_failures++;
|
||||
}
|
||||
|
||||
/* Create sem to receive thread-finished notification */
|
||||
else if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating sem\n"));
|
||||
g_failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create Thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 2 */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 3 */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 4 */
|
||||
if (atomThreadCreate(&tcb4, TEST_THREAD_PRIO, test_thread_func, 4,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/*
|
||||
* All four threads will now be performing Gets/Puts on queue1.
|
||||
* When they have finished they will post sem1, so we wait
|
||||
* until sem1 is posted four times.
|
||||
*/
|
||||
finish_cnt = 0;
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
* Attempt to Get sem1. When we have managed to get
|
||||
* the semaphore four times, it must have been posted
|
||||
* by all four threads.
|
||||
*/
|
||||
if (atomSemGet (&sem1, 0) == ATOM_OK)
|
||||
{
|
||||
/* Increment our count of finished threads */
|
||||
finish_cnt++;
|
||||
|
||||
/* Check if all four threads have now posted sem1 */
|
||||
if (finish_cnt == 4)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete OS objects, test finished */
|
||||
if (atomQueueDelete (&queue1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (g_failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), g_failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return g_failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* four test threads.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint32_t loop_cnt;
|
||||
uint8_t status;
|
||||
uint8_t msg;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Run a Put/Get pair many times */
|
||||
loop_cnt = NUM_TEST_LOOPS;
|
||||
while (loop_cnt--)
|
||||
{
|
||||
/* Put a message in the queue */
|
||||
msg = 0x66;
|
||||
if ((status = atomQueuePut (&queue1, 0, &msg)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting mutex, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Retrieve a messages from the queue */
|
||||
if ((status = atomQueueGet (&queue1, 0, &msg)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting queue msg, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post sem1 to notify the main thread we're finished */
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem1 putfail\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
343
tests/sem1.c
Normal file
343
tests/sem1.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1, sem2;
|
||||
static ATOM_TCB tcb1, tcb2;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test1_thread_func (uint32_t data);
|
||||
static void test2_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This test exercises the semaphore creation and deletion APIs, including
|
||||
* waking threads blocking on a semaphore if the semaphore is deleted.
|
||||
* Deletion wakeups are tested twice: once for a thread which is blocking
|
||||
* with a timeout and once for a thread which is blocking with no timeout.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t i;
|
||||
uint8_t status;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test creation and deletion of semaphores: good values */
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
if (atomSemCreate (&sem1, 0) == ATOM_OK)
|
||||
{
|
||||
if (atomSemDelete (&sem1) == ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error deleting semaphore\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating semaphore\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test creation and deletion of semaphores: creation checks */
|
||||
if (atomSemCreate (NULL, 0) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad semaphore creation checks\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test creation and deletion of semaphores: deletion checks */
|
||||
if (atomSemDelete (NULL) != ATOM_OK)
|
||||
{
|
||||
/* Success */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Bad semaphore deletion checks\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test wakeup of threads on semaphore deletion (thread blocking with no timeout) */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else if (atomSemCreate (&sem2, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test1_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* We have created two semaphores. sem1 is for the other thread
|
||||
* to wait on, which we will delete from this thread. We want
|
||||
* to see that the other thread is woken up if its semaphore
|
||||
* is deleted. This is indicated through sem2 being posted
|
||||
* back to us.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on sem1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on sem1 now, delete sem1 */
|
||||
if (atomSemDelete(&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed sem1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Sem1 deleted. The thread should now wake up and post sem2. */
|
||||
if ((status = atomSemGet (&sem2, (5*SYSTEM_TICKS_PER_SEC))) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail (%d)\n"), status);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
|
||||
/* Clean up the last remaining semaphore */
|
||||
if (atomSemDelete (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed sem2 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test wakeup of threads on semaphore deletion (thread blocking with timeout) */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomSemCreate (&sem2, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 2\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test2_thread_func, 0,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We have created two semaphores. sem1 is for the other thread
|
||||
* to wait on, which we will delete from this thread. We want
|
||||
* to see that the other thread is woken up if its semaphore
|
||||
* is deleted. This is indicated through sem2 being posted
|
||||
* back to us.
|
||||
*/
|
||||
|
||||
/* Wait for the other thread to start blocking on sem1 */
|
||||
if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed timer delay\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The other thread will be blocking on sem1 now, delete sem1 */
|
||||
if (atomSemDelete(&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed sem1 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Sem1 deleted. The thread should now wake up and post sem2. */
|
||||
if ((status = atomSemGet (&sem2, (5*SYSTEM_TICKS_PER_SEC))) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Notify fail (%d)\n"), status);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success */
|
||||
|
||||
/* Clean up the last remaining semaphore */
|
||||
if (atomSemDelete (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed sem2 delete\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test1_thread_func
|
||||
*
|
||||
* Entry point for test thread 1.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test1_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/*
|
||||
* Wait on sem1 with no timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomSemGet(&sem1, 0);
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, post sem2 to notify success */
|
||||
if ((status = atomSemPut(&sem2)) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error posting sem2 on wakeup (%d)\n"), status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test2_thread_func
|
||||
*
|
||||
* Entry point for test thread 2.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test2_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/*
|
||||
* Wait on sem1 with timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomSemGet(&sem1, (5 * SYSTEM_TICKS_PER_SEC));
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test2 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, post sem2 to notify success */
|
||||
if ((status = atomSemPut(&sem2)) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error posting sem2 on wakeup\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
334
tests/sem2.c
Normal file
334
tests/sem2.c
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1, sem2;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This test exercises the atomSemGet() and atomSemPut() APIs including
|
||||
* forcing the various error indications which can be returned from the
|
||||
* APIs to ensure that handling for these corner cases have been correctly
|
||||
* implemented.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint8_t status;
|
||||
ATOM_TIMER timer_cb;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test semaphore wait timeouts */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait on semaphore with timeout */
|
||||
if ((status = atomSemGet (&sem1, SYSTEM_TICKS_PER_SEC)) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Timo %d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Test semaphore still operates correctly after timeout */
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Put failed\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Count should now be 1 */
|
||||
if (atomSemGet (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Get failed\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Delete it, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test semaphore blocks if count is zero */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait on semaphore with timeout */
|
||||
if ((status = atomSemGet (&sem1, SYSTEM_TICKS_PER_SEC)) != ATOM_TIMEOUT)
|
||||
{
|
||||
ATOMLOG (_STR("Timo %d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Delete it, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test semaphore does not block if count is one */
|
||||
if (atomSemCreate (&sem1, 1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait on semaphore with timeout */
|
||||
if ((status = atomSemGet (&sem1, SYSTEM_TICKS_PER_SEC)) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Get %d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Delete it, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test parameter checks */
|
||||
if (atomSemGet (NULL, 0) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Get param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomSemPut (NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Put param failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test atomSemGet() cannot be called from interrupt context */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test sem1\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomSemCreate (&sem2, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test sem2\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fill out the timer callback request structure */
|
||||
timer_cb.cb_func = testCallback;
|
||||
timer_cb.cb_data = NULL;
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
|
||||
/* Request the timer callback to run in one second */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error registering timer\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait up to two seconds for sem2 to be posted indicating success */
|
||||
else if (atomSemGet (&sem2, 2 * SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Context check failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the two test semaphores */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem1 delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomSemDelete (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem2 delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test for ATOM_WOULDBLOCK */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test sem1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Semaphore count is zero so will block */
|
||||
if ((status = atomSemGet (&sem1, -1)) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
ATOMLOG (_STR("Wouldblock err %d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the test semaphore */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem1 delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Test no timeout */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test sem1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Increment the semaphore */
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error on put\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Semaphore count is one so will not block */
|
||||
if (atomSemGet (&sem1, -1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error on get\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the test semaphore */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem1 delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Test for semaphore counter overflows with too many puts */
|
||||
if (atomSemCreate (&sem1, 255) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test sem1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Increment the semaphore (expect this to overflow the count at 256) */
|
||||
if (atomSemPut (&sem1) != ATOM_ERR_OVF)
|
||||
{
|
||||
ATOMLOG (_STR("Failed to detect overflow\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the test semaphore */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem1 delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Attempt an atomSemGet() on sem1 from interrupt context.
|
||||
* Should receive an ATOM_ERR_CONTEXT error. Posts sem2 if successful.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
/* Check the return value from atomSemGet() */
|
||||
if (atomSemGet(&sem1, 0) == ATOM_ERR_CONTEXT)
|
||||
{
|
||||
/* Received the error we expected, post sem2 to notify success */
|
||||
atomSemPut(&sem2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Did not get the expected error, don't post sem2 and the test
|
||||
* thread will time out, signifying an error.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
253
tests/sem3.c
Normal file
253
tests/sem3.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Data updated by threads */
|
||||
static volatile uint8_t wake_cnt;
|
||||
static volatile uint8_t wake_order[4];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* With multiple threads blocking on a single semaphore, this test confirms that
|
||||
* they are woken in order when the semaphore is posted. The correct order for
|
||||
* waking is that the higher priority threads are woken first, followed by the
|
||||
* lower priority threads. Where multiple threads of the same priority are
|
||||
* waiting, the threads are woken in FIFO order (the order in which they started
|
||||
* waiting on the semaphore).
|
||||
*
|
||||
* To test this we create four threads which all wait on a single semaphore.
|
||||
* One pair of threads are running at high priority, with the other pair at a
|
||||
* lower priority:
|
||||
*
|
||||
* Thread 1: low prio thread A
|
||||
* Thread 2: low prio thread B
|
||||
* Thread 3: high prio thread A
|
||||
* Thread 4: high prio thread B
|
||||
*
|
||||
* The threads are forced to start blocking on the same semaphore in the
|
||||
* above order (the semaphore is initialised with count 0 to ensure any
|
||||
* threads calling atomSemGet() will block).
|
||||
*
|
||||
* We expect to see them woken up in the following order:
|
||||
* 3, 4, 1, 2
|
||||
*
|
||||
* This proves the multiple blocking thread ordering in terms of both
|
||||
* the priority-queueing and same-priority-FIFO-queueing.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create sem with count zero (so that all threads will block) */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create Thread 1 (lower priority thread A) */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO+1, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the semaphore */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 2 (lower priority thread B) */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO+1, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the semaphore */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 3 (higher priority thread A) */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the semaphore */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Create Thread 4 (higher priority thread B) */
|
||||
if (atomThreadCreate(&tcb4, TEST_THREAD_PRIO, test_thread_func, 4,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delay to ensure the thread will start blocking on the semaphore */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* All four threads will now be blocking on sem1 */
|
||||
|
||||
/*
|
||||
* Initialise wake count, used by threads to determine
|
||||
* what order they were woken in.
|
||||
*/
|
||||
wake_cnt = 0;
|
||||
|
||||
/*
|
||||
* Wake the four threads up in order, leaving some time between
|
||||
* each wake up for them to deal with global data in a
|
||||
* thread-safe fashion.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/* Post semaphore to wake one of the threads up */
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Post fail\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Sleep to give the thread time to manipulate global data */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC / 4);
|
||||
}
|
||||
|
||||
/* All four threads now woken up, check they woke in correct order */
|
||||
if ((wake_order[0] != 3) || (wake_order[1] != 4)
|
||||
|| (wake_order[2] != 1) || (wake_order[3] != 2))
|
||||
{
|
||||
ATOMLOG (_STR("Bad order %d,%d,%d,%d\n"),
|
||||
wake_order[0], wake_order[1], wake_order[2], wake_order[3]);
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete semaphore, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* four test threads, with the thread number/ID (1-4) passed as the entry
|
||||
* point parameter.
|
||||
*
|
||||
* @param[in] data Thread number (1,2,3,4)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t thread_id;
|
||||
|
||||
/* Thread ID is passed through the function parameter */
|
||||
thread_id = (uint8_t)data;
|
||||
|
||||
/*
|
||||
* Wait for sem1 to be posted. At creation of all test threads
|
||||
* the semaphore count is zero, so all four threads will block
|
||||
* here.
|
||||
*/
|
||||
if (atomSemGet (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Thread sem fail\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Store our thread ID in the array using the current
|
||||
* wake_cnt order. The threads are deliberately woken up
|
||||
* some time apart to ensure that no protection is required
|
||||
* on this global data.
|
||||
*/
|
||||
wake_order[wake_cnt++] = thread_id;
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
254
tests/sem4.c
Normal file
254
tests/sem4.c
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Number of test loops for stress-test */
|
||||
#define NUM_TEST_LOOPS 10000
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1, sem2;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3, tcb4;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test4_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/*
|
||||
* Global failure count (can be updated by test threads but is
|
||||
* protected by an interrupt lockout).
|
||||
*/
|
||||
static volatile int g_failures;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* Stress-tests semaphore Get and Put operations. Four threads are created which are
|
||||
* continually Getting and Putting the same semaphore, with no time delays between
|
||||
* each Get/Put. A single semaphore is used, which is initially created with a count
|
||||
* of two, meaning that at any one time there should be threads blocking and not
|
||||
* blocking. This ensures that the stress-test covers multiple threads accessing the
|
||||
* semaphore APIs simultaneously, as well as usage of the APIs while other threads
|
||||
* are blocking on the semaphore.
|
||||
*
|
||||
* @retval Number of g_failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
int finish_cnt;
|
||||
|
||||
/* Default to zero g_failures */
|
||||
g_failures = 0;
|
||||
|
||||
/* Create sem to stress with count two */
|
||||
if (atomSemCreate (&sem1, 2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
g_failures++;
|
||||
}
|
||||
/* Create sem to receive thread-finished notification */
|
||||
else if (atomSemCreate (&sem2, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
g_failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create Thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 2 */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 3 */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Create Thread 4 */
|
||||
if (atomThreadCreate(&tcb4, TEST_THREAD_PRIO, test_thread_func, 4,
|
||||
&test4_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/*
|
||||
* All four threads will now be performing Gets/Puts on sem1.
|
||||
* When they have finished they will post sem2, so we wait
|
||||
* until sem2 is posted four times.
|
||||
*/
|
||||
finish_cnt = 0;
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
* Attempt to Get sem2. When we have managed to get
|
||||
* the semaphore four times, it must have been posted
|
||||
* by all four threads.
|
||||
*/
|
||||
if (atomSemGet (&sem2, 0) == ATOM_OK)
|
||||
{
|
||||
/* Increment our count of finished threads */
|
||||
finish_cnt++;
|
||||
|
||||
/* Check if all four threads have now posted sem2 */
|
||||
if (finish_cnt == 4)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete semaphores, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
if (atomSemDelete (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (g_failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), g_failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return g_failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* four test threads.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint32_t loop_cnt;
|
||||
uint8_t status;
|
||||
CRITICAL_STORE;
|
||||
|
||||
/* Run a Get/Put pair many times */
|
||||
loop_cnt = NUM_TEST_LOOPS;
|
||||
while (loop_cnt--)
|
||||
{
|
||||
if ((status = atomSemGet (&sem1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
else if ((status = atomSemPut (&sem1)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post sem2 to notify the main thread we're finished */
|
||||
if (atomSemPut (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem2 putfail\n"));
|
||||
CRITICAL_START ();
|
||||
g_failures++;
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
195
tests/sem5.c
Normal file
195
tests/sem5.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1, sem2;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This tests basic synchronisation between threads using a semaphore.
|
||||
*
|
||||
* A second thread is created, which blocks on a semaphore until posted
|
||||
* by the main thread. Testing all aspects of this transaction proves
|
||||
* the basic usage of semaphores as a synchronisation mechanism
|
||||
* between threads.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create sem with count zero for second thread to block on */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
/* Create sem to receive test-passed notification */
|
||||
else if (atomSemCreate (&sem2, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create second thread */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and should block on
|
||||
* sem1 until we increment it to 1 (sem1 was created with a
|
||||
* count of zero). Once the second thread has stopped blocking
|
||||
* on sem1, it will post sem2 to notify us that it has finished.
|
||||
*
|
||||
* We can test the count of sem2 before and after posting sem1
|
||||
* to ensure that it was the posting of sem1 which caused the
|
||||
* second thread to wake up.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wait a little while, then check the second thread hasn't
|
||||
* already posted sem2, i.e. did not wait until sem1 was posted.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (atomSemGet (&sem2, -1) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
ATOMLOG (_STR("Did not wait\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Post sem1 to stop the second thread blocking */
|
||||
else if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Put fail\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Now check that the second thread has woken up and posted sem2 */
|
||||
else
|
||||
{
|
||||
/* Give the thread some time to wake up and post sem2 */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check that the second thread has now woken and posted sem2 */
|
||||
if (atomSemGet (&sem2, -1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem2 not posted\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Delete semaphores, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomSemDelete (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Block on sem1. Main thread will post when we should wake up. */
|
||||
if ((status = atomSemGet (&sem1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
}
|
||||
|
||||
/* Post sem2 to notify that we received the sem1 notification */
|
||||
else if ((status = atomSemPut (&sem2)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
201
tests/sem6.c
Normal file
201
tests/sem6.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Semaphore count */
|
||||
#define INITIAL_SEM_COUNT 10
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1, sem2;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This tests basic counting semaphore operation between two threads.
|
||||
*
|
||||
* A semaphore is created with a count of 10. A second thread then
|
||||
* ensures that it can decrement the semaphore 10 times before
|
||||
* it can no longer be decremented.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create sem with count ten for second thread to decrement */
|
||||
if (atomSemCreate (&sem1, INITIAL_SEM_COUNT) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
/* Create sem to receive test-passed notification */
|
||||
else if (atomSemCreate (&sem2, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check that sem2 doesn't already have a positive count */
|
||||
if (atomSemGet (&sem2, -1) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem2 already put\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create second thread */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and will attempt to
|
||||
* decrement sem1 ten times, then finally check that it cannot
|
||||
* decrement it any further. If this passes then the second
|
||||
* thread will post sem2 to notify us that the test has passed.
|
||||
*/
|
||||
else
|
||||
{
|
||||
/* Give the second thread one second to post sem2 */
|
||||
if (atomSemGet (&sem2, SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Sem2 not posted\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Delete semaphores, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomSemDelete (&sem2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
int count;
|
||||
int failures;
|
||||
|
||||
/*
|
||||
* Attempt to decrement sem1 ten times, which should happen immediately
|
||||
* each time.
|
||||
*/
|
||||
failures = 0;
|
||||
count = INITIAL_SEM_COUNT;
|
||||
while (count--)
|
||||
{
|
||||
/* Decrement sem1 */
|
||||
if ((status = atomSemGet (&sem1, -1)) != ATOM_OK)
|
||||
{
|
||||
/* Error decrementing semaphore, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check above stage was successful */
|
||||
if (failures == 0)
|
||||
{
|
||||
/* Sem1 should now have a count of zero, and not allow a decrement */
|
||||
if ((status = atomSemGet (&sem1, -1)) != ATOM_WOULDBLOCK)
|
||||
{
|
||||
/* Error getting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("W%d\n"), status);
|
||||
}
|
||||
|
||||
/* Post sem2 to notify that the test passed */
|
||||
else if ((status = atomSemPut (&sem2)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
278
tests/sem7.c
Normal file
278
tests/sem7.c
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1;
|
||||
static ATOM_TCB tcb1;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Global shared data protected by mutex */
|
||||
static volatile int shared_data;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This tests usage of a semaphore for basic mutual exclusion type
|
||||
* operation. Note that Atomthreads has a more fully-featured real
|
||||
* mutex implementation in the mutex module.
|
||||
*
|
||||
* The semaphore sem1 is initialised with a count of 1. Whichever
|
||||
* thread holds this semaphore can then modify the global variable
|
||||
* "shared_data".
|
||||
*
|
||||
* The main thread first takes the "mutex" sem1, then creates a
|
||||
* second thread. The second thread should block on the sem1 mutex
|
||||
* until the main thread releases it. The test checks that the
|
||||
* global "shared_data" is not modified by the second thread
|
||||
* until the main thread releases the mutex.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create sem with count one for mutex purposes */
|
||||
if (atomSemCreate (&sem1, 1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Initialise the shared_data to zero */
|
||||
shared_data = 0;
|
||||
|
||||
/* Take the mutex to ensure only this thread can modify shared_data */
|
||||
if (atomSemGet (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error taking mutex\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create second thread */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second thread has now been created and should block on
|
||||
* the "mutex" sem1 (which now has count zero) until we release
|
||||
* it. We wait a while and check that shared_data has not been
|
||||
* modified.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* Sleep for a while to give the second thread a chance to
|
||||
* modify shared_data, thought it shouldn't until we
|
||||
* release the mutex.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check shared data. The second thread always sets it to one. */
|
||||
if (shared_data != 0)
|
||||
{
|
||||
ATOMLOG (_STR("Shared data modified\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check successful so far */
|
||||
if (failures == 0)
|
||||
{
|
||||
/*
|
||||
* Release the mutex, which will allow the second thread to
|
||||
* wake and start modifying shared_data.
|
||||
*/
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed release\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait a little while then check that shared_data has
|
||||
* been modified.
|
||||
*/
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
if (shared_data != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Expected modify\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release and take the mutex again a few times to ensure
|
||||
* that the mutex continues to protect shared_data.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* Take the mutex again, to prevent second thread accessing
|
||||
* shared_data.
|
||||
*/
|
||||
if (atomSemGet (&sem1, SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Retake %d\n"), i);
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Set shared_data to 0 and wait to ensure that the
|
||||
* second thread doesn't modify it while we have the
|
||||
* mutex again.
|
||||
*/
|
||||
shared_data = 0;
|
||||
|
||||
/* Wait a while to give second thread potential to run */
|
||||
atomTimerDelay(SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/*
|
||||
* Check that shared_data has not been modified while we
|
||||
* own the mutex.
|
||||
*/
|
||||
if (shared_data != 0)
|
||||
{
|
||||
/* Thread is still modifying the data */
|
||||
ATOMLOG (_STR("Still modifying\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the mutex, which will allow the second thread to
|
||||
* wake and start modifying shared_data again.
|
||||
*/
|
||||
if (atomSemPut (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Failed release\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete semaphore, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Repeatedly attempt to get the mutex and set shared_data to 1 */
|
||||
while (1)
|
||||
{
|
||||
/* Block on the mutex sem1 */
|
||||
if ((status = atomSemGet (&sem1, 0)) != ATOM_OK)
|
||||
{
|
||||
/* Error getting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("G%d\n"), status);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Got the mutex */
|
||||
else
|
||||
{
|
||||
/* Set shared_data to signify that we think we have the mutex */
|
||||
shared_data = 1;
|
||||
|
||||
/* Release the mutex allowing the main thread to take it again */
|
||||
if ((status = atomSemPut (&sem1)) != ATOM_OK)
|
||||
{
|
||||
/* Error putting semaphore, notify the status code */
|
||||
ATOMLOG (_STR("P%d\n"), status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Loop forever - we only reach here on error */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
292
tests/sem8.c
Normal file
292
tests/sem8.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomsem.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test running flag */
|
||||
static volatile int test_running;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This stress-tests atomSemGet()/atomSemPut() with a single thread
|
||||
* continually calling atomSemGet() and several contexts continually
|
||||
* calling atomSemPut(). This stresses in particular the atomSemPut()
|
||||
* API, with three threads at different priorities posting
|
||||
* simultaneously, as well as a timer callback posting it from
|
||||
* interrupt context. In all cases the same semaphore is posted.
|
||||
*
|
||||
* This tests the thread-safety and interrupt-safety of the semaphore
|
||||
* APIs, and particularly the atomSemPut() function.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t end_time;
|
||||
ATOM_TIMER timer_cb;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create sem with count of zero */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the test running flag */
|
||||
test_running = TRUE;
|
||||
|
||||
/*
|
||||
* Fill out a timer callback request structure. Pass the timer
|
||||
* structure itself so that the callback can requeue the request.
|
||||
*/
|
||||
timer_cb.cb_func = testCallback;
|
||||
timer_cb.cb_data = &timer_cb;
|
||||
timer_cb.cb_ticks = 1;
|
||||
|
||||
/*
|
||||
* Request a timer callback to run in one tick's time. The callback
|
||||
* will automatically queue another so that this happens repeatedly
|
||||
* until the test is flagged as finished.
|
||||
*/
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error registering timer\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create thread 1: Higher priority than main thread so should sleep */
|
||||
else if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO - 1, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create thread 2: Same priority as main thread so should not sleep */
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create thread 3: Same priority as main thread so should not sleep */
|
||||
else if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO + 1, test_thread_func, 0,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* The test threads have now all been created */
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Continually decrement the semaphore while the test threads
|
||||
* and timer callbacks are continually incrementing it. The
|
||||
* test finishes after this runs without error for 5 seconds.
|
||||
*/
|
||||
end_time = atomTimeGet() + (5 * SYSTEM_TICKS_PER_SEC);
|
||||
while (atomTimeGet() < end_time)
|
||||
{
|
||||
/* Decrement the semaphore */
|
||||
if (atomSemGet (&sem1, SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("SemGet\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test finished, stop the other threads and timer callbacks */
|
||||
test_running = FALSE;
|
||||
|
||||
/*
|
||||
* Wait before finishing: a timer callback could be due
|
||||
* shortly, and we allocated the timer structure off the
|
||||
* local call stack.
|
||||
*/
|
||||
atomTimerDelay(2);
|
||||
}
|
||||
|
||||
/* Delete semaphores, test finished */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete failed\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread.
|
||||
*
|
||||
* @param[in] data sleep_flag passed through here
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
int sleep_flag, count, failures;
|
||||
|
||||
/* Were we requested to sleep occasionally? */
|
||||
sleep_flag = (int)data;
|
||||
|
||||
/* Run until the main thread sets the finish flag or we get an error */
|
||||
failures = 0;
|
||||
while ((test_running == TRUE) && (failures == 0))
|
||||
{
|
||||
/* Post the semaphore 50 times */
|
||||
count = 50;
|
||||
while (count--)
|
||||
{
|
||||
/*
|
||||
* Post the semaphore. Allow overflows as these are likely
|
||||
* to occur when so many threads are posting the same
|
||||
* semaphore continually.
|
||||
*/
|
||||
status = atomSemPut (&sem1);
|
||||
if ((status != ATOM_OK) && (status != ATOM_ERR_OVF))
|
||||
{
|
||||
ATOMLOG (_STR("Put\n"));
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If requested to do so, sleep for a tick. This only happens on threads which
|
||||
* are higher priority than the main test thread, and is necessary to allow
|
||||
* the main thread to actually run. For better stress-testing, same or lower
|
||||
* priority threads do not sleep.
|
||||
*/
|
||||
if (sleep_flag)
|
||||
{
|
||||
atomTimerDelay (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Post the semaphore from interrupt context. This will be occurring while
|
||||
* atomSemPut() calls for the other threads are in progress, because it is
|
||||
* called by the system tick ISR. This tests that the APIs lockout
|
||||
* interrupts where necessary.
|
||||
*
|
||||
* Automatically requeues itself for one tick in the future, so this
|
||||
* continually fires until the finish flag is set.
|
||||
*
|
||||
* @param[in] cb_data Pointer to the original ATOM_TIMER structure
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
ATOM_TIMER *ptimer;
|
||||
uint8_t status;
|
||||
|
||||
/* Pull out the original timer request */
|
||||
ptimer = (ATOM_TIMER *)cb_data;
|
||||
|
||||
/* Post sem1 */
|
||||
status = atomSemPut (&sem1);
|
||||
if ((status != ATOM_OK) && (status != ATOM_ERR_OVF))
|
||||
{
|
||||
/* Error */
|
||||
}
|
||||
|
||||
/* Enqueue another timer callback in one tick's time */
|
||||
if (test_running == TRUE)
|
||||
{
|
||||
/* Update the callback time and requeue */
|
||||
ptimer->cb_ticks = 1;
|
||||
if (atomTimerRegister (ptimer) != ATOM_OK)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Test finished, no more will be queued */
|
||||
}
|
||||
|
||||
}
|
||||
193
tests/sem9.c
Normal file
193
tests/sem9.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtests.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1;
|
||||
static ATOM_TCB tcb1, tcb2, tcb3;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Test results */
|
||||
static volatile int pass_flag[3];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start semaphore test.
|
||||
*
|
||||
* This test verifies the semaphore deletion API, by deleting a semaphore
|
||||
* on which multiple threads are blocking, and checking that all three
|
||||
* are woken up with an appropriate error code.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Initialise pass status for all three threads to FALSE */
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
pass_flag[i] = FALSE;
|
||||
}
|
||||
|
||||
/* Test wakeup of three threads on semaphore deletion */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Error creating test semaphore 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* Create test thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 0,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 2 */
|
||||
else if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create test thread 3 */
|
||||
else if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Error creating test thread 3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test threads now created */
|
||||
else
|
||||
{
|
||||
/* Wait a while for threads to start blocking on sem1 */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Delete sem1 now that all three threads should be blocking */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete fail\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait a while for all three threads to wake up */
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC/4);
|
||||
|
||||
/* Check that all three threads have passed */
|
||||
if ((pass_flag[0] != TRUE) || (pass_flag[1] != TRUE) || (pass_flag[2] != TRUE))
|
||||
{
|
||||
ATOMLOG (_STR("Thread fail\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test threads.
|
||||
*
|
||||
* @param[in] data Thread ID (0-2)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t status;
|
||||
int thread_id;
|
||||
|
||||
/* Pull out the passed thread ID */
|
||||
thread_id = (int)data;
|
||||
|
||||
/*
|
||||
* Wait on sem1 with timeout. We are expecting to be woken up
|
||||
* by the main thread while blocking.
|
||||
*/
|
||||
status = atomSemGet(&sem1, (5 * SYSTEM_TICKS_PER_SEC));
|
||||
if (status != ATOM_ERR_DELETED)
|
||||
{
|
||||
ATOMLOG (_STR("Test1 thread woke without deletion (%d)\n"), status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We were woken due to deletion as expected, set pass_flag to notify success */
|
||||
pass_flag[thread_id] = TRUE;
|
||||
}
|
||||
|
||||
/* Wait forever */
|
||||
while (1)
|
||||
{
|
||||
atomTimerDelay (SYSTEM_TICKS_PER_SEC);
|
||||
}
|
||||
}
|
||||
62
tests/test-template.c
Normal file
62
tests/test-template.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start test.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
258
tests/timer1.c
Normal file
258
tests/timer1.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* This test exercises the atomTimerDelay() API. It checks that
|
||||
* the correct time delay is used, and also checks that error
|
||||
* checking is correctly implemented within the API.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
uint32_t start_time, end_time;
|
||||
ATOM_TIMER timer_cb;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test parameter-checks */
|
||||
if (atomTimerDelay(0) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG(_STR("Param\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create a semaphore for receiving test notification */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("SemCreate\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Create a timer callback which will attempt to
|
||||
* call atomTimerDelay() at interrupt context.
|
||||
*/
|
||||
timer_cb.cb_func = testCallback;
|
||||
timer_cb.cb_data = NULL;
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
|
||||
/* Request the timer callback to run in one second */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerRegister\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait up to two seconds for sem1 to be posted indicating success */
|
||||
else if (atomSemGet (&sem1, 2 * SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Context check\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the test semaphore */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a 1 tick delay returns when system time
|
||||
* has increased by exactly 1 tick.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We first delay by 1 tick to ensure that the thread
|
||||
* is running at the start of a new tick, which should
|
||||
* ensure that the time does not tick over between
|
||||
* setting start_time and actually calling
|
||||
* atomTimerDelay().
|
||||
*/
|
||||
atomTimerDelay(1);
|
||||
|
||||
/* Record the start time */
|
||||
start_time = atomTimeGet();
|
||||
|
||||
/* Request a 1 tick sleep */
|
||||
if (atomTimerDelay(1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delay1\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Record the time we woke up */
|
||||
end_time = atomTimeGet();
|
||||
|
||||
/* Check that time has advanced by exactly 1 tick */
|
||||
if ((end_time - start_time) != 1)
|
||||
{
|
||||
ATOMLOG (_STR("Tick1:%d\n"), (end_time-start_time));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a 2 tick delay returns when system time
|
||||
* has increased by exactly 2 ticks.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We first delay by 1 tick to ensure that the thread
|
||||
* is running at the start of a new tick, which should
|
||||
* ensure that the time does not tick over between
|
||||
* setting start_time and actually calling
|
||||
* atomTimerDelay().
|
||||
*/
|
||||
atomTimerDelay(1);
|
||||
|
||||
/* Record the start time */
|
||||
start_time = atomTimeGet();
|
||||
|
||||
/* Request a 2 tick sleep */
|
||||
if (atomTimerDelay(2) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delay2\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Record the time we woke up */
|
||||
end_time = atomTimeGet();
|
||||
|
||||
/* Check that time has advanced by exactly 2 ticks */
|
||||
if ((end_time - start_time) != 2)
|
||||
{
|
||||
ATOMLOG (_STR("Tick2:%d\n"), (end_time-start_time));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a 500 tick delay returns when system time
|
||||
* has increased by exactly 500 ticks.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We first delay by 1 tick to ensure that the thread
|
||||
* is running at the start of a new tick, which should
|
||||
* ensure that the time does not tick over between
|
||||
* setting start_time and actually calling
|
||||
* atomTimerDelay().
|
||||
*/
|
||||
atomTimerDelay(1);
|
||||
|
||||
/* Record the start time */
|
||||
start_time = atomTimeGet();
|
||||
|
||||
/* Request a 500 tick sleep */
|
||||
if (atomTimerDelay(500) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delay500\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Record the time we woke up */
|
||||
end_time = atomTimeGet();
|
||||
|
||||
/* Check that time has advanced by exactly 500 ticks */
|
||||
if ((end_time - start_time) != 500)
|
||||
{
|
||||
ATOMLOG (_STR("Tick500:%d\n"), (end_time-start_time));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Attempt an atomTimerDelay() call from interrupt context.
|
||||
* Should receive an ATOM_ERR_CONTEXT error. Posts sem1 if successful.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
/* Check the return value from atomTimerDelay() */
|
||||
if (atomTimerDelay(1) == ATOM_ERR_CONTEXT)
|
||||
{
|
||||
/* Received the error we expected, post sem1 to notify success */
|
||||
atomSemPut(&sem1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Did not get the expected error, don't post sem1 and the test
|
||||
* thread will time out, signifying an error.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
176
tests/timer2.c
Normal file
176
tests/timer2.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test period (in seconds) */
|
||||
#define TEST_PERIOD_SECS 10
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TCB tcb1, tcb2, tcb3;
|
||||
static uint8_t test1_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test2_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
static uint8_t test3_thread_stack[TEST_THREAD_STACK_SIZE];
|
||||
|
||||
|
||||
/* Per-thread failure counts */
|
||||
static volatile int g_failure_cnt[3];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void test_thread_func (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* Tests that atomTimerDelay() delays for the correct time
|
||||
* period when used by three threads simultanously.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
g_failure_cnt[0] = g_failure_cnt[1] = g_failure_cnt[2] = 0;
|
||||
|
||||
/* Create Thread 1 */
|
||||
if (atomThreadCreate(&tcb1, TEST_THREAD_PRIO, test_thread_func, 1,
|
||||
&test1_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Thread1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create Thread 2 */
|
||||
if (atomThreadCreate(&tcb2, TEST_THREAD_PRIO, test_thread_func, 2,
|
||||
&test2_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Thread2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Create Thread 3 */
|
||||
if (atomThreadCreate(&tcb3, TEST_THREAD_PRIO, test_thread_func, 3,
|
||||
&test3_thread_stack[TEST_THREAD_STACK_SIZE - 1]) != ATOM_OK)
|
||||
{
|
||||
/* Fail */
|
||||
ATOMLOG (_STR("Thread3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Sleep for 10 seconds allowing the three threads to run tests */
|
||||
if (atomTimerDelay(TEST_PERIOD_SECS * SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Period\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Add the per-thread failure count to the main count */
|
||||
failures += g_failure_cnt[0] + g_failure_cnt[1] + g_failure_cnt[2];
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b test_thread_func
|
||||
*
|
||||
* Entry point for test thread. The same thread entry point is used for all
|
||||
* three test threads, with the thread number/ID (1-3) passed as the entry
|
||||
* point parameter.
|
||||
*
|
||||
* @param[in] data Thread number (1,2,3)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void test_thread_func (uint32_t data)
|
||||
{
|
||||
uint8_t thread_id;
|
||||
uint32_t start_time, end_time;
|
||||
|
||||
/* Thread ID is passed through the function parameter */
|
||||
thread_id = (uint8_t)data;
|
||||
|
||||
/*
|
||||
* Sleep for 1 tick to ensure that the thread starts near
|
||||
* a timer tick boundary. This ensures that the system
|
||||
* tick does not advance between the atomTimeGet() call
|
||||
* and the actual atomTimerDelay() call being made.
|
||||
*/
|
||||
atomTimerDelay (1);
|
||||
|
||||
/* Loop running the test forever */
|
||||
while (1)
|
||||
{
|
||||
/* Record the start time */
|
||||
start_time = atomTimeGet();
|
||||
|
||||
/* Sleep for n ticks, where n is the thread ID */
|
||||
if (atomTimerDelay(thread_id) != ATOM_OK)
|
||||
{
|
||||
g_failure_cnt[thread_id-1]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Record the time we woke up */
|
||||
end_time = atomTimeGet();
|
||||
|
||||
/* Check that time has advanced by exactly n ticks */
|
||||
if ((end_time - start_time) != thread_id)
|
||||
{
|
||||
g_failure_cnt[thread_id-1]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
252
tests/timer3.c
Normal file
252
tests/timer3.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_SEM sem1;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* This test exercises the atomTimerRegister() API. It tests that bad
|
||||
* parameters are trapped, and that timer callbacks occur at the
|
||||
* correct time.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
ATOM_TIMER timer_cb;
|
||||
uint32_t expected_time;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Create a semaphore for receiving test notifications */
|
||||
if (atomSemCreate (&sem1, 0) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("SemCreate\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test that bad parameters are trapped */
|
||||
|
||||
/* NULL parameter check */
|
||||
if (atomTimerRegister(NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Param1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* NULL callback function */
|
||||
timer_cb.cb_ticks = 1;
|
||||
timer_cb.cb_func = NULL;
|
||||
if (atomTimerRegister(&timer_cb) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Param2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Zero ticks */
|
||||
timer_cb.cb_ticks = 0;
|
||||
timer_cb.cb_func = testCallback;
|
||||
if (atomTimerRegister(&timer_cb) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Param3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Request a callback in 1 tick */
|
||||
|
||||
/*
|
||||
* Sleep for one tick first to ensure we start near a
|
||||
* tick boundary. This should ensure that the timer
|
||||
* tick does not advance while we are setting up the
|
||||
* timer but before registering the timer.
|
||||
*/
|
||||
atomTimerDelay(1);
|
||||
|
||||
/* Request a callback in one tick time */
|
||||
timer_cb.cb_ticks = 1;
|
||||
|
||||
/* We pass our testCallback() the expected end time */
|
||||
timer_cb.cb_func = testCallback;
|
||||
expected_time = atomTimeGet() + timer_cb.cb_ticks;
|
||||
timer_cb.cb_data = &expected_time;
|
||||
|
||||
/* Register the timer callback */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait up to 5 ticks for sem1 to be posted indicating success */
|
||||
else if (atomSemGet (&sem1, 5) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Tick1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
|
||||
/* Request a callback in 2 ticks */
|
||||
|
||||
/*
|
||||
* Sleep for one tick first to ensure we start near a
|
||||
* tick boundary. This should ensure that the timer
|
||||
* tick does not advance while we are setting up the
|
||||
* timer but before registering the timer.
|
||||
*/
|
||||
atomTimerDelay(1);
|
||||
|
||||
/* Request a callback in 2 ticks time */
|
||||
timer_cb.cb_ticks = 2;
|
||||
|
||||
/* We pass our testCallback() the expected end time */
|
||||
timer_cb.cb_func = testCallback;
|
||||
expected_time = atomTimeGet() + timer_cb.cb_ticks;
|
||||
timer_cb.cb_data = &expected_time;
|
||||
|
||||
/* Register the timer callback */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait up to 5 ticks for sem1 to be posted indicating success */
|
||||
else if (atomSemGet (&sem1, 5) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Tick2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
|
||||
/* Request a callback in 500 ticks */
|
||||
|
||||
/*
|
||||
* Sleep for one tick first to ensure we start near a
|
||||
* tick boundary. This should ensure that the timer
|
||||
* tick does not advance while we are setting up the
|
||||
* timer but before registering the timer.
|
||||
*/
|
||||
atomTimerDelay(1);
|
||||
|
||||
/* Request a callback in 500 ticks time */
|
||||
timer_cb.cb_ticks = 500;
|
||||
|
||||
/* We pass our testCallback() the expected end time */
|
||||
timer_cb.cb_func = testCallback;
|
||||
expected_time = atomTimeGet() + timer_cb.cb_ticks;
|
||||
timer_cb.cb_data = &expected_time;
|
||||
|
||||
/* Register the timer callback */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg500\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait up to 600 ticks for sem1 to be posted indicating success */
|
||||
else if (atomSemGet (&sem1, 600) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Tick500\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Delete the test semaphore */
|
||||
if (atomSemDelete (&sem1) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Delete\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Check the time at which this timer callback occurs matches
|
||||
* the time we expected. Post sem1 if correct.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
uint32_t expected_end_time;
|
||||
|
||||
/* Pull out the expected end time */
|
||||
expected_end_time = *(uint32_t *)cb_data;
|
||||
|
||||
/*
|
||||
* Check the callback time (now) matches the time
|
||||
* we expected the callback.
|
||||
*/
|
||||
if (atomTimeGet() == expected_end_time)
|
||||
{
|
||||
/* Called back when we expected, post sem1 to notify success */
|
||||
atomSemPut(&sem1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Not called at expected time, don't post sem1 and the test
|
||||
* thread will time out, signifying an error.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
183
tests/timer4.c
Normal file
183
tests/timer4.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TIMER timer_cb[4];
|
||||
|
||||
|
||||
/* Global test data */
|
||||
static uint32_t cb_ticks[4];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* This test exercises the atomTimerRegister() API, particularly the
|
||||
* linked lists used internally for managing an ordered list of timers.
|
||||
*
|
||||
* Several timers are registered out of order (in terms of the callback
|
||||
* time) and we check that the callbacks are called at the expected
|
||||
* times.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/*
|
||||
* Fill out four timer request structures. Callbacks are
|
||||
* requested starting in one second, with the others
|
||||
* at 1 tick intervals thereafter.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* testCallback() is passed the expected
|
||||
* callback time via cb_data.
|
||||
*/
|
||||
cb_ticks[i] = SYSTEM_TICKS_PER_SEC + i;
|
||||
timer_cb[i].cb_ticks = cb_ticks[i];
|
||||
timer_cb[i].cb_func = testCallback;
|
||||
timer_cb[i].cb_data = &cb_ticks[i];
|
||||
}
|
||||
|
||||
/* Register the timers in a different order */
|
||||
if (atomTimerRegister (&timer_cb[1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg1\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomTimerRegister (&timer_cb[3]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg3\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomTimerRegister (&timer_cb[2]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg2\n"));
|
||||
failures++;
|
||||
}
|
||||
else if (atomTimerRegister (&timer_cb[0]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg0\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Successfully registered timers */
|
||||
|
||||
/* Wait two seconds for callbacks to complete */
|
||||
if (atomTimerDelay(2 * SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Wait\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The callbacks should have cleared down cb_ticks[x]
|
||||
* to zero if they were called at the expected
|
||||
* system time.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/* Check the callback has zeroed the area */
|
||||
if (cb_ticks[i] != 0)
|
||||
{
|
||||
ATOMLOG (_STR("Clear%d\n"), i);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Check the time at which this timer callback occurs matches
|
||||
* the time we expected. Clear down the expected time location
|
||||
* if correct.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
uint32_t expected_end_time;
|
||||
|
||||
/* Pull out the expected end time */
|
||||
expected_end_time = *(uint32_t *)cb_data;
|
||||
|
||||
/*
|
||||
* Check the callback time (now) matches the time
|
||||
* we expected the callback.
|
||||
*/
|
||||
if (atomTimeGet() == expected_end_time)
|
||||
{
|
||||
/* Called back when we expected, clear the passed location */
|
||||
*(uint32_t *)cb_data = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not called at expected time, don't clear the location */
|
||||
}
|
||||
|
||||
}
|
||||
150
tests/timer5.c
Normal file
150
tests/timer5.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomsem.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Global test data */
|
||||
static volatile int callback_ran_flag;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* This test exercises the atomTimerCancel() API. It tests that bad
|
||||
* parameters are trapped, and that it can be used to cancel an
|
||||
* in-progress timer callback request.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
ATOM_TIMER timer_cb;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Test parameter checks */
|
||||
if (atomTimerCancel(NULL) != ATOM_ERR_PARAM)
|
||||
{
|
||||
ATOMLOG (_STR("Param\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test cancel when timer not registered */
|
||||
if (atomTimerCancel(&timer_cb) != ATOM_ERR_NOT_FOUND)
|
||||
{
|
||||
ATOMLOG (_STR("NotFound\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Test a callback can be cancelled */
|
||||
callback_ran_flag = FALSE;
|
||||
|
||||
/* Request a callback in one second, no callback param required */
|
||||
timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC;
|
||||
timer_cb.cb_func = testCallback;
|
||||
|
||||
/* Register the timer callback */
|
||||
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Successfully registered for one second's time */
|
||||
|
||||
/* Cancel the callback */
|
||||
if (atomTimerCancel (&timer_cb) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerCancel\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Successfully cancelled the callback */
|
||||
|
||||
/* Wait two seconds, and check callback did not occur */
|
||||
if (atomTimerDelay(2 * SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Wait\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The ran flag should still be FALSE */
|
||||
if (callback_ran_flag != FALSE)
|
||||
{
|
||||
ATOMLOG (_STR("Called back\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Set a flag to say we ran. Expected not to run for a pass.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
/* Callback was called */
|
||||
callback_ran_flag = TRUE;
|
||||
}
|
||||
165
tests/timer6.c
Normal file
165
tests/timer6.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtimer.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TIMER timer_cb[4];
|
||||
|
||||
|
||||
/* Global test data */
|
||||
static int callback_ran_flag[4];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* This test exercises the atomTimerCancel() API, particularly its
|
||||
* behaviour when there are several timers registered. Four timers
|
||||
* are registered, two of which are cancelled, and the test confirms
|
||||
* that only the two which are not cancelled are called back.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
int failures;
|
||||
int i;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/* Clear down the ran flag for all four timers */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
callback_ran_flag[i] = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill out four timer request structures. Callbacks are
|
||||
* requested starting in one second, with the others
|
||||
* at 1 tick intervals thereafter.
|
||||
*/
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
/*
|
||||
* testCallback() is passed a pointer to the flag it
|
||||
* should set to notify that it has run.
|
||||
*/
|
||||
timer_cb[i].cb_ticks = SYSTEM_TICKS_PER_SEC + i;
|
||||
timer_cb[i].cb_func = testCallback;
|
||||
timer_cb[i].cb_data = &callback_ran_flag[i];
|
||||
}
|
||||
|
||||
/* Register all four timers */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (atomTimerRegister (&timer_cb[i]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check timers were successfully created */
|
||||
if (failures == 0)
|
||||
{
|
||||
/* Cancel two of the callbacks */
|
||||
if (atomTimerCancel (&timer_cb[1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Cancel1\n"));
|
||||
failures++;
|
||||
}
|
||||
if (atomTimerCancel (&timer_cb[2]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Cancel2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Wait two seconds for callbacks to complete */
|
||||
if (atomTimerDelay(2 * SYSTEM_TICKS_PER_SEC) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("Wait\n"));
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We should now find that timer callbacks 0 and 3
|
||||
* have run, but 1 and 2 did not (due to cancellation).
|
||||
*/
|
||||
if ((callback_ran_flag[0] != TRUE) || (callback_ran_flag[3] != TRUE)
|
||||
|| (callback_ran_flag[1] != FALSE) || (callback_ran_flag[2] != FALSE))
|
||||
{
|
||||
ATOMLOG (_STR("Cancellations\n"));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Set a flag to say we ran. Some of the callbacks are
|
||||
* expected to execute this, while those that are
|
||||
* cancelled should not.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
/* Callback was called */
|
||||
*(int *)cb_data = TRUE;
|
||||
}
|
||||
183
tests/timer7.c
Normal file
183
tests/timer7.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "atom.h"
|
||||
#include "atomtests.h"
|
||||
|
||||
|
||||
/* Test OS objects */
|
||||
static ATOM_TIMER timer_cb[4];
|
||||
|
||||
|
||||
/* Global test data */
|
||||
static volatile uint32_t cb_order[4];
|
||||
static int cb_cnt = 0;
|
||||
static uint32_t cb_time[4];
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void testCallback (POINTER cb_data);
|
||||
|
||||
|
||||
/**
|
||||
* \b test_start
|
||||
*
|
||||
* Start timer test.
|
||||
*
|
||||
* Test the behaviour of the timer subsystem on a system clock rollover.
|
||||
*
|
||||
* Sets the system clock to just before rollover and registers several
|
||||
* timers. Tests that all timer callbacks occur and that they occur in
|
||||
* in the correct order, when they span a timer rollover.
|
||||
*
|
||||
* @retval Number of failures
|
||||
*/
|
||||
uint32_t test_start (void)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
int i, failures;
|
||||
|
||||
/* Default to zero failures */
|
||||
failures = 0;
|
||||
|
||||
/*
|
||||
* Lockout interrupts while registering to ensure that the clock does
|
||||
* not roll over under us while we are registering our test timers.
|
||||
*/
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Set the clock to rollover - 6 */
|
||||
atomTimeSet (0xFFFFFFFA);
|
||||
|
||||
/* Timer in 2 ticks (pre-rollover): should be called back first */
|
||||
timer_cb[0].cb_ticks = 2;
|
||||
timer_cb[0].cb_func = testCallback;
|
||||
timer_cb[0].cb_data = (POINTER)0;
|
||||
if (atomTimerRegister (&timer_cb[0]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg0\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Timer in 10 ticks (post-rollover): should be called back last */
|
||||
timer_cb[1].cb_ticks = 10;
|
||||
timer_cb[1].cb_func = testCallback;
|
||||
timer_cb[1].cb_data = (POINTER)3;
|
||||
if (atomTimerRegister (&timer_cb[1]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg1\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Timer in 4 ticks (pre-rollover): should be called back second */
|
||||
timer_cb[2].cb_ticks = 4;
|
||||
timer_cb[2].cb_func = testCallback;
|
||||
timer_cb[2].cb_data = (POINTER)1;
|
||||
if (atomTimerRegister (&timer_cb[2]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg2\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Timer in 9 ticks (post-rollover): should be called back third */
|
||||
timer_cb[3].cb_ticks = 9;
|
||||
timer_cb[3].cb_func = testCallback;
|
||||
timer_cb[3].cb_data = (POINTER)2;
|
||||
if (atomTimerRegister (&timer_cb[3]) != ATOM_OK)
|
||||
{
|
||||
ATOMLOG (_STR("TimerReg3\n"));
|
||||
failures++;
|
||||
}
|
||||
|
||||
/* Initialise the cb_order delay to known values */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
cb_order[i] = 99;
|
||||
}
|
||||
|
||||
/* Unlock interrupts and let the test begin */
|
||||
CRITICAL_END ();
|
||||
|
||||
/*
|
||||
* Wait 20 ticks for the callbacks to complete. Also tests another
|
||||
* timer registration via atomTimerDelay() for us.
|
||||
*/
|
||||
atomTimerDelay (20);
|
||||
|
||||
/* Check the order the callbacks came in matched our expectations */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (cb_order[i] != i)
|
||||
{
|
||||
ATOMLOG (_STR("T%d=%d\n"), i, cb_order[i]);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Log final status */
|
||||
if (failures == 0)
|
||||
{
|
||||
ATOMLOG (_STR("Pass\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ATOMLOG (_STR("Fail(%d)\n"), failures);
|
||||
}
|
||||
|
||||
/* Quit */
|
||||
return failures;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b testCallback
|
||||
*
|
||||
* Timer callback. Store our cb_data value in cb_order[].
|
||||
* This allows us to check that the callback order was as expectd.
|
||||
*
|
||||
* @param[in] cb_data Not used
|
||||
*/
|
||||
static void testCallback (POINTER cb_data)
|
||||
{
|
||||
int expected_order;
|
||||
|
||||
/* Pull out the expected ordere */
|
||||
expected_order = (int)cb_data;
|
||||
|
||||
/* Store our callback order in cb_order[] */
|
||||
cb_order[cb_cnt] = expected_order;
|
||||
|
||||
/* Store the current time for debug purposes */
|
||||
cb_time[cb_cnt] = atomTimeGet();
|
||||
|
||||
/* Interrupts are locked out so we can modify cb_cnt without protection */
|
||||
cb_cnt++;
|
||||
}
|
||||
Reference in New Issue
Block a user